Skip to content

Commit ca2cbd5

Browse files
Fix terminal colors and functionality issues (#86)
* Fix terminal colors and stop button functionality - Updated terminal theme to GitHub Dark with proper ANSI color support - Fixed terminal background and foreground colors for better readability - Removed aggressive CSS overrides that were breaking ANSI color handling - Fixed stop button restarting script execution issue - Added isStopped state to prevent automatic script restart after stop - Improved WebSocket connection stability to prevent duplicate executions - Fixed cursor rendering issues in whiptail sessions - Enhanced terminal styling with proper color palette configuration * Fix downloaded scripts terminal functionality - Add install functionality to DownloadedScriptsTab component - Pass onInstallScript prop from main page to DownloadedScriptsTab - Enable terminal display when installing from downloaded scripts tab - Maintain consistency with available scripts tab functionality - Fix missing terminal integration for downloaded script installations * Improve mobile terminal focus behavior - Add terminal ref to main page for precise scrolling - Update scroll behavior to focus terminal instead of page top - Add mobile-specific offset for better terminal visibility - Remove generic page scroll from ScriptDetailModal - Ensure terminal is properly focused when starting installations on mobile
1 parent d6803b9 commit ca2cbd5

File tree

7 files changed

+167
-44
lines changed

7 files changed

+167
-44
lines changed

scripts/ct/debian.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env bash
2+
SCRIPT_DIR="$(dirname "$0")"
3+
source "$SCRIPT_DIR/../core/build.func"
4+
# Copyright (c) 2021-2025 tteck
5+
# Author: tteck (tteckster)
6+
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
7+
# Source: https://www.debian.org/
8+
9+
APP="Debian"
10+
var_tags="${var_tags:-os}"
11+
var_cpu="${var_cpu:-1}"
12+
var_ram="${var_ram:-512}"
13+
var_disk="${var_disk:-2}"
14+
var_os="${var_os:-debian}"
15+
var_version="${var_version:-13}"
16+
var_unprivileged="${var_unprivileged:-1}"
17+
18+
header_info "$APP"
19+
variables
20+
color
21+
catch_errors
22+
23+
function update_script() {
24+
header_info
25+
check_container_storage
26+
check_container_resources
27+
if [[ ! -d /var ]]; then
28+
msg_error "No ${APP} Installation Found!"
29+
exit
30+
fi
31+
msg_info "Updating $APP LXC"
32+
$STD apt update
33+
$STD apt -y upgrade
34+
msg_ok "Updated $APP LXC"
35+
exit
36+
}
37+
38+
start
39+
build_container
40+
description
41+
42+
msg_ok "Completed Successfully!\n"
43+
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"

scripts/install/debian-install.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2021-2025 tteck
4+
# Author: tteck (tteckster)
5+
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
6+
# Source: https://www.debian.org/
7+
8+
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
9+
color
10+
verb_ip6
11+
catch_errors
12+
setting_up_container
13+
network_check
14+
update_os
15+
16+
motd_ssh
17+
customize
18+
19+
msg_info "Cleaning up"
20+
$STD apt -y autoremove
21+
$STD apt -y autoclean
22+
$STD apt -y clean
23+
msg_ok "Cleaned"
24+

src/app/_components/DownloadedScriptsTab.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@ import { FilterBar, type FilterState } from './FilterBar';
99
import { Button } from './ui/button';
1010
import type { ScriptCard as ScriptCardType } from '~/types/script';
1111

12-
export function DownloadedScriptsTab() {
12+
interface DownloadedScriptsTabProps {
13+
onInstallScript?: (
14+
scriptPath: string,
15+
scriptName: string,
16+
mode?: "local" | "ssh",
17+
server?: any,
18+
) => void;
19+
}
20+
21+
export function DownloadedScriptsTab({ onInstallScript }: DownloadedScriptsTabProps) {
1322
const [selectedSlug, setSelectedSlug] = useState<string | null>(null);
1423
const [isModalOpen, setIsModalOpen] = useState(false);
1524
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
@@ -462,9 +471,7 @@ export function DownloadedScriptsTab() {
462471
script={scriptData?.success ? scriptData.script : null}
463472
isOpen={isModalOpen}
464473
onClose={handleCloseModal}
465-
onInstallScript={() => {
466-
// Downloaded scripts don't need installation
467-
}}
474+
onInstallScript={onInstallScript}
468475
/>
469476
</div>
470477
</div>

src/app/_components/ScriptDetailModal.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,6 @@ export function ScriptDetailModal({
120120
// Pass execution mode and server info to the parent
121121
onInstallScript(scriptPath, scriptName, mode, server);
122122

123-
// Scroll to top of the page to see the terminal
124-
window.scrollTo({ top: 0, behavior: "smooth" });
125-
126123
onClose(); // Close the modal when starting installation
127124
}
128125
};

src/app/_components/Terminal.tsx

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
2929
const [lastInputSent, setLastInputSent] = useState<string | null>(null);
3030
const [inWhiptailSession, setInWhiptailSession] = useState(false);
3131
const [isMobile, setIsMobile] = useState(false);
32+
const [isStopped, setIsStopped] = useState(false);
3233
const terminalRef = useRef<HTMLDivElement>(null);
3334
const xtermRef = useRef<any>(null);
3435
const fitAddonRef = useRef<any>(null);
@@ -64,23 +65,8 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
6465
if (message.data.includes('\x1b[2J') || message.data.includes('\x1b[H\x1b[2J')) {
6566
// This is a clear screen sequence, ensure it's processed correctly
6667
xtermRef.current.write(message.data);
67-
} else if (message.data.includes('\x1b[') && message.data.includes('H')) {
68-
// This is a cursor positioning sequence, often implies a redraw of the entire screen
69-
if (inWhiptailSession) {
70-
// In whiptail session, completely reset the terminal
71-
// Completely clear everything
72-
xtermRef.current.clear();
73-
xtermRef.current.write('\x1b[2J\x1b[H\x1b[3J\x1b[2J');
74-
// Reset the terminal state
75-
xtermRef.current.reset();
76-
// Write the new content after reset
77-
setTimeout(() => {
78-
xtermRef.current.write(message.data);
79-
}, 100);
80-
} else {
81-
xtermRef.current.write(message.data);
82-
}
8368
} else {
69+
// Let xterm handle all ANSI sequences naturally
8470
xtermRef.current.write(message.data);
8571
}
8672
break;
@@ -153,9 +139,27 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
153139

154140
const terminal = new XTerm({
155141
theme: {
156-
background: '#000000',
157-
foreground: '#00ff00',
158-
cursor: '#00ff00',
142+
background: '#0d1117',
143+
foreground: '#e6edf3',
144+
cursor: '#58a6ff',
145+
cursorAccent: '#0d1117',
146+
// Let ANSI colors work naturally - only define basic colors
147+
black: '#484f58',
148+
red: '#f85149',
149+
green: '#3fb950',
150+
yellow: '#d29922',
151+
blue: '#58a6ff',
152+
magenta: '#bc8cff',
153+
cyan: '#39d353',
154+
white: '#b1bac4',
155+
brightBlack: '#6e7681',
156+
brightRed: '#ff7b72',
157+
brightGreen: '#56d364',
158+
brightYellow: '#e3b341',
159+
brightBlue: '#79c0ff',
160+
brightMagenta: '#d2a8ff',
161+
brightCyan: '#56d364',
162+
brightWhite: '#f0f6fc',
159163
},
160164
fontSize: isMobile ? 7 : 14,
161165
fontFamily: 'JetBrains Mono, Fira Code, Cascadia Code, Monaco, Menlo, Ubuntu Mono, monospace',
@@ -189,6 +193,13 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
189193
// Open terminal
190194
terminal.open(terminalElement);
191195

196+
// Ensure proper terminal rendering
197+
setTimeout(() => {
198+
terminal.refresh(0, terminal.rows - 1);
199+
// Ensure cursor is properly positioned
200+
terminal.focus();
201+
}, 100);
202+
192203
// Fit after a small delay to ensure proper sizing
193204
setTimeout(() => {
194205
fitAddon.fit();
@@ -269,6 +280,7 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
269280
}
270281

271282
isConnectingRef.current = true;
283+
const isInitialConnection = !hasConnectedRef.current;
272284
hasConnectedRef.current = true;
273285

274286
// Small delay to prevent rapid reconnection
@@ -284,17 +296,19 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
284296
setIsConnected(true);
285297
isConnectingRef.current = false;
286298

287-
// Send start message immediately after connection
288-
const message = {
289-
action: 'start',
290-
scriptPath,
291-
executionId,
292-
mode,
293-
server,
294-
isUpdate,
295-
containerId
296-
};
297-
ws.send(JSON.stringify(message));
299+
// Only auto-start on initial connection, not on reconnections
300+
if (isInitialConnection && !isRunning) {
301+
const message = {
302+
action: 'start',
303+
scriptPath,
304+
executionId,
305+
mode,
306+
server,
307+
isUpdate,
308+
containerId
309+
};
310+
ws.send(JSON.stringify(message));
311+
}
298312
};
299313

300314
ws.onmessage = (event) => {
@@ -335,7 +349,8 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
335349
}, [scriptPath, executionId, mode, server, isUpdate, containerId, handleMessage, isMobile]);
336350

337351
const startScript = () => {
338-
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
352+
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN && !isRunning) {
353+
setIsStopped(false);
339354
wsRef.current.send(JSON.stringify({
340355
action: 'start',
341356
scriptPath,
@@ -350,6 +365,8 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
350365

351366
const stopScript = () => {
352367
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
368+
setIsStopped(true);
369+
setIsRunning(false);
353370
wsRef.current.send(JSON.stringify({
354371
action: 'stop',
355372
executionId
@@ -586,10 +603,10 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
586603
<div className="flex flex-wrap gap-1 sm:gap-2">
587604
<Button
588605
onClick={startScript}
589-
disabled={!isConnected || isRunning}
606+
disabled={!isConnected || (isRunning && !isStopped)}
590607
variant="default"
591608
size="sm"
592-
className={`text-xs sm:text-sm ${isConnected && !isRunning ? 'bg-green-600 hover:bg-green-700' : 'bg-muted text-muted-foreground cursor-not-allowed'}`}
609+
className={`text-xs sm:text-sm ${isConnected && (!isRunning || isStopped) ? 'bg-green-600 hover:bg-green-700' : 'bg-muted text-muted-foreground cursor-not-allowed'}`}
593610
>
594611
<Play className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
595612
<span className="hidden sm:inline">Start</span>

src/app/page.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
'use client';
33

4-
import { useState } from 'react';
4+
import { useState, useRef } from 'react';
55
import { ScriptsGrid } from './_components/ScriptsGrid';
66
import { DownloadedScriptsTab } from './_components/DownloadedScriptsTab';
77
import { InstalledScriptsTab } from './_components/InstalledScriptsTab';
@@ -16,9 +16,25 @@ import { Rocket, Package, HardDrive, FolderOpen } from 'lucide-react';
1616
export default function Home() {
1717
const [runningScript, setRunningScript] = useState<{ path: string; name: string; mode?: 'local' | 'ssh'; server?: any } | null>(null);
1818
const [activeTab, setActiveTab] = useState<'scripts' | 'downloaded' | 'installed'>('scripts');
19+
const terminalRef = useRef<HTMLDivElement>(null);
20+
21+
const scrollToTerminal = () => {
22+
if (terminalRef.current) {
23+
// Get the element's position and scroll with a small offset for better mobile experience
24+
const elementTop = terminalRef.current.offsetTop;
25+
const offset = window.innerWidth < 768 ? 20 : 0; // Small offset on mobile
26+
27+
window.scrollTo({
28+
top: elementTop - offset,
29+
behavior: 'smooth'
30+
});
31+
}
32+
};
1933

2034
const handleRunScript = (scriptPath: string, scriptName: string, mode?: 'local' | 'ssh', server?: any) => {
2135
setRunningScript({ path: scriptPath, name: scriptName, mode, server });
36+
// Scroll to terminal after a short delay to ensure it's rendered
37+
setTimeout(scrollToTerminal, 100);
2238
};
2339

2440
const handleCloseTerminal = () => {
@@ -102,7 +118,7 @@ export default function Home() {
102118

103119
{/* Running Script Terminal */}
104120
{runningScript && (
105-
<div className="mb-8">
121+
<div ref={terminalRef} className="mb-8">
106122
<Terminal
107123
scriptPath={runningScript.path}
108124
onClose={handleCloseTerminal}
@@ -118,7 +134,7 @@ export default function Home() {
118134
)}
119135

120136
{activeTab === 'downloaded' && (
121-
<DownloadedScriptsTab />
137+
<DownloadedScriptsTab onInstallScript={handleRunScript} />
122138
)}
123139

124140
{activeTab === 'installed' && (

src/styles/globals.css

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128

129129
/* Terminal-specific styles for ANSI escape code rendering */
130130
.terminal-output {
131-
font-family: 'Courier New', 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
131+
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
132132
line-height: 1.2;
133133
}
134134

@@ -142,6 +142,25 @@
142142
background-color: inherit;
143143
}
144144

145+
/* Enhanced terminal styling */
146+
.xterm {
147+
padding: 0.5rem;
148+
}
149+
150+
/* Set basic background - let ANSI colors work naturally */
151+
.xterm .xterm-viewport {
152+
background-color: #0d1117;
153+
}
154+
155+
.xterm .xterm-screen {
156+
background-color: #0d1117;
157+
}
158+
159+
/* Better selection colors */
160+
.xterm .xterm-selection {
161+
background-color: #264f78;
162+
}
163+
145164
/* Mobile-specific improvements */
146165
@media (max-width: 640px) {
147166
/* Improve touch targets */

0 commit comments

Comments
 (0)