Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@
"type": "module",
"description": "Full-featured web terminal plugin for CloudCLI UI",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
"build": "tsc -p tsconfig.server.json && esbuild src/index.ts --bundle --format=esm --outfile=dist/index.js --loader:.css=text",
"dev": "tsc -p tsconfig.server.json --watch"
},
"dependencies": {
"@xterm/addon-clipboard": "^0.2.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode11": "^0.9.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"node-pty": "^1.1.0",
"ws": "^8.14.0"
},
"devDependencies": {
"typescript": "^5.5.0",
"@types/node": "^20.0.0",
"@types/ws": "^8.5.0"
"@types/ws": "^8.5.0",
"esbuild": "^0.28.0",
"typescript": "^5.5.0"
}
}
80 changes: 18 additions & 62 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
*/

import type { PluginAPI } from './types.js';

// ── CDN version pins ──────────────────────────────────────────────────────────
const CDN = 'https://esm.sh';
const XTERM_VER = '5.5.0';
const FIT_VER = '0.10.0';
const WEBLINKS_VER = '0.11.0';
const WEBGL_VER = '0.18.0';
const CLIPBOARD_VER = '0.1.0';
const UNICODE11_VER = '0.8.0';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { WebglAddon } from '@xterm/addon-webgl';
import { ClipboardAddon } from '@xterm/addon-clipboard';
import { Unicode11Addon } from '@xterm/addon-unicode11';
import xtermCSS from '@xterm/xterm/css/xterm.css';

// ── Types ─────────────────────────────────────────────────────────────────────

Expand All @@ -37,17 +35,7 @@ interface Prefs {
fontFamily?: string;
}

interface XtermModules {
Terminal: any;
FitAddon: any;
WebLinksAddon: any;
WebglAddon: any;
ClipboardAddon: any;
Unicode11Addon: any;
}

interface GlobalState {
modules: XtermModules | null;
sessions: Map<string, TerminalSession>;
prefs: Prefs | null;
tabCounter: number;
Expand Down Expand Up @@ -123,7 +111,7 @@ declare global {
}

if (!window.__wtState) {
window.__wtState = { modules: null, sessions: new Map(), prefs: null, tabCounter: 0, activeId: null };
window.__wtState = { sessions: new Map(), prefs: null, tabCounter: 0, activeId: null };
}
const _G: GlobalState = window.__wtState;

Expand All @@ -150,10 +138,10 @@ function divider(): HTMLElement { return el('div', 'wt-divider'); }
function injectStyles(): void {
if (document.getElementById('wt-css')) return;

const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = `${CDN}/@xterm/xterm@${XTERM_VER}/css/xterm.css`;
document.head.appendChild(link);
const xtermStyle = document.createElement('style');
xtermStyle.id = 'wt-xterm-css';
xtermStyle.textContent = xtermCSS;
document.head.appendChild(xtermStyle);

const s = document.createElement('style');
s.id = 'wt-css';
Expand Down Expand Up @@ -584,45 +572,14 @@ class TerminalSession {
}
}

// ── Module loader (cached) ────────────────────────────────────────────────────
async function loadModules(): Promise<XtermModules> {
if (_G.modules) return _G.modules;
const results = await Promise.all([
import(CDN + '/@xterm/xterm@' + XTERM_VER),
import(CDN + '/@xterm/addon-fit@' + FIT_VER),
import(CDN + '/@xterm/addon-web-links@' + WEBLINKS_VER),
import(CDN + '/@xterm/addon-webgl@' + WEBGL_VER),
import(CDN + '/@xterm/addon-clipboard@' + CLIPBOARD_VER).catch(() => ({ ClipboardAddon: null })),
import(CDN + '/@xterm/addon-unicode11@' + UNICODE11_VER).catch(() => ({ Unicode11Addon: null })),
]);
_G.modules = {
Terminal: results[0].Terminal, FitAddon: results[1].FitAddon,
WebLinksAddon: results[2].WebLinksAddon, WebglAddon: results[3].WebglAddon,
ClipboardAddon: results[4].ClipboardAddon, Unicode11Addon: results[5].Unicode11Addon,
};
return _G.modules;
}

// ── Mount ─────────────────────────────────────────────────────────────────────
export async function mount(container: HTMLElement, api: PluginAPI): Promise<void> {
injectStyles();

let mods: XtermModules;
try {
mods = await loadModules();
} catch (err) {
const errDiv = el('div');
errDiv.style.cssText = 'display:flex;align-items:center;justify-content:center;height:100%;color:#f14c4c;padding:24px;text-align:center;font-family:sans-serif';
const inner = el('div');
inner.appendChild(el('div', null, 'Failed to load xterm.js'));
(inner.firstChild as HTMLElement).style.cssText = 'font-size:16px;font-weight:600;margin-bottom:8px';
const detail = el('div', null, (err as Error).message);
detail.style.cssText = 'font-size:12px;opacity:.7';
inner.appendChild(detail);
errDiv.appendChild(inner);
container.appendChild(errDiv);
return;
}
let Clipboard: any = ClipboardAddon;
try { if (!Clipboard) throw new Error(); } catch { Clipboard = null; }
let Unicode11: any = Unicode11Addon;
try { if (!Unicode11) throw new Error(); } catch { Unicode11 = null; }

if (!_G.prefs) {
_G.prefs = loadPrefs() as Prefs;
Expand Down Expand Up @@ -772,9 +729,8 @@ export async function mount(container: HTMLElement, api: PluginAPI): Promise<voi
const id = 't' + _G.tabCounter;
const sess = new TerminalSession({
id, label: 'shell ' + _G.tabCounter,
Terminal: mods.Terminal, FitAddon: mods.FitAddon,
WebLinksAddon: mods.WebLinksAddon, WebglAddon: mods.WebglAddon,
ClipboardAddon: mods.ClipboardAddon, Unicode11Addon: mods.Unicode11Addon,
Terminal, FitAddon, WebLinksAddon, WebglAddon,
ClipboardAddon: Clipboard, Unicode11Addon: Unicode11,
prefs, onChange() { renderTabs(); },
});
_G.sessions.set(id, sess);
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"sourceMap": true,
"lib": ["ES2020", "DOM"]
},
"include": ["src"]
"include": ["src"],
"exclude": ["node_modules/@xterm"]
}
17 changes: 17 additions & 0 deletions tsconfig.server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"lib": ["ES2020", "DOM"]
},
"include": ["src/server.ts", "src/types.ts"]
}