prepare web build

This commit is contained in:
Samuel Bouchet 2026-03-18 18:03:52 +01:00
parent fcd270861c
commit d635ab673c
6 changed files with 44 additions and 45 deletions

View file

@ -35,6 +35,18 @@ On Windows, use the launcher for best UTF-8 support:
OpenTheBox.cmd
```
### Web Build (itch.io)
```
dotnet run --project src/OpenTheBox.Web # Dev local (http://localhost:5000)
dotnet publish src/OpenTheBox.Web -c Release -o builds/web # Publication
```
Upload `builds/web/wwwroot/` sur itch.io en HTML5.
Running locally:
```
npx serve builds/web/wwwroot/
```
## Test
```
dotnet test

View file

@ -592,7 +592,7 @@ public sealed class WebGameHost
ColorSystem = ColorSystemSupport.TrueColor
});
bufferConsole.Profile.Width = WebTerminal.Width;
bufferConsole.Profile.Height = _terminal.Height;
bufferConsole.Profile.Height = WebTerminal.Height;
bufferConsole.Write(InventoryPanel.Render(_state, _registry, _loc, scrollOffset, selectedIndex: selectedIndex));

View file

@ -28,7 +28,7 @@ public sealed class WebTerminal
});
public const int Width = 120;
public int Height { get; private set; } = 30;
public const int Height = 30;
public WebTerminal(IJSRuntime js)
{
@ -37,23 +37,12 @@ public sealed class WebTerminal
}
/// <summary>
/// Initializes the xterm.js terminal in the browser and reads actual dimensions.
/// Initializes the xterm.js terminal in the browser (fixed 120x30).
/// </summary>
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<TerminalDimensions>("terminalInterop.getDimensions");
if (dims.rows >= 30)
Height = dims.rows;
}
catch { /* fallback to 30 */ }
}
private record TerminalDimensions(int cols, int rows);
// ── Output ───────────────────────────────────────────────────────────
@ -94,7 +83,7 @@ public sealed class WebTerminal
ColorSystem = ColorSystemSupport.TrueColor
});
console.Profile.Width = Width;
console.Profile.Height = _instance?.Height ?? 50;
console.Profile.Height = Height;
console.Write(renderable);
return writer.ToString();
}

View file

@ -21,17 +21,16 @@ body {
align-items: center;
width: 100%;
height: 100%;
padding: 8px;
}
#terminal-container.active {
display: flex;
}
/* Let xterm.js determine the natural size from 120x30 @ fontSize 16.
CSS transform scale (set by JS) shrinks it to fit smaller viewports. */
#terminal {
width: 100%;
height: 100%;
max-width: 1200px;
flex-shrink: 0;
}
/* Loading screen */

View file

@ -20,7 +20,6 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.min.js"></script>
<script src="js/terminal-interop.js"></script>

View file

@ -1,14 +1,13 @@
// Terminal interop bridge: xterm.js <-> .NET Blazor WASM
window.terminalInterop = {
term: null,
fitAddon: null,
init: function () {
const term = new Terminal({
cols: 120,
rows: 30,
fontFamily: "'Cascadia Mono', 'Consolas', 'Courier New', monospace",
fontSize: 14,
fontSize: 16,
theme: {
background: '#1a1a2e',
foreground: '#e0e0e0',
@ -18,43 +17,47 @@ window.terminalInterop = {
},
cursorBlink: true,
allowProposedApi: true,
scrollback: 1000
scrollback: 0
});
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
// Show terminal container BEFORE opening so fitAddon can measure real dimensions
// Show terminal container BEFORE opening so xterm can measure
document.getElementById('loading').classList.add('hidden');
document.getElementById('terminal-container').classList.add('active');
term.open(document.getElementById('terminal'));
// Fit to container, enforce minimum dimensions for game readability
fitAddon.fit();
const minCols = 120, minRows = 30;
if (term.cols < minCols || term.rows < minRows) {
term.resize(Math.max(term.cols, minCols), Math.max(term.rows, minRows));
}
// Scale the terminal to fit the viewport while keeping exact 120x30
this._scaleToFit();
window.addEventListener('resize', () => this._scaleToFit());
// Forward key input to C#
term.onData(function (data) {
DotNet.invokeMethodAsync('OpenTheBox.Web', 'OnTerminalInput', data);
});
// Handle resize
window.addEventListener('resize', function () {
fitAddon.fit();
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;
term.focus();
},
_scaleToFit: function () {
const el = document.querySelector('#terminal .xterm-screen');
if (!el) return;
const container = document.getElementById('terminal-container');
const pad = 16; // padding around terminal
const maxW = container.clientWidth - pad;
const maxH = container.clientHeight - pad;
const termW = el.offsetWidth;
const termH = el.offsetHeight;
if (termW === 0 || termH === 0) return;
const scale = Math.min(1, maxW / termW, maxH / termH);
const wrapper = document.getElementById('terminal');
wrapper.style.transform = scale < 1 ? `scale(${scale})` : '';
wrapper.style.transformOrigin = 'center center';
},
write: function (text) {
if (this.term) {
this.term.write(text);
@ -75,9 +78,6 @@ window.terminalInterop = {
},
getDimensions: function () {
if (this.term) {
return { cols: this.term.cols, rows: this.term.rows };
}
return { cols: 120, rows: 30 };
}
};