@xterm/xterm 6.1.0-beta.185 → 6.1.0-beta.187

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xterm/xterm",
3
3
  "description": "Full xterm terminal, in your browser",
4
- "version": "6.1.0-beta.185",
4
+ "version": "6.1.0-beta.187",
5
5
  "main": "lib/xterm.js",
6
6
  "module": "lib/xterm.mjs",
7
7
  "style": "css/xterm.css",
@@ -119,5 +119,5 @@
119
119
  "ws": "^8.2.3",
120
120
  "xterm-benchmark": "^0.3.1"
121
121
  },
122
- "commit": "89980fc565c178d13588e135f8eb975f44a47712"
122
+ "commit": "af6731b5337e33724a2c6f4dff47015362138b09"
123
123
  }
@@ -414,7 +414,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
414
414
  this._register(addDisposableListener(this.textarea!, 'keyup', (ev: KeyboardEvent) => this._keyUp(ev), true));
415
415
  this._register(addDisposableListener(this.textarea!, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true));
416
416
  this._register(addDisposableListener(this.textarea!, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true));
417
- this._register(addDisposableListener(this.textarea!, 'compositionstart', () => this._compositionHelper!.compositionstart()));
417
+ this._register(addDisposableListener(this.textarea!, 'compositionstart', () => {
418
+ // Ensure the textarea is synced to the latest cursor location before composition begins. This
419
+ // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively
420
+ // would cause the IME to appear in the wrong position. The theory is that when the IME is
421
+ // triggered during a partial render the textarea position becomes locked and will not move
422
+ // until it is hidden and a custom move occurs.
423
+ this._syncTextArea();
424
+ this._compositionHelper!.compositionstart();
425
+ this._compositionHelper!.updateCompositionElements();
426
+ }));
418
427
  this._register(addDisposableListener(this.textarea!, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper!.compositionupdate(e)));
419
428
  this._register(addDisposableListener(this.textarea!, 'compositionend', () => this._compositionHelper!.compositionend()));
420
429
  this._register(addDisposableListener(this.textarea!, 'input', (ev: InputEvent) => this._inputEvent(ev), true));
@@ -644,7 +653,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
644
653
  this._mouseService.bindMouse({
645
654
  element: this.element!,
646
655
  screenElement: this.screenElement!,
647
- document: this._document!
656
+ document: this._document!,
657
+ handleTouchScroll: amount => this._viewport?.handleTouchScroll(amount)
648
658
  }, disposable => this._register(disposable), () => this.focus());
649
659
  }
650
660
 
@@ -8,12 +8,11 @@ import { ViewportConstants } from 'browser/shared/Constants';
8
8
  import { Disposable, toDisposable } from 'common/Lifecycle';
9
9
  import { IBufferService, IMouseStateService, IOptionsService } from 'common/services/Services';
10
10
  import { CoreMouseEventType } from 'common/Types';
11
- import { addDisposableListener, scheduleAtNextAnimationFrame } from 'browser/Dom';
11
+ import { scheduleAtNextAnimationFrame } from 'browser/Dom';
12
12
  import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
13
13
  import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
14
14
  import { Emitter, EventUtils } from 'common/Event';
15
15
  import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
16
- import { Gesture, EventType as GestureEventType, type IGestureEvent } from 'browser/scrollable/touch';
17
16
 
18
17
  export class Viewport extends Disposable {
19
18
 
@@ -107,9 +106,6 @@ export class Viewport extends Disposable {
107
106
 
108
107
  this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
109
108
 
110
- // Touch/gesture scrolling support
111
- this._register(Gesture.addTarget(screenElement));
112
- this._register(addDisposableListener(screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleGestureChange(e)));
113
109
  }
114
110
 
115
111
  public scrollLines(disp: number): void {
@@ -204,12 +200,10 @@ export class Viewport extends Disposable {
204
200
  this._isHandlingScroll = false;
205
201
  }
206
202
 
207
- private _handleGestureChange(e: IGestureEvent): void {
208
- e.preventDefault();
209
- e.stopPropagation();
203
+ public handleTouchScroll(translationY: number): void {
210
204
  const pos = this._scrollableElement.getScrollPosition();
211
205
  this._scrollableElement.setScrollPosition({
212
- scrollTop: pos.scrollTop - e.translationY
206
+ scrollTop: pos.scrollTop - translationY
213
207
  });
214
208
  }
215
209
  }
@@ -153,6 +153,8 @@ export interface IGestureEvent extends MouseEvent {
153
153
  translationY: number;
154
154
  pageX: number;
155
155
  pageY: number;
156
+ clientX: number;
157
+ clientY: number;
156
158
  tapCount: number;
157
159
  }
158
160
 
@@ -459,6 +461,8 @@ export class Gesture extends Disposable {
459
461
  evt.translationY = touch.pageY - tail(data.rollingPageY)!;
460
462
  evt.pageX = touch.pageX;
461
463
  evt.pageY = touch.pageY;
464
+ evt.clientX = touch.clientX;
465
+ evt.clientY = touch.clientY;
462
466
  this._dispatchEvent(evt);
463
467
 
464
468
  if (data.rollingPageX.length > 3) {
@@ -9,6 +9,7 @@ import { CoreMouseAction, CoreMouseButton, CoreMouseEventType, ICoreMouseEvent,
9
9
  import { C0 } from 'common/data/EscapeSequences';
10
10
  import { toDisposable } from 'common/Lifecycle';
11
11
  import { ICoreBrowserService, IMouseCoordsService, IMouseService, IMouseServiceTarget, IRenderService, ISelectionService } from './Services';
12
+ import { Gesture, EventType as GestureEventType, IGestureEvent } from 'browser/scrollable/touch';
12
13
 
13
14
  type RequestedMouseEvents = Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener | null>;
14
15
 
@@ -23,6 +24,7 @@ export class MouseService implements IMouseService {
23
24
 
24
25
  private _lastEvent: ICoreMouseEvent | null = null;
25
26
  private _wheelPartialScroll: number = 0;
27
+ private _touchScrollAccumulator: number = 0;
26
28
 
27
29
  constructor(
28
30
  @IRenderService private readonly _renderService: IRenderService,
@@ -82,6 +84,9 @@ export class MouseService implements IMouseService {
82
84
  */
83
85
  register(addDisposableListener(element, 'mousedown', (ev: MouseEvent) => this._handleMouseDown(ctx, ev)));
84
86
  register(addDisposableListener(element, 'wheel', (ev: WheelEvent) => this._handlePassiveWheel(ctx, ev), { passive: false }));
87
+ register(Gesture.addTarget(target.screenElement));
88
+ register(addDisposableListener(target.screenElement, GestureEventType.START, () => this._handleTouchStart()));
89
+ register(addDisposableListener(target.screenElement, GestureEventType.CHANGE, (e: IGestureEvent) => this._handleTouchChange(ctx, e)));
85
90
  }
86
91
 
87
92
  private _sendEvent(ctx: IMouseBindContext, ev: MouseEvent | WheelEvent): boolean {
@@ -264,9 +269,88 @@ export class MouseService implements IMouseService {
264
269
  }
265
270
  }
266
271
 
272
+ private _handleTouchStart(): void {
273
+ this._touchScrollAccumulator = 0;
274
+ }
275
+
276
+ private _handleTouchChange(ctx: IMouseBindContext, e: IGestureEvent): void {
277
+ e.preventDefault();
278
+ e.stopPropagation();
279
+
280
+ // When mouse protocol has wheel events active, send as mouse wheel events.
281
+ if (ctx.requestedEvents.wheel) {
282
+ this._handleTouchScrollAsWheel(ctx, e);
283
+ return;
284
+ }
285
+
286
+ // When in alt buffer (no scrollback), send up/down key sequences.
287
+ if (!this._bufferService.buffer.hasScrollback) {
288
+ this._handleTouchScrollAsKeys(e);
289
+ return;
290
+ }
291
+
292
+ // Normal scrollback: delegate to viewport scrolling when available.
293
+ ctx.target.handleTouchScroll?.(e.translationY);
294
+ }
295
+
296
+ private _handleTouchScrollAsKeys(e: IGestureEvent): void {
297
+ const cellHeight = this._renderService?.dimensions.css.cell.height;
298
+ if (!cellHeight) {
299
+ return;
300
+ }
301
+
302
+ this._touchScrollAccumulator -= e.translationY;
303
+ const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
304
+ if (lines === 0) {
305
+ return;
306
+ }
307
+
308
+ this._touchScrollAccumulator -= lines * cellHeight;
309
+ const sequence = C0.ESC
310
+ + (this._coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[')
311
+ + (lines < 0 ? 'A' : 'B');
312
+ for (let i = 0; i < Math.abs(lines); i++) {
313
+ this._coreService.triggerDataEvent(sequence, true);
314
+ }
315
+ }
316
+
317
+ private _handleTouchScrollAsWheel(ctx: IMouseBindContext, e: IGestureEvent): void {
318
+ const cellHeight = this._renderService?.dimensions.css.cell.height;
319
+ if (!cellHeight) {
320
+ return;
321
+ }
322
+
323
+ this._touchScrollAccumulator -= e.translationY;
324
+ const lines = Math.trunc(this._touchScrollAccumulator / cellHeight);
325
+ if (lines === 0) {
326
+ return;
327
+ }
328
+
329
+ this._touchScrollAccumulator -= lines * cellHeight;
330
+ const pos = this._mouseCoordsService.getMouseReportCoords(e, ctx.target.screenElement);
331
+ if (!pos) {
332
+ return;
333
+ }
334
+
335
+ for (let i = 0; i < Math.abs(lines); i++) {
336
+ this._triggerMouseEvent({
337
+ col: pos.col,
338
+ row: pos.row,
339
+ x: pos.x,
340
+ y: pos.y,
341
+ button: CoreMouseButton.WHEEL,
342
+ action: lines < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN,
343
+ ctrl: false,
344
+ alt: false,
345
+ shift: false
346
+ });
347
+ }
348
+ }
349
+
267
350
  public reset(): void {
268
351
  this._lastEvent = null;
269
352
  this._wheelPartialScroll = 0;
353
+ this._touchScrollAccumulator = 0;
270
354
  }
271
355
 
272
356
  private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
@@ -68,6 +68,7 @@ export interface IMouseServiceTarget {
68
68
  element: HTMLElement;
69
69
  screenElement: HTMLElement;
70
70
  document: Document;
71
+ handleTouchScroll?(amount: number): void;
71
72
  }
72
73
 
73
74
  export const IRenderService = createDecorator<IRenderService>('RenderService');
@@ -6,4 +6,4 @@
6
6
  /**
7
7
  * The xterm.js version. This is updated by the publish script from package.json.
8
8
  */
9
- export const XTERM_VERSION = '6.1.0-beta.185';
9
+ export const XTERM_VERSION = '6.1.0-beta.187';