paneful 0.8.9 → 0.9.1
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/README.md +22 -0
- package/dist/server/claude-monitor.js +2 -3
- package/dist/server/git-monitor.js +74 -0
- package/dist/server/index.js +25 -0
- package/dist/server/pty-manager.js +4 -3
- package/dist/server/ws-handler.js +11 -0
- package/dist/web/assets/index-CNl2qUBu.css +32 -0
- package/dist/web/assets/index-f8s-Q1ma.js +389 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-CTFgZeZH.js +0 -349
- package/dist/web/assets/index-CYCoL-NZ.css +0 -32
package/README.md
CHANGED
|
@@ -51,10 +51,30 @@ Requires:
|
|
|
51
51
|
|
|
52
52
|
Save a workspace layout as a favourite — name, layout preset, and per-pane commands. Launch any favourite with a click to instantly recreate the setup.
|
|
53
53
|
|
|
54
|
+
### Terminal Search
|
|
55
|
+
|
|
56
|
+
Press `Cmd+F` in any focused terminal to search its scrollback. Navigate matches with Enter / Shift+Enter or the up/down buttons. Press Escape to close.
|
|
57
|
+
|
|
58
|
+
### Command Palette
|
|
59
|
+
|
|
60
|
+
Press `Cmd+P` to open the command palette. Quickly switch projects, launch favourites, change layouts, or run any action — all from one fuzzy-searchable list.
|
|
61
|
+
|
|
62
|
+
### Git Branch Display
|
|
63
|
+
|
|
64
|
+
The sidebar shows the current Git branch next to each project's working directory as a small pill badge. Updates automatically every 10 seconds. Non-git directories show no badge.
|
|
65
|
+
|
|
66
|
+
### AI Agent Detection
|
|
67
|
+
|
|
68
|
+
Automatically detects when Claude Code or Codex CLI is running in a Paneful terminal. A purple **AI** badge appears next to the project name in the sidebar — pulsing when the agent is actively working, dimmed when idle. Disappears instantly when the agent exits. Uses zero filesystem access; detection is purely in-memory via the PTY process name and terminal output timestamps.
|
|
69
|
+
|
|
54
70
|
### Dev Server Detection
|
|
55
71
|
|
|
56
72
|
Automatically detects when a dev server starts in a terminal (Vite, Next.js, Angular, etc.). A green dot appears next to the project name in the sidebar while the port is alive, and disappears when it stops. Tracks ports per-terminal so the same port across different projects is handled correctly.
|
|
57
73
|
|
|
74
|
+
### Project Cleanup
|
|
75
|
+
|
|
76
|
+
Click the broom icon in the sidebar header to scan for projects whose directories no longer exist on disk. A confirmation modal shows matching projects before removing them.
|
|
77
|
+
|
|
58
78
|
### Auto-Reorganize
|
|
59
79
|
|
|
60
80
|
Press `Cmd+R` or click the dashboard icon in the toolbar to automatically pick the best layout for your current pane count.
|
|
@@ -85,6 +105,8 @@ Paneful checks for newer versions on npm and shows a notification in the sidebar
|
|
|
85
105
|
|
|
86
106
|
| Shortcut | Action |
|
|
87
107
|
| ------------------ | ------------------------------- |
|
|
108
|
+
| `Cmd+P` | Command palette |
|
|
109
|
+
| `Cmd+F` | Search terminal scrollback |
|
|
88
110
|
| `Cmd+N` | New pane (vertical split) |
|
|
89
111
|
| `Cmd+Shift+N` | New pane (horizontal split) |
|
|
90
112
|
| `Cmd+W` | Close focused pane |
|
|
@@ -34,10 +34,9 @@ export class ClaudeMonitor {
|
|
|
34
34
|
if (this.destroyed)
|
|
35
35
|
return;
|
|
36
36
|
const now = Date.now();
|
|
37
|
-
|
|
38
|
-
const claudeProjects = this.ptyManager.getClaudeProjects();
|
|
37
|
+
const agentProjects = this.ptyManager.getAgentProjects();
|
|
39
38
|
const statuses = {};
|
|
40
|
-
for (const [projectId, terminalIds] of
|
|
39
|
+
for (const [projectId, terminalIds] of agentProjects) {
|
|
41
40
|
let latestOutput = 0;
|
|
42
41
|
for (const tid of terminalIds) {
|
|
43
42
|
const ts = this.lastOutput.get(tid) ?? 0;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
export class GitMonitor {
|
|
3
|
+
projectStore;
|
|
4
|
+
onChange;
|
|
5
|
+
branches = new Map();
|
|
6
|
+
pollTimer = null;
|
|
7
|
+
destroyed = false;
|
|
8
|
+
constructor(projectStore, onChange) {
|
|
9
|
+
this.projectStore = projectStore;
|
|
10
|
+
this.onChange = onChange;
|
|
11
|
+
this.poll();
|
|
12
|
+
this.pollTimer = setInterval(() => this.poll(), 10_000);
|
|
13
|
+
}
|
|
14
|
+
getBranches() {
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const [id, branch] of this.branches) {
|
|
17
|
+
result[id] = branch;
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
destroy() {
|
|
22
|
+
this.destroyed = true;
|
|
23
|
+
if (this.pollTimer) {
|
|
24
|
+
clearInterval(this.pollTimer);
|
|
25
|
+
this.pollTimer = null;
|
|
26
|
+
}
|
|
27
|
+
this.branches.clear();
|
|
28
|
+
}
|
|
29
|
+
async poll() {
|
|
30
|
+
if (this.destroyed)
|
|
31
|
+
return;
|
|
32
|
+
const projects = this.projectStore.list();
|
|
33
|
+
const results = await Promise.all(projects.map((p) => this.getBranch(p.cwd).then((branch) => [p.id, branch])));
|
|
34
|
+
if (this.destroyed)
|
|
35
|
+
return;
|
|
36
|
+
const newBranches = new Map();
|
|
37
|
+
for (const [id, branch] of results) {
|
|
38
|
+
newBranches.set(id, branch);
|
|
39
|
+
}
|
|
40
|
+
// Check if changed
|
|
41
|
+
if (this.mapsEqual(newBranches))
|
|
42
|
+
return;
|
|
43
|
+
this.branches = newBranches;
|
|
44
|
+
this.notify();
|
|
45
|
+
}
|
|
46
|
+
getBranch(cwd) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
execFile('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, timeout: 2000 }, (err, stdout) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
resolve(null);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const branch = stdout.trim();
|
|
54
|
+
resolve(branch || null);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
mapsEqual(other) {
|
|
59
|
+
if (other.size !== this.branches.size)
|
|
60
|
+
return false;
|
|
61
|
+
for (const [key, val] of other) {
|
|
62
|
+
if (this.branches.get(key) !== val)
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
notify() {
|
|
68
|
+
const result = {};
|
|
69
|
+
for (const [id, branch] of this.branches) {
|
|
70
|
+
result[id] = branch;
|
|
71
|
+
}
|
|
72
|
+
this.onChange(result);
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/server/index.js
CHANGED
|
@@ -228,6 +228,31 @@ async function startServer(devMode, port) {
|
|
|
228
228
|
const killed = ptyManager.killProject(req.params.id);
|
|
229
229
|
res.json({ killed: killed.length });
|
|
230
230
|
});
|
|
231
|
+
function getStaleProjects() {
|
|
232
|
+
const stale = [];
|
|
233
|
+
for (const project of projectStore.list()) {
|
|
234
|
+
try {
|
|
235
|
+
const stat = fs.statSync(project.cwd);
|
|
236
|
+
if (!stat.isDirectory())
|
|
237
|
+
throw new Error('not a directory');
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
stale.push({ id: project.id, name: project.name, cwd: project.cwd });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return stale;
|
|
244
|
+
}
|
|
245
|
+
app.get('/api/cleanup-projects', (_req, res) => {
|
|
246
|
+
res.json({ stale: getStaleProjects() });
|
|
247
|
+
});
|
|
248
|
+
app.post('/api/cleanup-projects', (_req, res) => {
|
|
249
|
+
const stale = getStaleProjects();
|
|
250
|
+
for (const project of stale) {
|
|
251
|
+
ptyManager.killProject(project.id);
|
|
252
|
+
projectStore.remove(project.id);
|
|
253
|
+
}
|
|
254
|
+
res.json({ removed: stale.map((p) => p.name) });
|
|
255
|
+
});
|
|
231
256
|
app.post('/api/validate-path', (req, res) => {
|
|
232
257
|
const { path: rawPath } = req.body;
|
|
233
258
|
if (!rawPath) {
|
|
@@ -72,12 +72,13 @@ export class PtyManager {
|
|
|
72
72
|
terminalExists(terminalId) {
|
|
73
73
|
return this.sessions.has(terminalId);
|
|
74
74
|
}
|
|
75
|
-
/** Returns projectId → terminalIds[] for terminals
|
|
76
|
-
|
|
75
|
+
/** Returns projectId → terminalIds[] for terminals running an AI coding agent. */
|
|
76
|
+
getAgentProjects() {
|
|
77
77
|
const result = new Map();
|
|
78
78
|
for (const [terminalId, managed] of this.sessions) {
|
|
79
79
|
try {
|
|
80
|
-
|
|
80
|
+
const proc = managed.process.process;
|
|
81
|
+
if (proc === 'claude' || proc.startsWith('codex')) {
|
|
81
82
|
const list = result.get(managed.projectId);
|
|
82
83
|
if (list)
|
|
83
84
|
list.push(terminalId);
|
|
@@ -2,6 +2,7 @@ import { WebSocket, WebSocketServer } from 'ws';
|
|
|
2
2
|
import { newProject } from './project-store.js';
|
|
3
3
|
import { PortMonitor } from './port-monitor.js';
|
|
4
4
|
import { ClaudeMonitor } from './claude-monitor.js';
|
|
5
|
+
import { GitMonitor } from './git-monitor.js';
|
|
5
6
|
export class WsHandler {
|
|
6
7
|
wss;
|
|
7
8
|
client = null;
|
|
@@ -9,6 +10,7 @@ export class WsHandler {
|
|
|
9
10
|
projectStore;
|
|
10
11
|
portMonitor;
|
|
11
12
|
claudeMonitor;
|
|
13
|
+
gitMonitor;
|
|
12
14
|
idleTimer = null;
|
|
13
15
|
onIdle;
|
|
14
16
|
constructor(server, ptyManager, projectStore, options) {
|
|
@@ -22,6 +24,9 @@ export class WsHandler {
|
|
|
22
24
|
this.send({ type: 'claude:status', statuses });
|
|
23
25
|
});
|
|
24
26
|
this.claudeMonitor.start();
|
|
27
|
+
this.gitMonitor = new GitMonitor(projectStore, (branches) => {
|
|
28
|
+
this.send({ type: 'git:branch', branches });
|
|
29
|
+
});
|
|
25
30
|
this.wss = new WebSocketServer({ noServer: true });
|
|
26
31
|
server.on('upgrade', (req, socket, head) => {
|
|
27
32
|
if (req.url === '/ws') {
|
|
@@ -40,6 +45,11 @@ export class WsHandler {
|
|
|
40
45
|
this.idleTimer = null;
|
|
41
46
|
}
|
|
42
47
|
console.log('WebSocket client connected');
|
|
48
|
+
// Send current git branch state to newly connected client
|
|
49
|
+
const branches = this.gitMonitor.getBranches();
|
|
50
|
+
if (Object.keys(branches).length > 0) {
|
|
51
|
+
this.send({ type: 'git:branch', branches });
|
|
52
|
+
}
|
|
43
53
|
ws.on('message', (raw) => {
|
|
44
54
|
try {
|
|
45
55
|
const msg = JSON.parse(raw.toString());
|
|
@@ -127,6 +137,7 @@ export class WsHandler {
|
|
|
127
137
|
destroy() {
|
|
128
138
|
this.portMonitor.destroy();
|
|
129
139
|
this.claudeMonitor.destroy();
|
|
140
|
+
this.gitMonitor.destroy();
|
|
130
141
|
}
|
|
131
142
|
handlePtySpawn(terminalId, projectId, cwd) {
|
|
132
143
|
try {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-left-\[2px\]{left:-2px}.-top-\[2px\]{top:-2px}.bottom-0{bottom:0}.left-0{left:0}.right-0{right:0}.top-0{top:0}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-\[20vh\]{margin-top:20vh}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-1\/2{height:50%}.h-16{height:4rem}.h-4{height:1rem}.h-7{height:1.75rem}.h-9{height:2.25rem}.h-\[4px\]{height:4px}.h-\[8px\]{height:8px}.h-full{height:100%}.h-screen{height:100vh}.max-h-40{max-height:10rem}.max-h-72{max-height:18rem}.max-h-\[80vh\]{max-height:80vh}.min-h-0{min-height:0px}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-16{width:4rem}.w-80{width:20rem}.w-\[380px\]{width:380px}.w-\[400px\]{width:400px}.w-\[440px\]{width:440px}.w-\[4px\]{width:4px}.w-\[8px\]{width:8px}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-\[24px\]{min-width:24px}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-\[var\(--accent\)\]{border-color:var(--accent)}.border-\[var\(--border\)\]{border-color:var(--border)}.border-\[var\(--danger\)\]{border-color:var(--danger)}.border-transparent{border-color:transparent}.bg-\[var\(--border\)\]{background-color:var(--border)}.bg-\[var\(--danger\)\]{background-color:var(--danger)}.bg-\[var\(--success\)\]{background-color:var(--success)}.bg-\[var\(--surface-0\)\]{background-color:var(--surface-0)}.bg-\[var\(--surface-1\)\]{background-color:var(--surface-1)}.bg-\[var\(--surface-2\)\]{background-color:var(--surface-2)}.bg-\[var\(--surface-3\)\]{background-color:var(--surface-3)}.bg-accent{background-color:var(--accent)}.bg-black\/50{background-color:#00000080}.bg-purple-500\/10{background-color:#a855f71a}.bg-purple-500\/20{background-color:#a855f733}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-500\/10{background-color:#eab3081a}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pt-1{padding-top:.25rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-\[var\(--accent\)\]{color:var(--accent)}.text-\[var\(--danger\)\]{color:var(--danger)}.text-\[var\(--text-muted\)\]{color:var(--text-muted)}.text-\[var\(--text-primary\)\]{color:var(--text-primary)}.text-\[var\(--text-secondary\)\]{color:var(--text-secondary)}.text-accent{color:var(--accent)}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-purple-400\/50{color:#c084fc80}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.opacity-40{opacity:.4}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_1px_0_var\(--border\)\]{--tw-shadow: 0 1px 0 var(--border);--tw-shadow-colored: 0 1px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-inset{--tw-ring-inset: inset}.ring-\[var\(--accent\)\]{--tw-ring-color: var(--accent)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}:root{--surface-0: #0a0a0f;--surface-1: #111118;--surface-2: #1a1a24;--surface-3: #252532;--border: #2a2a3a;--border-focus: #5b5bd6;--accent: #5b5bd6;--accent-dim: #3a3a8c;--text-primary: #ececf1;--text-secondary: #a1a1b5;--text-muted: #6b6b80;--danger: #e5484d;--danger-dim: #3c1618;--success: #30a46c}[data-theme=light]{--surface-0: #ffffff;--surface-1: #f5f5f7;--surface-2: #ebebef;--surface-3: #dddde3;--border: #d0d0d8;--border-focus: #5b5bd6;--accent: #5b5bd6;--accent-dim: #e0e0ff;--text-primary: #1a1a2e;--text-secondary: #555570;--text-muted: #8888a0;--danger: #dc3545;--danger-dim: #fde8ea;--success: #1a8f5c}@media (prefers-color-scheme: light){[data-theme=system]{--surface-0: #ffffff;--surface-1: #f5f5f7;--surface-2: #ebebef;--surface-3: #dddde3;--border: #d0d0d8;--border-focus: #5b5bd6;--accent: #5b5bd6;--accent-dim: #e0e0ff;--text-primary: #1a1a2e;--text-secondary: #555570;--text-muted: #8888a0;--danger: #dc3545;--danger-dim: #fde8ea;--success: #1a8f5c}}*{margin:0;padding:0;box-sizing:border-box}html,body,#root{height:100%;width:100%;overflow:hidden;background:var(--surface-0);color:var(--text-primary);font-family:-apple-system,BlinkMacSystemFont,Inter,Segoe UI,sans-serif}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.xterm{padding:4px 8px}.xterm-viewport::-webkit-scrollbar{width:6px}.xterm-viewport::-webkit-scrollbar-track{background:transparent}.xterm-viewport::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideIn{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes slideOut{0%{transform:translate(0)}to{transform:translate(-100%)}}.animate-fade-in{animation:fadeIn .15s ease-out}.placeholder\:text-\[var\(--text-muted\)\]::-moz-placeholder{color:var(--text-muted)}.placeholder\:text-\[var\(--text-muted\)\]::placeholder{color:var(--text-muted)}.hover\:bg-\[var\(--accent\)\]:hover{background-color:var(--accent)}.hover\:bg-\[var\(--danger-dim\)\]:hover{background-color:var(--danger-dim)}.hover\:bg-\[var\(--surface-2\)\]:hover{background-color:var(--surface-2)}.hover\:bg-\[var\(--surface-3\)\]:hover{background-color:var(--surface-3)}.hover\:bg-green-500\/10:hover{background-color:#22c55e1a}.hover\:text-\[var\(--danger\)\]:hover{color:var(--danger)}.hover\:text-\[var\(--success\)\]:hover{color:var(--success)}.hover\:text-\[var\(--text-primary\)\]:hover{color:var(--text-primary)}.hover\:text-\[var\(--text-secondary\)\]:hover{color:var(--text-secondary)}.hover\:text-yellow-400:hover{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-40:hover{opacity:.4}.hover\:opacity-90:hover{opacity:.9}.focus\:border-\[var\(--accent\)\]:focus{border-color:var(--accent)}.focus\:border-\[var\(--danger\)\]:focus{border-color:var(--danger)}.focus\:border-accent:focus{border-color:var(--accent)}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.active\:cursor-grabbing:active{cursor:grabbing}.active\:bg-\[var\(--accent\)\]:active{background-color:var(--accent)}.active\:opacity-60:active{opacity:.6}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.group:hover .group-hover\:flex{display:flex}.group:active .group-active\:bg-accent{background-color:var(--accent)}@media (min-width: 640px){.sm\:inline{display:inline}.sm\:inline-flex{display:inline-flex}}/**
|
|
2
|
+
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
|
3
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
4
|
+
* https://github.com/chjj/term.js
|
|
5
|
+
* @license MIT
|
|
6
|
+
*
|
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
* furnished to do so, subject to the following conditions:
|
|
13
|
+
*
|
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
|
15
|
+
* all copies or substantial portions of the Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
* THE SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* Originally forked from (with the author's permission):
|
|
26
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
27
|
+
* http://bellard.org/jslinux/
|
|
28
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
29
|
+
* The original design remains. The terminal itself
|
|
30
|
+
* has been extended to include xterm CSI codes, among
|
|
31
|
+
* other features.
|
|
32
|
+
*/.xterm{cursor:text;position:relative;-moz-user-select:none;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::-moz-selection{color:transparent}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;-moz-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:double underline;text-decoration:double underline}.xterm-underline-3{-webkit-text-decoration:wavy underline;text-decoration:wavy underline}.xterm-underline-4{-webkit-text-decoration:dotted underline;text-decoration:dotted underline}.xterm-underline-5{-webkit-text-decoration:dashed underline;text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}
|