Skip to content

Commit 105ca28

Browse files
authored
fix: fixed options not being passed to wasm
1 parent 853bc65 commit 105ca28

File tree

5 files changed

+204
-9
lines changed

5 files changed

+204
-9
lines changed

demo/bin/demo.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ const HTML_TEMPLATE = `<!doctype html>
215215
rows: 24,
216216
fontFamily: 'JetBrains Mono, Menlo, Monaco, monospace',
217217
fontSize: 14,
218+
theme: {
219+
background: '#1e1e1e',
220+
foreground: '#d4d4d4',
221+
},
218222
});
219223
220224
const fitAddon = new FitAddon();

lib/ghostty.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import {
99
CellFlags,
1010
type Cursor,
11+
GHOSTTY_CONFIG_SIZE,
1112
type GhosttyCell,
13+
type GhosttyTerminalConfig,
1214
type GhosttyWasmExports,
1315
KeyEncoderOption,
1416
type KeyEvent,
@@ -49,8 +51,12 @@ export class Ghostty {
4951
/**
5052
* Create a terminal emulator instance
5153
*/
52-
createTerminal(cols: number = 80, rows: number = 24): GhosttyTerminal {
53-
return new GhosttyTerminal(this.exports, this.memory, cols, rows);
54+
createTerminal(
55+
cols: number = 80,
56+
rows: number = 24,
57+
config?: GhosttyTerminalConfig
58+
): GhosttyTerminal {
59+
return new GhosttyTerminal(this.exports, this.memory, cols, rows, config);
5460
}
5561

5662
/**
@@ -286,20 +292,67 @@ export class GhosttyTerminal {
286292
* @param memory WASM memory
287293
* @param cols Number of columns (default: 80)
288294
* @param rows Number of rows (default: 24)
295+
* @param config Optional terminal configuration (colors, scrollback)
289296
* @throws Error if allocation fails
290297
*/
291298
constructor(
292299
exports: GhosttyWasmExports,
293300
memory: WebAssembly.Memory,
294301
cols: number = 80,
295-
rows: number = 24
302+
rows: number = 24,
303+
config?: GhosttyTerminalConfig
296304
) {
297305
this.exports = exports;
298306
this.memory = memory;
299307
this._cols = cols;
300308
this._rows = rows;
301309

302-
const handle = this.exports.ghostty_terminal_new(cols, rows);
310+
let handle: TerminalHandle;
311+
312+
if (config) {
313+
// Allocate config struct in WASM memory
314+
const configPtr = this.exports.ghostty_wasm_alloc_u8_array(GHOSTTY_CONFIG_SIZE);
315+
if (configPtr === 0) {
316+
throw new Error('Failed to allocate config (out of memory)');
317+
}
318+
319+
try {
320+
// Write config to WASM memory
321+
const view = new DataView(this.memory.buffer);
322+
let offset = configPtr;
323+
324+
// scrollback_limit (u32)
325+
view.setUint32(offset, config.scrollbackLimit ?? 10000, true);
326+
offset += 4;
327+
328+
// fg_color (u32)
329+
view.setUint32(offset, config.fgColor ?? 0, true);
330+
offset += 4;
331+
332+
// bg_color (u32)
333+
view.setUint32(offset, config.bgColor ?? 0, true);
334+
offset += 4;
335+
336+
// cursor_color (u32)
337+
view.setUint32(offset, config.cursorColor ?? 0, true);
338+
offset += 4;
339+
340+
// palette[16] (u32 * 16)
341+
for (let i = 0; i < 16; i++) {
342+
const color = config.palette?.[i] ?? 0;
343+
view.setUint32(offset, color, true);
344+
offset += 4;
345+
}
346+
347+
handle = this.exports.ghostty_terminal_new_with_config(cols, rows, configPtr);
348+
} finally {
349+
// Free config memory
350+
this.exports.ghostty_wasm_free_u8_array(configPtr, GHOSTTY_CONFIG_SIZE);
351+
}
352+
} else {
353+
handle = this.exports.ghostty_terminal_new(cols, rows);
354+
}
355+
303356
if (handle === 0) {
304357
throw new Error('Failed to allocate terminal (out of memory)');
305358
}

lib/terminal.ts

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { OSC8LinkProvider } from './providers/osc8-link-provider';
3636
import { UrlRegexProvider } from './providers/url-regex-provider';
3737
import { CanvasRenderer } from './renderer';
3838
import { SelectionManager } from './selection-manager';
39+
import type { GhosttyTerminalConfig } from './types';
3940
import type { ILink, ILinkProvider } from './types';
4041

4142
// ============================================================================
@@ -174,6 +175,82 @@ export class Terminal implements ITerminalCore {
174175
this.buffer = new BufferNamespace(this);
175176
}
176177

178+
// ==========================================================================
179+
// Theme to WASM Config Conversion
180+
// ==========================================================================
181+
182+
/**
183+
* Parse a CSS color string to 0xRRGGBB format.
184+
* Returns 0 if the color is undefined or invalid.
185+
*/
186+
private parseColorToHex(color?: string): number {
187+
if (!color) return 0;
188+
189+
// Handle hex colors (#RGB, #RRGGBB)
190+
if (color.startsWith('#')) {
191+
let hex = color.slice(1);
192+
if (hex.length === 3) {
193+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
194+
}
195+
const value = Number.parseInt(hex, 16);
196+
return Number.isNaN(value) ? 0 : value;
197+
}
198+
199+
// Handle rgb(r, g, b) format
200+
const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
201+
if (match) {
202+
const r = Number.parseInt(match[1], 10);
203+
const g = Number.parseInt(match[2], 10);
204+
const b = Number.parseInt(match[3], 10);
205+
return (r << 16) | (g << 8) | b;
206+
}
207+
208+
return 0;
209+
}
210+
211+
/**
212+
* Convert terminal options to WASM terminal config.
213+
*/
214+
private buildWasmConfig(): GhosttyTerminalConfig | undefined {
215+
const theme = this.options.theme;
216+
const scrollback = this.options.scrollback;
217+
218+
// If no theme and default scrollback, use defaults
219+
if (!theme && scrollback === 1000) {
220+
return undefined;
221+
}
222+
223+
// Build palette array from theme colors
224+
// Order: black, red, green, yellow, blue, magenta, cyan, white,
225+
// brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite
226+
const palette: number[] = [
227+
this.parseColorToHex(theme?.black),
228+
this.parseColorToHex(theme?.red),
229+
this.parseColorToHex(theme?.green),
230+
this.parseColorToHex(theme?.yellow),
231+
this.parseColorToHex(theme?.blue),
232+
this.parseColorToHex(theme?.magenta),
233+
this.parseColorToHex(theme?.cyan),
234+
this.parseColorToHex(theme?.white),
235+
this.parseColorToHex(theme?.brightBlack),
236+
this.parseColorToHex(theme?.brightRed),
237+
this.parseColorToHex(theme?.brightGreen),
238+
this.parseColorToHex(theme?.brightYellow),
239+
this.parseColorToHex(theme?.brightBlue),
240+
this.parseColorToHex(theme?.brightMagenta),
241+
this.parseColorToHex(theme?.brightCyan),
242+
this.parseColorToHex(theme?.brightWhite),
243+
];
244+
245+
return {
246+
scrollbackLimit: scrollback,
247+
fgColor: this.parseColorToHex(theme?.foreground),
248+
bgColor: this.parseColorToHex(theme?.background),
249+
cursorColor: this.parseColorToHex(theme?.cursor),
250+
palette,
251+
};
252+
}
253+
177254
// ==========================================================================
178255
// Option Change Handling (for mutable options)
179256
// ==========================================================================
@@ -248,8 +325,9 @@ export class Terminal implements ITerminalCore {
248325
parent.setAttribute('tabindex', '0');
249326
}
250327

251-
// Create WASM terminal with current dimensions
252-
this.wasmTerm = this.ghostty!.createTerminal(this.cols, this.rows);
328+
// Create WASM terminal with current dimensions and theme config
329+
const wasmConfig = this.buildWasmConfig();
330+
this.wasmTerm = this.ghostty!.createTerminal(this.cols, this.rows, wasmConfig);
253331

254332
// Create canvas element
255333
this.canvas = document.createElement('canvas');
@@ -549,7 +627,8 @@ export class Terminal implements ITerminalCore {
549627
if (this.wasmTerm) {
550628
this.wasmTerm.free();
551629
}
552-
this.wasmTerm = this.ghostty!.createTerminal(this.cols, this.rows);
630+
const wasmConfig = this.buildWasmConfig();
631+
this.wasmTerm = this.ghostty!.createTerminal(this.cols, this.rows, wasmConfig);
553632

554633
// Clear renderer
555634
this.renderer!.clear();

lib/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,25 @@ export interface GhosttyWasmExports extends WebAssembly.Exports {
387387
// Terminal Types
388388
// ============================================================================
389389

390+
/**
391+
* Terminal configuration for WASM.
392+
* All colors use 0xRRGGBB format. A value of 0 means "use default".
393+
*/
394+
export interface GhosttyTerminalConfig {
395+
scrollbackLimit?: number;
396+
fgColor?: number; // 0xRRGGBB
397+
bgColor?: number; // 0xRRGGBB
398+
cursorColor?: number; // 0xRRGGBB
399+
palette?: number[]; // 16 colors, 0xRRGGBB format
400+
}
401+
402+
/**
403+
* Size of GhosttyTerminalConfig struct in WASM memory (bytes).
404+
* Layout: scrollback_limit(u32) + fg_color(u32) + bg_color(u32) + cursor_color(u32) + palette[16](u32*16)
405+
* Total: 4 + 4 + 4 + 4 + 64 = 80 bytes
406+
*/
407+
export const GHOSTTY_CONFIG_SIZE = 80;
408+
390409
/**
391410
* Opaque terminal pointer (WASM memory address)
392411
*/

patches/ghostty-wasm-api.patch

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ new file mode 100644
3232
index 000000000..078a0b872
3333
--- /dev/null
3434
+++ b/include/ghostty/vt/terminal.h
35-
@@ -0,0 +1,486 @@
35+
@@ -0,0 +1,499 @@
3636
+/**
3737
+ * @file terminal.h
3838
+ *
@@ -112,6 +112,7 @@ index 000000000..078a0b872
112112
+ * Terminal configuration options.
113113
+ *
114114
+ * Used when creating a new terminal to specify behavior and limits.
115+
+ * All colors use 0xRRGGBB format. A value of 0 means "use default".
115116
+ */
116117
+typedef struct {
117118
+ /**
@@ -131,6 +132,18 @@ index 000000000..078a0b872
131132
+ * Initial background color (RGB, 0xRRGGBB format, 0 = use default).
132133
+ */
133134
+ uint32_t bg_color;
135+
+
136+
+ /**
137+
+ * Cursor color (RGB, 0xRRGGBB format, 0 = use default).
138+
+ */
139+
+ uint32_t cursor_color;
140+
+
141+
+ /**
142+
+ * ANSI color palette (16 colors, 0xRRGGBB format, 0 = use default).
143+
+ * Index 0-7: Normal colors (black, red, green, yellow, blue, magenta, cyan, white)
144+
+ * Index 8-15: Bright colors (same order)
145+
+ */
146+
+ uint32_t palette[16];
134147
+} GhosttyTerminalConfig;
135148
+
136149
+/**
@@ -609,7 +622,7 @@ new file mode 100644
609622
index 000000000..e79702488
610623
--- /dev/null
611624
+++ b/src/terminal/c/terminal.zig
612-
@@ -0,0 +1,611 @@
625+
@@ -0,0 +1,638 @@
613626
+//! C API wrapper for Terminal
614627
+//!
615628
+//! This provides a C-compatible interface to Ghostty's Terminal for WASM export.
@@ -655,6 +668,8 @@ index 000000000..e79702488
655668
+ scrollback_limit: u32,
656669
+ fg_color: u32,
657670
+ bg_color: u32,
671+
+ cursor_color: u32,
672+
+ palette: [16]u32,
658673
+ };
659674
+};
660675
+
@@ -679,6 +694,8 @@ index 000000000..e79702488
679694
+ scrollback_limit: u32,
680695
+ fg_color: u32,
681696
+ bg_color: u32,
697+
+ cursor_color: u32,
698+
+ palette: [16]u32,
682699
+};
683700
+
684701
+// ============================================================================
@@ -705,10 +722,14 @@ index 000000000..e79702488
705722
+ .scrollback_limit = cfg.scrollback_limit,
706723
+ .fg_color = cfg.fg_color,
707724
+ .bg_color = cfg.bg_color,
725+
+ .cursor_color = cfg.cursor_color,
726+
+ .palette = cfg.palette,
708727
+ } else .{
709728
+ .scrollback_limit = 10_000,
710729
+ .fg_color = 0,
711730
+ .bg_color = 0,
731+
+ .cursor_color = 0,
732+
+ .palette = [_]u32{0} ** 16,
712733
+ };
713734
+
714735
+ // Allocate wrapper
@@ -735,6 +756,25 @@ index 000000000..e79702488
735756
+ };
736757
+ colors.background = color.DynamicRGB.init(rgb);
737758
+ }
759+
+ if (config.cursor_color != 0) {
760+
+ const rgb = color.RGB{
761+
+ .r = @truncate((config.cursor_color >> 16) & 0xFF),
762+
+ .g = @truncate((config.cursor_color >> 8) & 0xFF),
763+
+ .b = @truncate(config.cursor_color & 0xFF),
764+
+ };
765+
+ colors.cursor = color.DynamicRGB.init(rgb);
766+
+ }
767+
+ // Apply palette colors (0 = use default, non-zero = override)
768+
+ for (config.palette, 0..) |palette_color, i| {
769+
+ if (palette_color != 0) {
770+
+ const rgb = color.RGB{
771+
+ .r = @truncate((palette_color >> 16) & 0xFF),
772+
+ .g = @truncate((palette_color >> 8) & 0xFF),
773+
+ .b = @truncate(palette_color & 0xFF),
774+
+ };
775+
+ colors.palette.set(@intCast(i), rgb);
776+
+ }
777+
+ }
738778
+
739779
+ // Create terminal
740780
+ const terminal = Terminal.init(

0 commit comments

Comments
 (0)