Skip to content

Commit

Permalink
wip(panel/console): added scroll to bottom button
Browse files Browse the repository at this point in the history
  • Loading branch information
tabarra committed Jan 8, 2024
1 parent dac1aaf commit 08339c8
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/dev_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- [x] terminal layout + jsx
- [x] basic xterm.js (canvas mode)
- [x] auto re-fit
- [ ] scroll to bottom button
- [x] scroll to bottom button
- [ ] search addon + search bar
- [ ] custom event handler for f5, esc/ctrl+f (search), and ctrl+c
- [ ] command history (arrows only) without local storage
Expand Down
14 changes: 8 additions & 6 deletions panel/src/pages/LiveConsole/LiveConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { useSetPageTitle } from "@/hooks/pages";
import LiveConsoleFooter from "./LiveConsoleFooter";
import LiveConsoleHeader from "./LiveConsoleHeader";
import { ChevronsDownIcon } from "lucide-react";
import type { ITerminalInitOnlyOptions, ITerminalOptions, ITheme } from 'xterm';
import type { ITerminalInitOnlyOptions, ITerminalOptions, ITheme } from '@xterm/xterm';
import { Terminal } from '@xterm/xterm';
import { CanvasAddon } from '@xterm/addon-canvas';
import { FitAddon } from '@xterm/addon-fit';
import { useEventListener } from 'usehooks-ts';
import debounce from 'debounce';
import '@xterm/xterm/css/xterm.css';
import ScrollDownAddon from "./ScrollDownAddon";


//From legacy systemLog.ejs, based on the ANSI-UP colors
Expand Down Expand Up @@ -54,7 +55,6 @@ const terminalOptions: ITerminalOptions | ITerminalInitOnlyOptions = {


export default function LiveConsole() {
const jumpBottomBtnRef = useRef<HTMLButtonElement>(null);
// const [isSaveSheetOpen, setIsSaveSheetOpen] = useState(false);
const [isConnected, setIsConnected] = useState(false);
const setPageTitle = useSetPageTitle();
Expand All @@ -64,6 +64,7 @@ export default function LiveConsole() {
/**
* xterm stuff
*/
const jumpBottomBtnRef = useRef<HTMLButtonElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const termElRef = useRef<HTMLDivElement>(null);
const term = useMemo(() => new Terminal(terminalOptions), []);
Expand Down Expand Up @@ -91,10 +92,11 @@ export default function LiveConsole() {

useEffect(() => {
if (containerRef.current && termElRef.current && !term.element) {
console.log('terminal init', Math.random().toString(36).substring(2, 15));
console.log('live console xterm init');
termElRef.current.innerHTML = ''; //due to HMR, the terminal element might still be there
term.loadAddon(fitAddon);
term.loadAddon(new CanvasAddon());
term.loadAddon(new ScrollDownAddon(jumpBottomBtnRef));
term.open(termElRef.current);
refitTerminal();
term.write('\x1b[?25l'); //hide cursor
Expand Down Expand Up @@ -145,16 +147,16 @@ export default function LiveConsole() {
<div className="flex flex-col relative grow">
{/* <LiveConsoleSaveSheet isOpen={isSaveSheetOpen} closeSheet={() => setIsSaveSheetOpen(false)} /> */}

<div ref={containerRef} className='w-full h-full relative overflow-hidden bg-pink-500'>
<div ref={containerRef} className='w-full h-full relative overflow-hidden'>
<div ref={termElRef} className='absolute inset-x-2 top-1' />
</div>

<button
ref={jumpBottomBtnRef}
className='absolute bottom-0 right-6 z-50 opacity-75'
className='absolute bottom-0 right-6 z-50 hidden opacity-75'
onClick={() => { term.scrollToBottom() }}
>
<ChevronsDownIcon className='w-20 h-20 animate-pulse hover:animate-none hover:scale-105' />
<ChevronsDownIcon className='w-20 h-20 animate-pulse hover:animate-none hover:scale-110' />
</button>
</div>

Expand Down
48 changes: 48 additions & 0 deletions panel/src/pages/LiveConsole/ScrollDownAddon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm';


/**
* This addon is used to toggle the "scroll down" button based on viewportY and baseY.
*
* References:
* http://xtermjs.org/docs/guides/using-addons/
* http://xtermjs.org/docs/api/terminal/interfaces/ibuffer/#basey
*/
export default class ScrollDownAddon implements ITerminalAddon {
private btnRef: React.RefObject<HTMLButtonElement>;
private _disposables: IDisposable[] = [];

constructor(btnRef: React.RefObject<HTMLButtonElement>) {
this.btnRef = btnRef;
}

activate(terminal: Terminal): void {
const isAtBottom = () => {
// console.log({
// viewportY: terminal.buffer.active.viewportY,
// baseY: terminal.buffer.active.baseY,
// rows: terminal.rows,
// });
return terminal.buffer.active.viewportY === terminal.buffer.active.baseY;
};

const onScrollDisposable = terminal.onScroll(() => {
if (this.btnRef.current && isAtBottom()) {
this.btnRef.current.classList.add('hidden');
}
});
this._disposables.push(onScrollDisposable);

const onLineFeedDisposable = terminal.onLineFeed(() => {
if (this.btnRef.current && !isAtBottom()) {
this.btnRef.current.classList.remove('hidden');
}
});
this._disposables.push(onLineFeedDisposable);
}

dispose(): void {
this._disposables.forEach(d => d.dispose());
this._disposables.length = 0;
}
}

0 comments on commit 08339c8

Please sign in to comment.