first commit

This commit is contained in:
douboer
2026-03-21 18:57:10 +08:00
commit c49aa1a5e9
570 changed files with 107167 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
/* global module */
const DEFAULT_TERMINAL_KEY_MODES = Object.freeze({
applicationCursorKeys: false,
applicationKeypad: false,
bracketedPasteMode: false
});
const DEFAULT_TERMINAL_KEY_MODIFIERS = Object.freeze({
shift: false
});
const CTRL_KEY_MAP = Object.freeze({
ctrla: "\u0001",
ctrlc: "\u0003",
ctrld: "\u0004",
ctrle: "\u0005",
ctrlk: "\u000b",
ctrll: "\u000c",
ctrlu: "\u0015",
ctrlw: "\u0017",
ctrlz: "\u001a"
});
/**
* 触屏方向区直接对应 Figma frame 2250 内部的四向布局。
* 这里保留静态资源路径作为单一真相源,页面层可按主题把路径再映射为 data URI。
*/
const TERMINAL_TOUCH_DIRECTION_KEYS = Object.freeze([
Object.freeze({
key: "up",
icon: "/assets/icons/up.svg",
slotClass: "terminal-touch-direction-btn-up"
}),
Object.freeze({
key: "left",
icon: "/assets/icons/left.svg",
slotClass: "terminal-touch-direction-btn-left"
}),
Object.freeze({
key: "down",
icon: "/assets/icons/down.svg",
slotClass: "terminal-touch-direction-btn-down"
}),
Object.freeze({
key: "right",
icon: "/assets/icons/right.svg",
slotClass: "terminal-touch-direction-btn-right"
})
]);
/**
* SH 键盘区只保留最常用的辅助键:
* 1. 仅保留移动端高频键,`home` 复用为“回到服务器工作目录”快捷键;
* 2. Figma 这一版左列示意用了 backspace/shift带 backspace 图标的按钮发送真实退格序列;
* 3. 页面层会把 shift 当成“输入框大写状态键”,这里仅保留图标和基础 key
* 4. 方向键承担基础导航,纵向操作区保留 enter/home/shift/backspace/paste/esc/ctrl+c/tab
* 5. paste 走独立剪贴板链路home 走页面层 shell 命令,其余按钮统一映射为 VT 控制序列。
*/
const TERMINAL_TOUCH_ACTION_BUTTONS = Object.freeze([
Object.freeze({
key: "enter",
icon: "/assets/icons/enter.svg",
action: "control",
slotClass: "terminal-touch-action-btn-enter"
}),
Object.freeze({
key: "home",
icon: "/assets/icons/home.svg",
action: "control",
slotClass: "terminal-touch-action-btn-home"
}),
Object.freeze({
key: "shift",
icon: "/assets/icons/shift.svg",
action: "modifier",
slotClass: "terminal-touch-action-btn-shift"
}),
Object.freeze({
key: "backspace",
icon: "/assets/icons/backspace.svg",
action: "control",
slotClass: "terminal-touch-action-btn-delete"
}),
Object.freeze({
key: "paste",
icon: "/assets/icons/paste.svg",
action: "paste",
slotClass: "terminal-touch-action-btn-paste"
}),
Object.freeze({
key: "esc",
icon: "/assets/icons/esc.svg",
action: "control",
slotClass: "terminal-touch-action-btn-esc"
}),
Object.freeze({
key: "ctrlc",
icon: "/assets/icons/ctrlc.svg",
action: "control",
slotClass: "terminal-touch-action-btn-ctrlc"
}),
Object.freeze({
key: "tab",
icon: "/assets/icons/tab.svg",
action: "control",
slotClass: "terminal-touch-action-btn-tab"
})
]);
function normalizeTerminalKeyModes(input) {
const source = input && typeof input === "object" ? input : null;
return {
applicationCursorKeys:
source && source.applicationCursorKeys !== undefined
? !!source.applicationCursorKeys
: DEFAULT_TERMINAL_KEY_MODES.applicationCursorKeys,
applicationKeypad:
source && source.applicationKeypad !== undefined
? !!source.applicationKeypad
: DEFAULT_TERMINAL_KEY_MODES.applicationKeypad,
bracketedPasteMode:
source && source.bracketedPasteMode !== undefined
? !!source.bracketedPasteMode
: DEFAULT_TERMINAL_KEY_MODES.bracketedPasteMode
};
}
function normalizeTerminalKeyModifiers(input) {
const source = input && typeof input === "object" ? input : null;
return {
shift: source && source.shift !== undefined ? !!source.shift : DEFAULT_TERMINAL_KEY_MODIFIERS.shift
};
}
/**
* Alt/Meta 在终端里通常表现为“额外前置一个 ESC”
* 因此这里把组合键统一编码成 `ESC + 基础键序列`。
*/
function encodeTerminalAltSequence(normalizedKey, modes) {
const match = /^(alt|meta)(?:[+:-]|_)?(.+)$/.exec(normalizedKey);
if (!match) return "";
const nestedKey = String(match[2] || "")
.trim()
.toLowerCase();
if (!nestedKey) return "";
if (nestedKey.length === 1) {
return `\u001b${nestedKey}`;
}
const nestedSequence = encodeTerminalKey(nestedKey, modes);
return nestedSequence ? `\u001b${nestedSequence}` : "";
}
function encodeShiftModifiedKey(normalizedKey) {
if (normalizedKey === "tab") return "\u001b[Z";
if (normalizedKey === "up") return "\u001b[1;2A";
if (normalizedKey === "down") return "\u001b[1;2B";
if (normalizedKey === "right") return "\u001b[1;2C";
if (normalizedKey === "left") return "\u001b[1;2D";
if (normalizedKey === "home") return "\u001b[1;2H";
if (normalizedKey === "end") return "\u001b[1;2F";
if (normalizedKey === "insert") return "\u001b[2;2~";
if (normalizedKey === "delete") return "\u001b[3;2~";
if (normalizedKey === "pageup") return "\u001b[5;2~";
if (normalizedKey === "pagedown") return "\u001b[6;2~";
return "";
}
function encodeTerminalKey(key, modes, modifiers) {
const normalizedKey = String(key || "")
.trim()
.toLowerCase();
const normalizedModes = normalizeTerminalKeyModes(modes);
const normalizedModifiers = normalizeTerminalKeyModifiers(modifiers);
const applicationPrefix = normalizedModes.applicationCursorKeys ? "\u001bO" : "\u001b[";
const altSequence = encodeTerminalAltSequence(normalizedKey, normalizedModes);
if (altSequence) return altSequence;
if (normalizedModifiers.shift) {
const shiftedSequence = encodeShiftModifiedKey(normalizedKey);
if (shiftedSequence) return shiftedSequence;
}
if (normalizedKey === "up") return `${applicationPrefix}A`;
if (normalizedKey === "down") return `${applicationPrefix}B`;
if (normalizedKey === "right") return `${applicationPrefix}C`;
if (normalizedKey === "left") return `${applicationPrefix}D`;
if (normalizedKey === "home") return `${applicationPrefix}H`;
if (normalizedKey === "end") return `${applicationPrefix}F`;
if (normalizedKey === "enter") return "\r";
if (normalizedKey === "tab") return "\t";
if (normalizedKey === "esc") return "\u001b";
if (normalizedKey === "backspace") return "\u007f";
if (normalizedKey === "delete") return "\u001b[3~";
if (normalizedKey === "insert") return "\u001b[2~";
if (normalizedKey === "pageup") return "\u001b[5~";
if (normalizedKey === "pagedown") return "\u001b[6~";
if (CTRL_KEY_MAP[normalizedKey]) return CTRL_KEY_MAP[normalizedKey];
return "";
}
function encodeTerminalPaste(text, modes) {
const value = String(text || "");
if (!value) return "";
const normalizedModes = normalizeTerminalKeyModes(modes);
if (!normalizedModes.bracketedPasteMode) {
return value;
}
return `\u001b[200~${value}\u001b[201~`;
}
module.exports = {
DEFAULT_TERMINAL_KEY_MODIFIERS,
DEFAULT_TERMINAL_KEY_MODES,
TERMINAL_TOUCH_DIRECTION_KEYS,
TERMINAL_TOUCH_ACTION_BUTTONS,
encodeTerminalKey,
encodeTerminalPaste,
normalizeTerminalKeyModifiers,
normalizeTerminalKeyModes
};