first commit
This commit is contained in:
63
xterminal/source/renderer/dom.ts
Normal file
63
xterminal/source/renderer/dom.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { isArray, isObject } from "../helpers";
|
||||
import type { IElementProps } from "./interface";
|
||||
|
||||
export const NEWLINE = "\n";
|
||||
|
||||
export const SPACE = " ";
|
||||
|
||||
/**
|
||||
* CSS class names
|
||||
*/
|
||||
export const THEME = {
|
||||
CONTAINER: "xt",
|
||||
INACTIVE: "xt-inactive",
|
||||
CURSOR: "xt-cursor",
|
||||
OUTPUT: "xt-stdout",
|
||||
INPUT: "xt-stdin"
|
||||
};
|
||||
|
||||
export function scrollDown(el: HTMLElement): void {
|
||||
if (el) el.scrollTo(0, el.scrollHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ready to use HTMLElement
|
||||
*
|
||||
* https://github.com/henryhale/render-functions
|
||||
*
|
||||
* https://vuejs.org/guide/extras/render-function.html
|
||||
*
|
||||
* @param tag The HTML element tag
|
||||
* @param options The some properties of the element
|
||||
* @returns The HTML Element
|
||||
*/
|
||||
export function h<T extends HTMLElement>(
|
||||
tag: string,
|
||||
options?: IElementProps
|
||||
): T {
|
||||
const elem = document.createElement(tag);
|
||||
if (!isObject(options)) {
|
||||
return elem as T;
|
||||
}
|
||||
if (options?.id) {
|
||||
elem.id = options.id || "";
|
||||
}
|
||||
if (options?.class) {
|
||||
elem.className = options.class || "";
|
||||
}
|
||||
if (options?.content) {
|
||||
elem.appendChild(document.createTextNode(options.content || ""));
|
||||
}
|
||||
if (options?.html) {
|
||||
elem.innerHTML = options.html;
|
||||
}
|
||||
if (isArray(options?.children)) {
|
||||
options.children.forEach((c) => elem.append(c));
|
||||
}
|
||||
if (isObject(options?.props)) {
|
||||
Object.entries(options.props).forEach((v) =>
|
||||
elem.setAttribute(v[0], v[1])
|
||||
);
|
||||
}
|
||||
return elem as T;
|
||||
}
|
||||
28
xterminal/source/renderer/events.ts
Normal file
28
xterminal/source/renderer/events.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { IDisposable } from "../types";
|
||||
|
||||
export const ENTER_KEY = "Enter",
|
||||
TAB_KEY = "Tab",
|
||||
ARROW_UP_KEY = "ArrowUp",
|
||||
ARROW_DOWN_KEY = "ArrowDown";
|
||||
|
||||
/**
|
||||
* Attaches an event listener to the element returning a disposable object
|
||||
* to remove the event listener
|
||||
*/
|
||||
export function addEvent(
|
||||
el: Element | Document | Window | VisualViewport | EventTarget,
|
||||
type: string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
handler: (e: any) => void,
|
||||
opt?: boolean | AddEventListenerOptions
|
||||
): IDisposable {
|
||||
el.addEventListener(type, handler, opt);
|
||||
let disposed = false;
|
||||
return {
|
||||
dispose() {
|
||||
if (disposed) return;
|
||||
el.removeEventListener(type, handler, opt);
|
||||
disposed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
68
xterminal/source/renderer/index.ts
Normal file
68
xterminal/source/renderer/index.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { THEME, h } from "./dom";
|
||||
|
||||
/**
|
||||
* Generate a build for the output component
|
||||
* @param target The parent element in which it is mounted
|
||||
* @returns DOM reference to the console box and output container
|
||||
*/
|
||||
export default function outputBuild(target: HTMLElement) {
|
||||
const consoleBox = h<HTMLSpanElement>("span");
|
||||
|
||||
const outputBox = h<HTMLDivElement>("div", {
|
||||
class: THEME.OUTPUT,
|
||||
children: [consoleBox]
|
||||
});
|
||||
|
||||
target.appendChild(outputBox);
|
||||
|
||||
return { outputBox, consoleBox };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a build for the input component
|
||||
* @param target The parent element in which it is mounted
|
||||
* @returns DOM reference to the input element
|
||||
*/
|
||||
export function inputBuild(target: HTMLElement) {
|
||||
const inputBox = h<HTMLTextAreaElement>("textarea", {
|
||||
props: {
|
||||
// 明确声明为普通文本输入,降低移动端将其识别为登录表单的概率。
|
||||
spellcheck: false,
|
||||
autocorrect: "off",
|
||||
autocapitalize: "off",
|
||||
autocomplete: "off",
|
||||
name: "xterminal_input",
|
||||
inputmode: "text",
|
||||
enterkeyhint: "enter",
|
||||
rows: 1,
|
||||
wrap: "off"
|
||||
}
|
||||
});
|
||||
// 部分密码管理器会读取 data-* 做识别,显式标记为非登录用途。
|
||||
inputBox.setAttribute("data-form-type", "other");
|
||||
inputBox.setAttribute("data-lpignore", "true");
|
||||
inputBox.setAttribute("data-1p-ignore", "true");
|
||||
inputBox.setAttribute("data-bwignore", "true");
|
||||
inputBox.setAttribute("aria-autocomplete", "none");
|
||||
inputBox.setAttribute("autocapitalize", "off");
|
||||
inputBox.setAttribute("autocorrect", "off");
|
||||
// 某些浏览器在首帧点击文本输入框时会误触发凭据弹窗;
|
||||
// 先只读,获得焦点后立刻解除,可显著降低“刷新后点一下就弹登录”的概率。
|
||||
inputBox.readOnly = true;
|
||||
inputBox.addEventListener(
|
||||
"focus",
|
||||
() => {
|
||||
inputBox.readOnly = false;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
const stdin = h<HTMLDivElement>("div", {
|
||||
class: THEME.INPUT,
|
||||
children: [inputBox]
|
||||
});
|
||||
|
||||
target.appendChild(stdin);
|
||||
|
||||
return inputBox;
|
||||
}
|
||||
37
xterminal/source/renderer/interface.ts
Normal file
37
xterminal/source/renderer/interface.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { IDisposable } from "../types";
|
||||
|
||||
/**
|
||||
* Render function - element props
|
||||
*/
|
||||
export interface IElementProps {
|
||||
id?: string;
|
||||
class?: string;
|
||||
content?: string;
|
||||
html?: string;
|
||||
children?: (string | Node)[];
|
||||
props?: object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key Bindings to the Input
|
||||
*/
|
||||
interface IKeyBindingAction {
|
||||
(arg1: unknown, arg2?: unknown): void;
|
||||
}
|
||||
|
||||
export interface IKeyBindings {
|
||||
[key: string]: IKeyBindingAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer
|
||||
*/
|
||||
export interface IRenderer extends IDisposable {
|
||||
canInput: boolean;
|
||||
setKeyBindings(options: IKeyBindings): void;
|
||||
mount(el: HTMLElement): void;
|
||||
focusInput(): void;
|
||||
blurInput(): void;
|
||||
clearConsole(): void;
|
||||
output(data: string): void;
|
||||
}
|
||||
Reference in New Issue
Block a user