@aurigax-ai/ghostty-web 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/buffer.ts CHANGED
@@ -180,9 +180,7 @@ export class Buffer implements IBuffer {
180
180
  // So scrollbackOffset = y directly!
181
181
  const scrollbackOffset = y;
182
182
  cells = wasmTerm.getScrollbackLine(scrollbackOffset);
183
- // TODO: We'd need WASM API to check if scrollback line is wrapped
184
- // For now, assume not wrapped
185
- isWrapped = false;
183
+ isWrapped = wasmTerm.isScrollbackRowWrapped(scrollbackOffset);
186
184
  } else {
187
185
  // Accessing visible screen
188
186
  lineNumber = this.bufferType === 'normal' ? y - scrollbackLength : y;
package/lib/ghostty.ts CHANGED
@@ -930,9 +930,19 @@ export class GhosttyTerminal {
930
930
  }
931
931
 
932
932
  isRowWrapped(row: number): boolean {
933
+ return this.rowContinues(POINT_ACTIVE, row);
934
+ }
935
+
936
+ /** Whether a scrollback line continues the one before it; offset 0 is the oldest line. */
937
+ isScrollbackRowWrapped(offset: number): boolean {
938
+ if (offset < 0 || offset >= this.getScrollbackLength()) return false;
939
+ return this.rowContinues(POINT_HISTORY, offset);
940
+ }
941
+
942
+ private rowContinues(point: PointSpace, row: number): boolean {
933
943
  const abi = this.abi;
934
944
  return (
935
- this.gridRef(POINT_ACTIVE, 0, row, (ref) =>
945
+ this.gridRef(point, 0, row, (ref) =>
936
946
  abi.with(8, (rowPtr) => {
937
947
  if (abi.call('ghostty_grid_ref_row', ref, rowPtr) !== GHOSTTY_SUCCESS) return false;
938
948
  const raw = abi.view().getBigUint64(rowPtr, true);
@@ -1000,6 +1010,7 @@ export class GhosttyTerminal {
1000
1010
  configure(config: GhosttyTerminalConfig): void {
1001
1011
  const abi = this.abi;
1002
1012
  if (config.scrollbackLimit !== undefined) {
1013
+ this.setOption('SCROLLBACK_MAX_BYTES', 0);
1003
1014
  abi.with(4, (ptr) => {
1004
1015
  abi.view().setUint32(ptr, config.scrollbackLimit!, true);
1005
1016
  this.setOption('SCROLLBACK_MAX_LINES', ptr);
@@ -409,8 +409,9 @@ export class InputHandler {
409
409
  }
410
410
  }
411
411
 
412
- // Allow Ctrl+V and Cmd+V to trigger paste event (don't preventDefault)
413
- if ((event.ctrlKey || event.metaKey) && event.code === 'KeyV') {
412
+ // Cmd+V pastes through the browser's paste event; Ctrl+V reaches the
413
+ // program as ^V, as in xterm.js.
414
+ if (event.metaKey && event.code === 'KeyV') {
414
415
  // Let the browser's native paste event fire
415
416
  return;
416
417
  }
@@ -5,7 +5,6 @@
5
5
  * - Mouse drag selection
6
6
  * - Double-click word selection
7
7
  * - Text extraction from terminal buffer
8
- * - Automatic clipboard copy
9
8
  * - Visual selection highlighting (integrated into CanvasRenderer cell rendering)
10
9
  * - Auto-scroll during drag selection
11
10
  */
@@ -588,11 +587,7 @@ export class SelectionManager {
588
587
  }
589
588
 
590
589
  if (this.hasSelection()) {
591
- const text = this.getSelection();
592
- if (text) {
593
- this.copyToClipboard(text);
594
- this.selectionChangedEmitter.fire();
595
- }
590
+ if (this.getSelection()) this.selectionChangedEmitter.fire();
596
591
  }
597
592
  }
598
593
  };
@@ -613,11 +608,7 @@ export class SelectionManager {
613
608
  this.selectionEnd = { col: word.endCol, absoluteRow };
614
609
  this.requestRender();
615
610
 
616
- const text = this.getSelection();
617
- if (text) {
618
- this.copyToClipboard(text);
619
- this.selectionChangedEmitter.fire();
620
- }
611
+ if (this.getSelection()) this.selectionChangedEmitter.fire();
621
612
  }
622
613
  } else if (e.detail >= 3) {
623
614
  // Triple-click (or more) - select line content (like native Ghostty)
@@ -654,11 +645,7 @@ export class SelectionManager {
654
645
  this.selectionEnd = { col: endCol, absoluteRow };
655
646
  this.requestRender();
656
647
 
657
- const text = this.getSelection();
658
- if (text) {
659
- this.copyToClipboard(text);
660
- this.selectionChangedEmitter.fire();
661
- }
648
+ if (this.getSelection()) this.selectionChangedEmitter.fire();
662
649
  }
663
650
  }
664
651
  });
package/lib/terminal.ts CHANGED
@@ -582,9 +582,6 @@ export class Terminal implements ITerminalCore {
582
582
 
583
583
  // Start render loop
584
584
  this.startRenderLoop();
585
-
586
- // Focus input (auto-focus so user can start typing immediately)
587
- this.focus();
588
585
  } catch (error) {
589
586
  // Clean up on error
590
587
  this.isOpen = false;
@@ -702,8 +699,9 @@ export class Terminal implements ITerminalCore {
702
699
 
703
700
  // Call callback if provided
704
701
  if (callback) {
705
- // Queue callback after next render
706
- requestAnimationFrame(callback);
702
+ // The data is parsed by now, so run the callback as soon as this task
703
+ // ends, as xterm.js does once a write is processed.
704
+ queueMicrotask(callback);
707
705
  }
708
706
 
709
707
  if (this.awaitingEcho) {
@@ -857,16 +855,7 @@ export class Terminal implements ITerminalCore {
857
855
  */
858
856
  focus(): void {
859
857
  this.wake();
860
- if (this.isOpen && this.element) {
861
- // Focus immediately for immediate keyboard/wheel event handling
862
- this.element.focus();
863
-
864
- // Also schedule a delayed focus as backup to ensure it sticks
865
- // (some browsers may need this if DOM isn't fully settled)
866
- setTimeout(() => {
867
- this.element?.focus();
868
- }, 0);
869
- }
858
+ if (this.isOpen && this.element) this.element.focus();
870
859
  }
871
860
 
872
861
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurigax-ai/ghostty-web",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Ghostty's terminal engine (libghostty-vt) in the browser: WebAssembly build, an xterm.js-shaped API and WebGL and canvas renderers. A hard fork of coder/ghostty-web.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.ts",