mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #5525 from Tyriar/demo_hover
Implement cell inspector in demo
This commit is contained in:
@@ -19,6 +19,7 @@ import { AddonImageWindow } from './components/window/addonImageWindow';
|
||||
import { AddonSearchWindow } from './components/window/addonSearchWindow';
|
||||
import { AddonSerializeWindow } from './components/window/addonSerializeWindow';
|
||||
import { AddonsWindow } from './components/window/addonsWindow';
|
||||
import { CellInspectorWindow } from './components/window/cellInspectorWindow';
|
||||
import { ControlBar } from './components/controlBar';
|
||||
import { WebglWindow } from './components/window/webglWindow';
|
||||
import { OptionsWindow } from './components/window/optionsWindow';
|
||||
@@ -206,6 +207,7 @@ if (document.location.pathname === '/test') {
|
||||
controlBar = new ControlBar(document.getElementById('sidebar'), document.querySelector('.banner-tabs'), []);
|
||||
optionsWindow = controlBar.registerWindow(new OptionsWindow(typedTerm, addons, { updateTerminalSize, updateTerminalContainerBackground }));
|
||||
const styleWindow = controlBar.registerWindow(new StyleWindow(typedTerm, addons));
|
||||
controlBar.registerWindow(new CellInspectorWindow(typedTerm, addons));
|
||||
controlBar.registerWindow(new VtWindow(typedTerm, addons));
|
||||
addonsWindow = controlBar.registerWindow(new AddonsWindow(typedTerm, addons));
|
||||
addonSearchWindow = controlBar.registerWindow(new AddonSearchWindow(typedTerm, addons), { afterId: 'addons', hidden: true, italics: true });
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { BaseWindow } from './baseWindow';
|
||||
import type { IControlWindow } from '../controlBar';
|
||||
import type { Terminal, IBufferCell } from '@xterm/xterm';
|
||||
import type { AddonCollection } from '../../types';
|
||||
|
||||
export class CellInspectorWindow extends BaseWindow implements IControlWindow {
|
||||
public readonly id = 'cell-inspector';
|
||||
public readonly label = 'Cell Inspector';
|
||||
|
||||
private _container: HTMLElement;
|
||||
private _positionEl: HTMLElement;
|
||||
private _charEl: HTMLElement;
|
||||
private _codeEl: HTMLElement;
|
||||
private _widthEl: HTMLElement;
|
||||
private _fgEl: HTMLElement;
|
||||
private _bgEl: HTMLElement;
|
||||
private _attrsEl: HTMLElement;
|
||||
|
||||
constructor(
|
||||
terminal: Terminal,
|
||||
addons: AddonCollection,
|
||||
) {
|
||||
super(terminal, addons);
|
||||
}
|
||||
|
||||
public build(container: HTMLElement): void {
|
||||
this._container = container;
|
||||
|
||||
const dl = document.createElement('dl');
|
||||
dl.style.fontFamily = 'monospace';
|
||||
dl.style.fontSize = '12px';
|
||||
|
||||
this._positionEl = this._addRow(dl, 'Position');
|
||||
this._charEl = this._addRow(dl, 'Character');
|
||||
this._codeEl = this._addRow(dl, 'Codepoint');
|
||||
this._widthEl = this._addRow(dl, 'Width');
|
||||
this._fgEl = this._addRow(dl, 'Foreground');
|
||||
this._bgEl = this._addRow(dl, 'Background');
|
||||
this._attrsEl = this._addRow(dl, 'Attributes');
|
||||
|
||||
container.appendChild(dl);
|
||||
|
||||
// Add mouse move listener
|
||||
this._setupMouseListener();
|
||||
}
|
||||
|
||||
private _addRow(dl: HTMLElement, label: string): HTMLElement {
|
||||
const dt = document.createElement('dt');
|
||||
dt.textContent = label;
|
||||
dt.style.fontWeight = 'bold';
|
||||
dt.style.marginTop = '4px';
|
||||
dl.appendChild(dt);
|
||||
|
||||
const dd = document.createElement('dd');
|
||||
dd.textContent = '-';
|
||||
dd.style.margin = '0 0 0 16px';
|
||||
dl.appendChild(dd);
|
||||
|
||||
return dd;
|
||||
}
|
||||
|
||||
private _setupMouseListener(): void {
|
||||
const terminal = this._terminal;
|
||||
if (!terminal.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cell = terminal.buffer.active.getNullCell();
|
||||
|
||||
terminal.element.addEventListener('mousemove', (e: MouseEvent) => {
|
||||
const core = (terminal as any)._core;
|
||||
const coords = core._mouseService?.getCoords(e, core.screenElement, terminal.cols, terminal.rows);
|
||||
if (!coords) {
|
||||
this._clearDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
const x = coords[0] - 1;
|
||||
const y = coords[1] - 1;
|
||||
const bufferY = terminal.buffer.active.viewportY + y;
|
||||
|
||||
const line = terminal.buffer.active.getLine(bufferY);
|
||||
if (!line) {
|
||||
this._clearDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
const cellData = line.getCell(x, cell);
|
||||
if (!cellData) {
|
||||
this._clearDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateDisplay(x, y, bufferY, cellData);
|
||||
});
|
||||
|
||||
terminal.element.addEventListener('mouseleave', () => {
|
||||
this._clearDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
private _clearDisplay(): void {
|
||||
this._positionEl.textContent = '-';
|
||||
this._charEl.textContent = '-';
|
||||
this._codeEl.textContent = '-';
|
||||
this._widthEl.textContent = '-';
|
||||
this._fgEl.textContent = '-';
|
||||
this._bgEl.textContent = '-';
|
||||
this._attrsEl.textContent = '-';
|
||||
}
|
||||
|
||||
private _updateDisplay(x: number, y: number, bufferY: number, cell: IBufferCell): void {
|
||||
// Position
|
||||
this._positionEl.textContent = `x=${x}, y=${y} (buffer: ${bufferY})`;
|
||||
|
||||
// Character
|
||||
const chars = cell.getChars();
|
||||
this._charEl.textContent = chars.length > 0 ? `"${chars}"` : '(empty)';
|
||||
|
||||
// Codepoint(s)
|
||||
if (chars.length > 0) {
|
||||
const codepoints = [...chars].map(c => {
|
||||
const cp = c.codePointAt(0)!;
|
||||
return `U+${cp.toString(16).toUpperCase().padStart(4, '0')}`;
|
||||
});
|
||||
this._codeEl.textContent = codepoints.join(' ');
|
||||
} else {
|
||||
this._codeEl.textContent = '(none)';
|
||||
}
|
||||
|
||||
// Width
|
||||
this._widthEl.textContent = cell.getWidth().toString();
|
||||
|
||||
// Foreground color
|
||||
this._fgEl.textContent = this._formatFgColor(
|
||||
cell.getFgColor(),
|
||||
cell.isFgDefault(),
|
||||
cell.isFgPalette(),
|
||||
cell.isFgRGB()
|
||||
);
|
||||
|
||||
// Background color
|
||||
this._bgEl.textContent = this._formatBgColor(
|
||||
cell.getBgColor(),
|
||||
cell.isBgDefault(),
|
||||
cell.isBgPalette(),
|
||||
cell.isBgRGB()
|
||||
);
|
||||
|
||||
// Attributes (SGR codes)
|
||||
const attrs: string[] = [];
|
||||
if (cell.isBold()) attrs.push('bold (1)');
|
||||
if (cell.isDim()) attrs.push('dim (2)');
|
||||
if (cell.isItalic()) attrs.push('italic (3)');
|
||||
if (cell.isUnderline()) attrs.push('underline (4)');
|
||||
if (cell.isBlink()) attrs.push('blink (5)');
|
||||
if (cell.isInverse()) attrs.push('inverse (7)');
|
||||
if (cell.isInvisible()) attrs.push('invisible (8)');
|
||||
if (cell.isStrikethrough()) attrs.push('strikethrough (9)');
|
||||
if (cell.isOverline()) attrs.push('overline (53)');
|
||||
this._attrsEl.textContent = attrs.length > 0 ? attrs.join(', ') : '(none)';
|
||||
}
|
||||
|
||||
private _formatFgColor(color: number, isDefault: boolean, isPalette: boolean, isRGB: boolean): string {
|
||||
if (isDefault) {
|
||||
return 'default (39)';
|
||||
}
|
||||
if (isPalette) {
|
||||
// SGR 30-37 for colors 0-7, 90-97 for colors 8-15, or 38;5;n for 0-255
|
||||
let sgr: string;
|
||||
if (color < 8) {
|
||||
sgr = `${30 + color}`;
|
||||
} else if (color < 16) {
|
||||
sgr = `${90 + color - 8}`;
|
||||
} else {
|
||||
sgr = `38;5;${color}`;
|
||||
}
|
||||
return `palette(${color}) (${sgr})`;
|
||||
}
|
||||
if (isRGB) {
|
||||
const r = (color >> 16) & 0xFF;
|
||||
const g = (color >> 8) & 0xFF;
|
||||
const b = color & 0xFF;
|
||||
return `#${color.toString(16).toUpperCase().padStart(6, '0')} (38;2;${r};${g};${b})`;
|
||||
}
|
||||
return `unknown(${color})`;
|
||||
}
|
||||
|
||||
private _formatBgColor(color: number, isDefault: boolean, isPalette: boolean, isRGB: boolean): string {
|
||||
if (isDefault) {
|
||||
return 'default (49)';
|
||||
}
|
||||
if (isPalette) {
|
||||
// SGR 40-47 for colors 0-7, 100-107 for colors 8-15, or 48;5;n for 0-255
|
||||
let sgr: string;
|
||||
if (color < 8) {
|
||||
sgr = `${40 + color}`;
|
||||
} else if (color < 16) {
|
||||
sgr = `${100 + color - 8}`;
|
||||
} else {
|
||||
sgr = `48;5;${color}`;
|
||||
}
|
||||
return `palette(${color}) (${sgr})`;
|
||||
}
|
||||
if (isRGB) {
|
||||
const r = (color >> 16) & 0xFF;
|
||||
const g = (color >> 8) & 0xFF;
|
||||
const b = color & 0xFF;
|
||||
return `#${color.toString(16).toUpperCase().padStart(6, '0')} (48;2;${r};${g};${b})`;
|
||||
}
|
||||
return `unknown(${color})`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user