Improve demo by making full screen, add control bar
This commit is contained in:
@@ -19,6 +19,7 @@ if ('WebAssembly' in window) {
|
|||||||
|
|
||||||
import { Terminal, ITerminalOptions, type IDisposable, type ITheme } from '@xterm/xterm';
|
import { Terminal, ITerminalOptions, type IDisposable, type ITheme } from '@xterm/xterm';
|
||||||
import { AttachAddon } from '@xterm/addon-attach';
|
import { AttachAddon } from '@xterm/addon-attach';
|
||||||
|
import { initControlBar } from './components/controlBar';
|
||||||
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
import { LigaturesAddon } from '@xterm/addon-ligatures';
|
import { LigaturesAddon } from '@xterm/addon-ligatures';
|
||||||
@@ -231,6 +232,7 @@ if (document.location.pathname === '/test') {
|
|||||||
window.WebglAddon = WebglAddon;
|
window.WebglAddon = WebglAddon;
|
||||||
} else {
|
} else {
|
||||||
createTerminal();
|
createTerminal();
|
||||||
|
initControlBar();
|
||||||
document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
|
document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
|
||||||
document.getElementById('create-new-window').addEventListener('click', createNewWindowButtonHandler);
|
document.getElementById('create-new-window').addEventListener('click', createNewWindowButtonHandler);
|
||||||
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
|
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
|
||||||
@@ -337,6 +339,7 @@ function createTerminal(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
term.focus();
|
term.focus();
|
||||||
|
updateTerminalContainerBackground();
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(entries => {
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
if (autoResize) {
|
if (autoResize) {
|
||||||
@@ -611,10 +614,18 @@ function initOptions(term: Terminal): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
term.options[o] = value;
|
term.options[o] = value;
|
||||||
|
if (o === 'theme') {
|
||||||
|
updateTerminalContainerBackground();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTerminalContainerBackground(): void {
|
||||||
|
const bg = term.options.theme?.background ?? '#000000';
|
||||||
|
terminalContainer.style.backgroundColor = bg;
|
||||||
|
}
|
||||||
|
|
||||||
function initAddons(term: Terminal): void {
|
function initAddons(term: Terminal): void {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function initControlBar(): void {
|
||||||
|
// Sidebar resize handling
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const resizeHandleH = document.getElementById('sidebar-resize-handle-horizontal');
|
||||||
|
const resizeHandleV = document.getElementById('sidebar-resize-handle-vertical');
|
||||||
|
const resizeHandleCorner = document.getElementById('sidebar-resize-handle-corner');
|
||||||
|
let resizeMode: 'none' | 'horizontal' | 'vertical' | 'corner' = 'none';
|
||||||
|
|
||||||
|
function startResize(mode: 'horizontal' | 'vertical' | 'corner', e: MouseEvent): void {
|
||||||
|
resizeMode = mode;
|
||||||
|
document.body.style.cursor = mode === 'horizontal' ? 'ew-resize' : mode === 'vertical' ? 'ns-resize' : 'nesw-resize';
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeHandleH.addEventListener('mousedown', (e: MouseEvent) => startResize('horizontal', e));
|
||||||
|
resizeHandleV.addEventListener('mousedown', (e: MouseEvent) => startResize('vertical', e));
|
||||||
|
resizeHandleCorner.addEventListener('mousedown', (e: MouseEvent) => startResize('corner', e));
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', (e: MouseEvent) => {
|
||||||
|
if (resizeMode === 'none') return;
|
||||||
|
if (resizeMode === 'horizontal' || resizeMode === 'corner') {
|
||||||
|
const newWidth = window.innerWidth - e.clientX - 10;
|
||||||
|
sidebar.style.width = `${Math.max(200, newWidth)}px`;
|
||||||
|
}
|
||||||
|
if (resizeMode === 'vertical' || resizeMode === 'corner') {
|
||||||
|
const rect = sidebar.getBoundingClientRect();
|
||||||
|
const newHeight = e.clientY - rect.top;
|
||||||
|
sidebar.style.height = `${Math.max(100, newHeight)}px`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', () => {
|
||||||
|
resizeMode = 'none';
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
+33
-15
@@ -12,19 +12,25 @@
|
|||||||
<link rel="stylesheet" href="/style.css" />
|
<link rel="stylesheet" href="/style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 style="color: #2D2E2C">xterm.js: A terminal for the <em style="color: #5DA5D5">web</em></h1>
|
<div id="banner">
|
||||||
|
<span class="banner-title">xterm.js</span>
|
||||||
|
<div class="banner-tabs">
|
||||||
|
<button id="optionsbutton" class="tabLinks" onclick="openSection(event, 'options')">Options</button>
|
||||||
|
<button id="addonsbutton" class="tabLinks" onclick="openSection(event, 'addons')">Addons</button>
|
||||||
|
<button id="stylebutton" class="tabLinks" onclick="openSection(event, 'style')">Style</button>
|
||||||
|
<button id="testbutton" class="tabLinks" onclick="openSection(event, 'test')">Test</button>
|
||||||
|
<button id="vtbutton" class="tabLinks" onclick="openSection(event, 'vt')">VT</button>
|
||||||
|
<button id="gpubutton" class="tabLinks" onclick="openSection(event, 'gpu')">GPU</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div id="terminal-container"></div>
|
<div id="terminal-container"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid">
|
<div id="sidebar" class="grid">
|
||||||
<div class="tab">
|
<div id="sidebar-resize-handle-horizontal"></div>
|
||||||
<button id="optionsbutton" class="tabLinks" onclick="openSection(event, 'options')">Options</button>
|
<div id="sidebar-resize-handle-vertical"></div>
|
||||||
<button id="addonsbutton" class="tabLinks" onclick="openSection(event, 'addons')">Addons</button>
|
<div id="sidebar-resize-handle-corner"></div>
|
||||||
<button id="stylebutton" class="tabLinks" onclick="openSection(event, 'style')">Style</button>
|
|
||||||
<button id="testbutton" class="tabLinks" onclick="openSection(event, 'test')">Test</button>
|
|
||||||
<button id="vtbutton" class="tabLinks" onclick="openSection(event, 'vt')">VT</button>
|
|
||||||
</div>
|
|
||||||
<div id="options" class="tabContent">
|
<div id="options" class="tabContent">
|
||||||
<h3>Options</h3>
|
<h3>Options</h3>
|
||||||
<p>These options can be set in the <code>Terminal</code> constructor or by using the <code>Terminal.options</code> property.</p>
|
<p>These options can be set in the <code>Terminal</code> constructor or by using the <code>Terminal.options</code> property.</p>
|
||||||
@@ -169,12 +175,13 @@
|
|||||||
<h3>VT</h3>
|
<h3>VT</h3>
|
||||||
<div id="vt-container"></div>
|
<div id="vt-container"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="gpu" class="tabContent">
|
||||||
|
<h3>GPU</h3>
|
||||||
|
<input type="checkbox" id="texture-atlas-zoom"/>
|
||||||
|
<label for="texture-atlas-zoom">Zoom texture atlas</label>
|
||||||
|
<div id="texture-atlas"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div id="texture-atlas-container">
|
|
||||||
<input type="checkbox" id="texture-atlas-zoom"/>
|
|
||||||
<label for="texture-atlas-zoom">Zoom texture atlas</label>
|
|
||||||
<div id="texture-atlas"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<script src="dist/client-bundle.js" defer ></script>
|
<script src="dist/client-bundle.js" defer ></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -194,12 +201,23 @@
|
|||||||
document.getElementById(""+tab+"button").classList.add("active");
|
document.getElementById(""+tab+"button").classList.add("active");
|
||||||
}
|
}
|
||||||
function openSection(event, section) {
|
function openSection(event, section) {
|
||||||
|
const sidebar = document.getElementById("sidebar");
|
||||||
const tabContent = document.getElementsByClassName("tabContent");
|
const tabContent = document.getElementsByClassName("tabContent");
|
||||||
|
const tabLinks = document.getElementsByClassName("tabLinks");
|
||||||
let itr;
|
let itr;
|
||||||
|
|
||||||
|
// If clicking active tab, toggle sidebar visibility
|
||||||
|
if (event.currentTarget.classList.contains("active")) {
|
||||||
|
sidebar.classList.toggle("sidebar-hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show sidebar if hidden
|
||||||
|
sidebar.classList.remove("sidebar-hidden");
|
||||||
|
|
||||||
for (itr = 0; itr < tabContent.length; itr+=1) {
|
for (itr = 0; itr < tabContent.length; itr+=1) {
|
||||||
tabContent[itr].style.display = "none";
|
tabContent[itr].style.display = "none";
|
||||||
}
|
}
|
||||||
const tabLinks = document.getElementsByClassName("tabLinks");
|
|
||||||
for (itr = 0; itr < tabLinks.length; itr+=1) {
|
for (itr = 0; itr < tabLinks.length; itr+=1) {
|
||||||
tabLinks[itr].className = tabLinks[itr].className.replace(" active", "");
|
tabLinks[itr].className = tabLinks[itr].className.replace(" active", "");
|
||||||
}
|
}
|
||||||
|
|||||||
+97
-29
@@ -2,16 +2,50 @@ body {
|
|||||||
font-family: helvetica, sans-serif, arial;
|
font-family: helvetica, sans-serif, arial;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: #111;
|
color: #111;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 24px 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
/* Top banner */
|
||||||
text-align: center;
|
#banner {
|
||||||
|
background: #2D2E2C;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-bottom: 1px solid #0005;
|
||||||
|
}
|
||||||
|
.banner-title {
|
||||||
|
color: #5DA5D5;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.banner-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.banner-tabs button {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: #ccc;
|
||||||
|
padding: 4px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.banner-tabs button:hover {
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
.banner-tabs button.active {
|
||||||
|
background: rgba(255,255,255,0.2);
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#terminal-container {
|
#terminal-container {
|
||||||
height: 60%;
|
height: 60%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@@ -44,45 +78,79 @@ pre {
|
|||||||
|
|
||||||
#container {
|
#container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.grid {
|
.grid {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
/* max-height: 80vh;
|
|
||||||
overflow-y: auto; */
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
div:first-of-type.grid {
|
div:first-of-type.grid {
|
||||||
flex: 2;
|
flex: 2;
|
||||||
height: 60vh;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.tab {
|
|
||||||
overflow: hidden;
|
/* Sidebar */
|
||||||
|
#sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 24px;
|
||||||
|
right: 0;
|
||||||
|
width: 400px;
|
||||||
|
height: 500px;
|
||||||
|
background: #fff;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
background-color: #f1f1f1;
|
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: auto;
|
||||||
|
flex: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.1s;
|
||||||
|
}
|
||||||
|
#sidebar:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#sidebar.sidebar-hidden {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style the buttons inside the tab */
|
/* Resize handles */
|
||||||
.tab button {
|
#sidebar-resize-handle-horizontal {
|
||||||
background-color: inherit;
|
position: absolute;
|
||||||
float: left;
|
left: 0;
|
||||||
border: none;
|
top: 0;
|
||||||
outline: none;
|
bottom: 0;
|
||||||
cursor: pointer;
|
width: 6px;
|
||||||
padding: 14px 16px;
|
cursor: ew-resize;
|
||||||
transition: 0.3s;
|
background: transparent;
|
||||||
font-size: 17px;
|
|
||||||
}
|
}
|
||||||
|
#sidebar-resize-handle-horizontal:hover {
|
||||||
/* Change background color of buttons on hover */
|
background: rgba(0, 120, 212, 0.3);
|
||||||
.tab button:hover {
|
}
|
||||||
background-color: #ddd;
|
#sidebar-resize-handle-vertical {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 6px;
|
||||||
|
cursor: ns-resize;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
#sidebar-resize-handle-vertical:hover {
|
||||||
|
background: rgba(0, 120, 212, 0.3);
|
||||||
|
}
|
||||||
|
#sidebar-resize-handle-corner {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
cursor: nesw-resize;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
#sidebar-resize-handle-corner:hover {
|
||||||
|
background: rgba(0, 120, 212, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create an active/current tablink class */
|
|
||||||
.tab button.active {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style the tab content */
|
/* Style the tab content */
|
||||||
.tabContent {
|
.tabContent {
|
||||||
@@ -92,7 +160,7 @@ div:first-of-type.grid {
|
|||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#texture-atlas-zoom:checked + label + #texture-atlas canvas {
|
#texture-atlas-zoom:checked ~ #texture-atlas canvas {
|
||||||
/* Zoom atlas to the width of the container*/
|
/* Zoom atlas to the width of the container*/
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: auto !important;
|
height: auto !important;
|
||||||
|
|||||||
Reference in New Issue
Block a user