feat: upgrade terminal to xterm.js + WebSocket
- Rewrite terminal.js: xterm.js Terminal per tab, WebSocket per connection - Light theme terminal (Lumina: white bg, blue cursor, proper ANSI colors) - Alpine.js keeps tab management, xterm.js handles rendering - Each tab gets its own WebSocket to /terminal/ws/:hostID - Mock handler iterates chars (fixes multi-char xterm.js messages) - Sync mock commands with React template (help, ls, docker ps, etc.) - Add xterm.css custom scrollbar styles - terminal_content.html: xterm containers with Alpine show/hide per tab
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
<!-- Terminal shell frame -->
|
||||
<div class="flex-1 bg-white border border-outline-variant rounded-2xl shadow-sm flex flex-col overflow-hidden min-h-0">
|
||||
<!-- Terminal Header -->
|
||||
<div class="px-4 py-2 bg-surface-container-low border-b border-outline-variant/30 flex justify-between items-center">
|
||||
<div class="px-4 py-2 bg-surface-container-low border-b border-outline-variant/30 flex justify-between items-center shrink-0">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="w-3 h-3 rounded-full bg-error/80"></span>
|
||||
<span class="w-3 h-3 rounded-full bg-primary-container/80"></span>
|
||||
@@ -49,18 +49,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Console display -->
|
||||
<div class="flex-1 overflow-y-auto min-h-0 font-mono text-sm leading-relaxed p-5 bg-white" id="terminal-output">
|
||||
<template x-for="(line, idx) in history" :key="idx">
|
||||
<div :class="line.type === 'input' ? 'text-on-surface font-bold' : line.type === 'error' ? 'text-error bg-error-container/20 px-2.5 py-1 rounded-md' : line.type === 'success' ? 'text-secondary bg-secondary-container/10 px-2.5 py-1 rounded-md font-semibold' : 'text-[#2244aa]'"
|
||||
class="whitespace-pre-wrap" x-text="line.text"></div>
|
||||
<!-- xterm.js containers — one per tab, shown/hidden by Alpine -->
|
||||
<div class="flex-1 relative min-h-0">
|
||||
<template x-for="(tab, idx) in tabs" :key="tab.id">
|
||||
<div x-show="idx === activeTab"
|
||||
x-cloak
|
||||
class="absolute inset-0">
|
||||
<div :id="'xterm-' + tab.id" class="w-full h-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="h-4"></div>
|
||||
</div>
|
||||
|
||||
<!-- Input prompt -->
|
||||
<form @submit.prevent="submitCommand()"
|
||||
class="p-3 border-t border-outline-variant/40 bg-surface-container-low flex items-center gap-2.5">
|
||||
class="p-3 border-t border-outline-variant/40 bg-surface-container-low flex items-center gap-2.5 shrink-0">
|
||||
<span class="font-mono text-xs font-bold text-primary pl-2 shrink-0 flex items-center gap-1">
|
||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.75 17.25L12 21m0 0l-3.75-3.75M12 21V3"/></svg>
|
||||
root@<span x-text="tabs.length ? tabs[activeTab].name : 'host'"></span>:~$
|
||||
@@ -88,4 +90,4 @@
|
||||
<button type="button" @click="insertCommand('clear')" class="px-2.5 py-1 rounded-md bg-surface-container border border-outline-variant/30 hover:border-primary hover:bg-primary-container/10 transition-colors">clear</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user