feat: initial commit of next_term modern linux terminal
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { TerminalView } from './terminal-view.js';
|
||||
|
||||
export class TabSplitTree {
|
||||
constructor(options = {}) {
|
||||
this.container = options.container;
|
||||
this.tabId = options.tabId;
|
||||
this.onActiveTerminalChange = options.onActiveTerminalChange || (() => {});
|
||||
this.onEmpty = options.onEmpty || (() => {});
|
||||
this.onTitleChange = options.onTitleChange || (() => {});
|
||||
|
||||
this.terminals = new Map(); // id -> TerminalView
|
||||
this.activeTerminal = null;
|
||||
|
||||
this.rootElement = document.createElement('div');
|
||||
this.rootElement.className = 'split-container horizontal';
|
||||
this.container.appendChild(this.rootElement);
|
||||
|
||||
// 创建初始窗格
|
||||
this.addPane(this.rootElement);
|
||||
}
|
||||
|
||||
addPane(parentElement, orientation = 'horizontal') {
|
||||
const paneElement = document.createElement('div');
|
||||
paneElement.className = 'split-pane';
|
||||
|
||||
parentElement.appendChild(paneElement);
|
||||
|
||||
const termView = new TerminalView({
|
||||
container: paneElement,
|
||||
onFocus: (term) => {
|
||||
this.setActiveTerminal(term);
|
||||
},
|
||||
onTitleChange: (title) => {
|
||||
if (this.activeTerminal === termView) {
|
||||
this.onTitleChange(title);
|
||||
}
|
||||
},
|
||||
onExit: () => {
|
||||
this.closeTerminal(termView.id);
|
||||
},
|
||||
});
|
||||
|
||||
this.terminals.set(termView.id, {
|
||||
termView,
|
||||
paneElement,
|
||||
parentElement,
|
||||
});
|
||||
|
||||
paneElement.addEventListener('click', () => {
|
||||
termView.focus();
|
||||
});
|
||||
|
||||
this.setActiveTerminal(termView);
|
||||
return termView;
|
||||
}
|
||||
|
||||
splitCurrent(direction = 'horizontal') {
|
||||
if (!this.activeTerminal) return;
|
||||
const currentInfo = this.terminals.get(this.activeTerminal.id);
|
||||
if (!currentInfo) return;
|
||||
|
||||
const currentPane = currentInfo.paneElement;
|
||||
const parent = currentPane.parentElement;
|
||||
|
||||
// 创建一个新的 split-container 容器替换原有 pane,实现嵌套分屏
|
||||
const splitWrapper = document.createElement('div');
|
||||
splitWrapper.className = `split-container ${direction}`;
|
||||
|
||||
parent.replaceChild(splitWrapper, currentPane);
|
||||
splitWrapper.appendChild(currentPane);
|
||||
|
||||
// 添加分割线
|
||||
const divider = document.createElement('div');
|
||||
divider.className = 'split-divider';
|
||||
splitWrapper.appendChild(divider);
|
||||
|
||||
// 添加新窗格
|
||||
const newTerm = this.addPane(splitWrapper, direction);
|
||||
|
||||
// 刷新自适应尺寸
|
||||
setTimeout(() => {
|
||||
this.fitAll();
|
||||
newTerm.focus();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
setActiveTerminal(termView) {
|
||||
this.activeTerminal = termView;
|
||||
|
||||
// 清除所有聚焦样式
|
||||
for (const info of this.terminals.values()) {
|
||||
info.paneElement.classList.remove('focused');
|
||||
}
|
||||
|
||||
const currentInfo = this.terminals.get(termView.id);
|
||||
if (currentInfo) {
|
||||
currentInfo.paneElement.classList.add('focused');
|
||||
}
|
||||
|
||||
this.onActiveTerminalChange(termView);
|
||||
this.onTitleChange(termView.title);
|
||||
}
|
||||
|
||||
closeTerminal(termId) {
|
||||
const info = this.terminals.get(termId);
|
||||
if (!info) return;
|
||||
|
||||
info.termView.destroy();
|
||||
const pane = info.paneElement;
|
||||
const parent = pane.parentElement;
|
||||
|
||||
this.terminals.delete(termId);
|
||||
pane.remove();
|
||||
|
||||
if (this.terminals.size === 0) {
|
||||
this.onEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
// 焦点转移给另一个终端
|
||||
const nextTerm = this.terminals.values().next().value.termView;
|
||||
this.setActiveTerminal(nextTerm);
|
||||
nextTerm.focus();
|
||||
this.fitAll();
|
||||
}
|
||||
|
||||
fitAll() {
|
||||
for (const info of this.terminals.values()) {
|
||||
info.termView.fit();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
for (const info of this.terminals.values()) {
|
||||
info.termView.destroy();
|
||||
}
|
||||
this.terminals.clear();
|
||||
this.rootElement.remove();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user