diff --git a/src/OpenTheBox.Web/WebGameHost.cs b/src/OpenTheBox.Web/WebGameHost.cs
index 3b7ca7b..db5a2e1 100644
--- a/src/OpenTheBox.Web/WebGameHost.cs
+++ b/src/OpenTheBox.Web/WebGameHost.cs
@@ -592,7 +592,7 @@ public sealed class WebGameHost
ColorSystem = ColorSystemSupport.TrueColor
});
bufferConsole.Profile.Width = WebTerminal.Width;
- bufferConsole.Profile.Height = WebTerminal.Height;
+ bufferConsole.Profile.Height = _terminal.Height;
bufferConsole.Write(InventoryPanel.Render(_state, _registry, _loc, scrollOffset, selectedIndex: selectedIndex));
diff --git a/src/OpenTheBox.Web/WebTerminal.cs b/src/OpenTheBox.Web/WebTerminal.cs
index 83920ad..86c9757 100644
--- a/src/OpenTheBox.Web/WebTerminal.cs
+++ b/src/OpenTheBox.Web/WebTerminal.cs
@@ -28,7 +28,7 @@ public sealed class WebTerminal
});
public const int Width = 120;
- public const int Height = 30;
+ public int Height { get; private set; } = 30;
public WebTerminal(IJSRuntime js)
{
@@ -37,13 +37,24 @@ public sealed class WebTerminal
}
///
- /// Initializes the xterm.js terminal in the browser.
+ /// Initializes the xterm.js terminal in the browser and reads actual dimensions.
///
public async Task InitAsync()
{
await _js.InvokeVoidAsync("terminalInterop.init");
+
+ // Read actual terminal dimensions after fitAddon has sized the terminal
+ try
+ {
+ var dims = await _js.InvokeAsync("terminalInterop.getDimensions");
+ if (dims.rows >= 30)
+ Height = dims.rows;
+ }
+ catch { /* fallback to 30 */ }
}
+ private record TerminalDimensions(int cols, int rows);
+
// ── Output ───────────────────────────────────────────────────────────
///
@@ -83,7 +94,7 @@ public sealed class WebTerminal
ColorSystem = ColorSystemSupport.TrueColor
});
console.Profile.Width = Width;
- console.Profile.Height = Height;
+ console.Profile.Height = _instance?.Height ?? 50;
console.Write(renderable);
return writer.ToString();
}
@@ -227,6 +238,7 @@ public sealed class WebTerminal
while (true)
{
+
int scrollOffset = 0;
if (selected >= pageSize)
scrollOffset = selected - pageSize + 1;
@@ -262,7 +274,6 @@ public sealed class WebTerminal
console.MarkupLine("[dim] ▼ ...[/]");
string rendered = writer.ToString().Replace("\n", "\r\n");
- // Count actual line breaks — Split('\n').Length overcounts by 1 due to trailing newline
int lineCount = rendered.Count(c => c == '\n');
await WriteAsync(rendered);
@@ -278,7 +289,6 @@ public sealed class WebTerminal
selected = (selected + 1) % options.Count;
break;
case ConsoleKey.Enter:
- // Clear the rendered selection before returning
await WriteAsync($"\x1b[{lineCount}A\x1b[J");
return selected;
case ConsoleKey.D1 or ConsoleKey.NumPad1: if (options.Count >= 1) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 0; } break;
@@ -292,7 +302,7 @@ public sealed class WebTerminal
case ConsoleKey.D9 or ConsoleKey.NumPad9: if (options.Count >= 9) { await WriteAsync($"\x1b[{lineCount}A\x1b[J"); return 8; } break;
}
- // Clear and re-render
+ // Clear menu before re-rendering
await WriteAsync($"\x1b[{lineCount}A\x1b[J");
}
}
diff --git a/src/OpenTheBox.Web/wwwroot/css/terminal.css b/src/OpenTheBox.Web/wwwroot/css/terminal.css
index 927c4c4..98a179d 100644
--- a/src/OpenTheBox.Web/wwwroot/css/terminal.css
+++ b/src/OpenTheBox.Web/wwwroot/css/terminal.css
@@ -31,8 +31,7 @@ body {
#terminal {
width: 100%;
height: 100%;
- max-width: 960px;
- max-height: 600px;
+ max-width: 1200px;
}
/* Loading screen */
diff --git a/src/OpenTheBox.Web/wwwroot/js/terminal-interop.js b/src/OpenTheBox.Web/wwwroot/js/terminal-interop.js
index 214f866..ea17324 100644
--- a/src/OpenTheBox.Web/wwwroot/js/terminal-interop.js
+++ b/src/OpenTheBox.Web/wwwroot/js/terminal-interop.js
@@ -24,12 +24,17 @@ window.terminalInterop = {
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
+ // Show terminal container BEFORE opening so fitAddon can measure real dimensions
+ document.getElementById('loading').classList.add('hidden');
+ document.getElementById('terminal-container').classList.add('active');
+
term.open(document.getElementById('terminal'));
- // Fit to container, but enforce minimum dimensions for game readability
+ // Fit to container, enforce minimum dimensions for game readability
fitAddon.fit();
- if (term.cols < 120 || term.rows < 30) {
- term.resize(Math.max(term.cols, 120), Math.max(term.rows, 30));
+ const minCols = 120, minRows = 30;
+ if (term.cols < minCols || term.rows < minRows) {
+ term.resize(Math.max(term.cols, minCols), Math.max(term.rows, minRows));
}
// Forward key input to C#
@@ -40,18 +45,13 @@ window.terminalInterop = {
// Handle resize
window.addEventListener('resize', function () {
fitAddon.fit();
- if (term.cols < 120 || term.rows < 30) {
- term.resize(Math.max(term.cols, 120), Math.max(term.rows, 30));
+ if (term.cols < minCols || term.rows < minRows) {
+ term.resize(Math.max(term.cols, minCols), Math.max(term.rows, minRows));
}
});
this.term = term;
this.fitAddon = fitAddon;
-
- // Show terminal, hide loading
- document.getElementById('loading').classList.add('hidden');
- document.getElementById('terminal-container').classList.add('active');
-
term.focus();
},
@@ -72,5 +72,12 @@ window.terminalInterop = {
if (this.term) {
this.term.focus();
}
+ },
+
+ getDimensions: function () {
+ if (this.term) {
+ return { cols: this.term.cols, rows: this.term.rows };
+ }
+ return { cols: 120, rows: 30 };
}
};