@xterm/xterm 6.1.0-beta.98 → 6.1.0-beta.99
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/xterm.js +1 -1
- package/lib/xterm.js.map +1 -1
- package/lib/xterm.mjs +16 -16
- package/lib/xterm.mjs.map +4 -4
- package/package.json +2 -2
- package/src/browser/CoreBrowserTerminal.ts +17 -7
- package/src/browser/services/KeyboardService.ts +41 -0
- package/src/browser/services/Services.ts +9 -1
- package/src/common/InputHandler.ts +121 -3
- package/src/common/Types.ts +17 -0
- package/src/common/Version.ts +1 -1
- package/src/common/input/KittyKeyboard.ts +513 -0
- package/src/common/services/CoreService.ts +12 -1
- package/src/common/services/OptionsService.ts +2 -1
- package/src/common/services/Services.ts +8 -1
- package/typings/xterm.d.ts +35 -6
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.
|
|
4
|
+
"version": "6.1.0-beta.99",
|
|
5
5
|
"main": "lib/xterm.js",
|
|
6
6
|
"module": "lib/xterm.mjs",
|
|
7
7
|
"style": "css/xterm.css",
|
|
@@ -116,5 +116,5 @@
|
|
|
116
116
|
"ws": "^8.2.3",
|
|
117
117
|
"xterm-benchmark": "^0.3.1"
|
|
118
118
|
},
|
|
119
|
-
"commit": "
|
|
119
|
+
"commit": "91c4761b8229e76f420e728b5f6ace1976bd42ec"
|
|
120
120
|
}
|
|
@@ -39,8 +39,9 @@ import { LinkProviderService } from 'browser/services/LinkProviderService';
|
|
|
39
39
|
import { MouseService } from 'browser/services/MouseService';
|
|
40
40
|
import { RenderService } from 'browser/services/RenderService';
|
|
41
41
|
import { SelectionService } from 'browser/services/SelectionService';
|
|
42
|
-
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, ILinkProviderService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
|
42
|
+
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
|
43
43
|
import { ThemeService } from 'browser/services/ThemeService';
|
|
44
|
+
import { KeyboardService } from 'browser/services/KeyboardService';
|
|
44
45
|
import { channels, color } from 'common/Color';
|
|
45
46
|
import { CoreTerminal } from 'common/CoreTerminal';
|
|
46
47
|
import * as Browser from 'common/Platform';
|
|
@@ -48,7 +49,6 @@ import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType,
|
|
|
48
49
|
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
|
49
50
|
import { IBuffer } from 'common/buffer/Types';
|
|
50
51
|
import { C0, C1_ESCAPED } from 'common/data/EscapeSequences';
|
|
51
|
-
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
|
52
52
|
import { toRgbString } from 'common/input/XParseColor';
|
|
53
53
|
import { DecorationService } from 'common/services/DecorationService';
|
|
54
54
|
import { IDecorationService } from 'common/services/Services';
|
|
@@ -80,8 +80,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
80
80
|
private _customWheelEventHandler: CustomWheelEventHandler | undefined;
|
|
81
81
|
|
|
82
82
|
// Browser services
|
|
83
|
-
private _decorationService: DecorationService;
|
|
84
|
-
private
|
|
83
|
+
private readonly _decorationService: DecorationService;
|
|
84
|
+
private readonly _keyboardService: IKeyboardService;
|
|
85
|
+
private readonly _linkProviderService: ILinkProviderService;
|
|
85
86
|
|
|
86
87
|
// Optional browser services
|
|
87
88
|
private _charSizeService: ICharSizeService | undefined;
|
|
@@ -173,6 +174,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
173
174
|
|
|
174
175
|
this._decorationService = this._instantiationService.createInstance(DecorationService);
|
|
175
176
|
this._instantiationService.setService(IDecorationService, this._decorationService);
|
|
177
|
+
this._keyboardService = this._instantiationService.createInstance(KeyboardService);
|
|
178
|
+
this._instantiationService.setService(IKeyboardService, this._keyboardService);
|
|
176
179
|
this._linkProviderService = this._instantiationService.createInstance(LinkProviderService);
|
|
177
180
|
this._instantiationService.setService(ILinkProviderService, this._linkProviderService);
|
|
178
181
|
this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(OscLinkProvider));
|
|
@@ -1081,7 +1084,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
1081
1084
|
this._unprocessedDeadKey = true;
|
|
1082
1085
|
}
|
|
1083
1086
|
|
|
1084
|
-
const result =
|
|
1087
|
+
const result = this._keyboardService.evaluateKeyDown(event);
|
|
1085
1088
|
|
|
1086
1089
|
this.updateCursorStyle(event);
|
|
1087
1090
|
|
|
@@ -1109,8 +1112,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
1109
1112
|
}
|
|
1110
1113
|
|
|
1111
1114
|
// HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case
|
|
1112
|
-
// letters cannot be input while caps lock is on.
|
|
1113
|
-
|
|
1115
|
+
// letters cannot be input while caps lock is on. Skip this hack when using kitty protocol
|
|
1116
|
+
// as it needs to send proper CSI u sequences for all key events.
|
|
1117
|
+
if (!this._keyboardService.useKitty && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
|
|
1114
1118
|
if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) {
|
|
1115
1119
|
return true;
|
|
1116
1120
|
}
|
|
@@ -1168,6 +1172,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|
|
1168
1172
|
this.focus();
|
|
1169
1173
|
}
|
|
1170
1174
|
|
|
1175
|
+
// Handle key release for Kitty keyboard protocol
|
|
1176
|
+
const result = this._keyboardService.evaluateKeyUp(ev);
|
|
1177
|
+
if (result?.key) {
|
|
1178
|
+
this.coreService.triggerDataEvent(result.key, true);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1171
1181
|
this.updateCursorStyle(ev);
|
|
1172
1182
|
this._keyPressHandled = false;
|
|
1173
1183
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IKeyboardService } from 'browser/services/Services';
|
|
7
|
+
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
|
8
|
+
import { evaluateKeyboardEventKitty, KittyKeyboardEventType, KittyKeyboardFlags, shouldUseKittyProtocol } from 'common/input/KittyKeyboard';
|
|
9
|
+
import { isMac } from 'common/Platform';
|
|
10
|
+
import { ICoreService, IOptionsService } from 'common/services/Services';
|
|
11
|
+
import { IKeyboardResult } from 'common/Types';
|
|
12
|
+
|
|
13
|
+
export class KeyboardService implements IKeyboardService {
|
|
14
|
+
public serviceBrand: undefined;
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
@ICoreService private readonly _coreService: ICoreService,
|
|
18
|
+
@IOptionsService private readonly _optionsService: IOptionsService
|
|
19
|
+
) {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public evaluateKeyDown(event: KeyboardEvent): IKeyboardResult {
|
|
23
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
24
|
+
return this.useKitty
|
|
25
|
+
? evaluateKeyboardEventKitty(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS)
|
|
26
|
+
: evaluateKeyboardEvent(event, this._coreService.decPrivateModes.applicationCursorKeys, isMac, this._optionsService.rawOptions.macOptionIsMeta);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined {
|
|
30
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
31
|
+
if (this.useKitty && (kittyFlags & KittyKeyboardFlags.REPORT_EVENT_TYPES)) {
|
|
32
|
+
return evaluateKeyboardEventKitty(event, kittyFlags, KittyKeyboardEventType.RELEASE);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get useKitty(): boolean {
|
|
38
|
+
const kittyFlags = this._coreService.kittyKeyboard.flags;
|
|
39
|
+
return !!(this._optionsService.rawOptions.vtExtensions?.kittyKeyboard && shouldUseKittyProtocol(kittyFlags));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -7,7 +7,7 @@ import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
|
|
|
7
7
|
import { IColorSet, ILink, ReadonlyColorSet } from 'browser/Types';
|
|
8
8
|
import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
|
|
9
9
|
import { createDecorator } from 'common/services/ServiceRegistry';
|
|
10
|
-
import { AllColorIndex, IDisposable } from 'common/Types';
|
|
10
|
+
import { AllColorIndex, IDisposable, IKeyboardResult } from 'common/Types';
|
|
11
11
|
import type { Event } from 'vs/base/common/event';
|
|
12
12
|
|
|
13
13
|
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
|
|
@@ -156,3 +156,11 @@ export interface ILinkProviderService extends IDisposable {
|
|
|
156
156
|
export interface ILinkProvider {
|
|
157
157
|
provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void;
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
export const IKeyboardService = createDecorator<IKeyboardService>('KeyboardService');
|
|
161
|
+
export interface IKeyboardService {
|
|
162
|
+
serviceBrand: undefined;
|
|
163
|
+
evaluateKeyDown(event: KeyboardEvent): IKeyboardResult;
|
|
164
|
+
evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined;
|
|
165
|
+
readonly useKitty: boolean;
|
|
166
|
+
}
|
|
@@ -270,6 +270,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
270
270
|
this._parser.registerCsiHandler({ intermediates: '$', final: 'p' }, params => this.requestMode(params, true));
|
|
271
271
|
this._parser.registerCsiHandler({ prefix: '?', intermediates: '$', final: 'p' }, params => this.requestMode(params, false));
|
|
272
272
|
|
|
273
|
+
// Kitty keyboard protocol handlers
|
|
274
|
+
this._parser.registerCsiHandler({ prefix: '=', final: 'u' }, params => this.kittyKeyboardSet(params));
|
|
275
|
+
this._parser.registerCsiHandler({ prefix: '?', final: 'u' }, params => this.kittyKeyboardQuery(params));
|
|
276
|
+
this._parser.registerCsiHandler({ prefix: '>', final: 'u' }, params => this.kittyKeyboardPush(params));
|
|
277
|
+
this._parser.registerCsiHandler({ prefix: '<', final: 'u' }, params => this.kittyKeyboardPop(params));
|
|
278
|
+
|
|
273
279
|
/**
|
|
274
280
|
* execute handler
|
|
275
281
|
*/
|
|
@@ -2003,6 +2009,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
2003
2009
|
// FALL-THROUGH
|
|
2004
2010
|
case 47: // alt screen buffer
|
|
2005
2011
|
case 1047: // alt screen buffer
|
|
2012
|
+
// Swap kitty keyboard flags: save main, restore alt
|
|
2013
|
+
if (this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
2014
|
+
const state = this._coreService.kittyKeyboard;
|
|
2015
|
+
state.mainFlags = state.flags;
|
|
2016
|
+
state.flags = state.altFlags;
|
|
2017
|
+
}
|
|
2006
2018
|
this._bufferService.buffers.activateAltBuffer(this._eraseAttrData());
|
|
2007
2019
|
this._coreService.isCursorInitialized = true;
|
|
2008
2020
|
this._onRequestRefreshRows.fire(undefined);
|
|
@@ -2232,6 +2244,12 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
2232
2244
|
// FALL-THROUGH
|
|
2233
2245
|
case 47: // normal screen buffer
|
|
2234
2246
|
case 1047: // normal screen buffer - clearing it first
|
|
2247
|
+
// Swap kitty keyboard flags: save alt, restore main
|
|
2248
|
+
if (this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
2249
|
+
const state = this._coreService.kittyKeyboard;
|
|
2250
|
+
state.altFlags = state.flags;
|
|
2251
|
+
state.flags = state.mainFlags;
|
|
2252
|
+
}
|
|
2235
2253
|
// Ensure the selection manager has the correct buffer
|
|
2236
2254
|
this._bufferService.buffers.activateNormalBuffer();
|
|
2237
2255
|
if (params.params[i] === 1049) {
|
|
@@ -2654,10 +2672,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
2654
2672
|
} else if (p === 55) {
|
|
2655
2673
|
// not overline
|
|
2656
2674
|
attr.bg &= ~BgFlags.OVERLINE;
|
|
2657
|
-
} else if (p === 221) {
|
|
2675
|
+
} else if (p === 221 && (this._optionsService.rawOptions.vtExtensions?.kittySgrBoldFaintControl ?? true)) {
|
|
2658
2676
|
// not bold (kitty extension)
|
|
2659
2677
|
attr.fg &= ~FgFlags.BOLD;
|
|
2660
|
-
} else if (p === 222) {
|
|
2678
|
+
} else if (p === 222 && (this._optionsService.rawOptions.vtExtensions?.kittySgrBoldFaintControl ?? true)) {
|
|
2661
2679
|
// not faint (kitty extension)
|
|
2662
2680
|
attr.bg &= ~BgFlags.DIM;
|
|
2663
2681
|
} else if (p === 59) {
|
|
@@ -2984,7 +3002,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
2984
3002
|
return true;
|
|
2985
3003
|
}
|
|
2986
3004
|
|
|
2987
|
-
|
|
2988
3005
|
/**
|
|
2989
3006
|
* OSC 2; <data> ST (set window title)
|
|
2990
3007
|
* Proxy to set window title.
|
|
@@ -3496,6 +3513,107 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|
|
3496
3513
|
public markRangeDirty(y1: number, y2: number): void {
|
|
3497
3514
|
this._dirtyRowTracker.markRangeDirty(y1, y2);
|
|
3498
3515
|
}
|
|
3516
|
+
|
|
3517
|
+
// #region Kitty keyboard
|
|
3518
|
+
|
|
3519
|
+
/**
|
|
3520
|
+
* CSI = flags ; mode u
|
|
3521
|
+
* Set Kitty keyboard protocol flags.
|
|
3522
|
+
* mode: 1=set, 2=set-only-specified, 3=reset-only-specified
|
|
3523
|
+
*
|
|
3524
|
+
* @vt: #Y CSI KKBDSET "Kitty Keyboard Set" "CSI = Ps ; Pm u" "Set Kitty keyboard protocol flags."
|
|
3525
|
+
*/
|
|
3526
|
+
public kittyKeyboardSet(params: IParams): boolean {
|
|
3527
|
+
if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
3528
|
+
return true;
|
|
3529
|
+
}
|
|
3530
|
+
const flags = params.params[0] || 0;
|
|
3531
|
+
const mode = params.params[1] || 1;
|
|
3532
|
+
const state = this._coreService.kittyKeyboard;
|
|
3533
|
+
|
|
3534
|
+
switch (mode) {
|
|
3535
|
+
case 1: // Set all flags
|
|
3536
|
+
state.flags = flags;
|
|
3537
|
+
break;
|
|
3538
|
+
case 2: // Set only specified flags (OR)
|
|
3539
|
+
state.flags |= flags;
|
|
3540
|
+
break;
|
|
3541
|
+
case 3: // Reset only specified flags (AND NOT)
|
|
3542
|
+
state.flags &= ~flags;
|
|
3543
|
+
break;
|
|
3544
|
+
}
|
|
3545
|
+
return true;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* CSI ? u
|
|
3550
|
+
* Query Kitty keyboard protocol flags.
|
|
3551
|
+
* Terminal responds with CSI ? flags u
|
|
3552
|
+
*
|
|
3553
|
+
* @vt: #Y CSI KKBDQUERY "Kitty Keyboard Query" "CSI ? u" "Query Kitty keyboard protocol flags."
|
|
3554
|
+
*/
|
|
3555
|
+
public kittyKeyboardQuery(params: IParams): boolean {
|
|
3556
|
+
if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
3557
|
+
return true;
|
|
3558
|
+
}
|
|
3559
|
+
const flags = this._coreService.kittyKeyboard.flags;
|
|
3560
|
+
this._coreService.triggerDataEvent(`${C0.ESC}[?${flags}u`);
|
|
3561
|
+
return true;
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
/**
|
|
3565
|
+
* CSI > flags u
|
|
3566
|
+
* Push Kitty keyboard flags onto stack and set new flags.
|
|
3567
|
+
*
|
|
3568
|
+
* @vt: #Y CSI KKBDPUSH "Kitty Keyboard Push" "CSI > Ps u" "Push keyboard flags to stack and set new flags."
|
|
3569
|
+
*/
|
|
3570
|
+
public kittyKeyboardPush(params: IParams): boolean {
|
|
3571
|
+
if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
3572
|
+
return true;
|
|
3573
|
+
}
|
|
3574
|
+
const flags = params.params[0] || 0;
|
|
3575
|
+
const state = this._coreService.kittyKeyboard;
|
|
3576
|
+
const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt;
|
|
3577
|
+
const stack = isAlt ? state.altStack : state.mainStack;
|
|
3578
|
+
|
|
3579
|
+
// Evict oldest entry if stack is full (DoS protection, limit of 16)
|
|
3580
|
+
if (stack.length >= 16) {
|
|
3581
|
+
stack.shift();
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
// Push current flags onto stack and set new flags
|
|
3585
|
+
stack.push(state.flags);
|
|
3586
|
+
state.flags = flags;
|
|
3587
|
+
return true;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
/**
|
|
3591
|
+
* CSI < count u
|
|
3592
|
+
* Pop Kitty keyboard flags from stack.
|
|
3593
|
+
*
|
|
3594
|
+
* @vt: #Y CSI KKBDPOP "Kitty Keyboard Pop" "CSI < Ps u" "Pop keyboard flags from stack."
|
|
3595
|
+
*/
|
|
3596
|
+
public kittyKeyboardPop(params: IParams): boolean {
|
|
3597
|
+
if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) {
|
|
3598
|
+
return true;
|
|
3599
|
+
}
|
|
3600
|
+
const count = Math.max(1, params.params[0] || 1);
|
|
3601
|
+
const state = this._coreService.kittyKeyboard;
|
|
3602
|
+
const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt;
|
|
3603
|
+
const stack = isAlt ? state.altStack : state.mainStack;
|
|
3604
|
+
|
|
3605
|
+
// Pop specified number of entries from stack
|
|
3606
|
+
for (let i = 0; i < count && stack.length > 0; i++) {
|
|
3607
|
+
state.flags = stack.pop()!;
|
|
3608
|
+
}
|
|
3609
|
+
// If stack is empty after popping, reset to 0
|
|
3610
|
+
if (stack.length === 0 && count > 0) {
|
|
3611
|
+
state.flags = 0;
|
|
3612
|
+
}
|
|
3613
|
+
return true;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// #endregion
|
|
3499
3617
|
}
|
|
3500
3618
|
|
|
3501
3619
|
export interface IDirtyRowTracker {
|
package/src/common/Types.ts
CHANGED
|
@@ -277,6 +277,23 @@ export interface IDecPrivateModes {
|
|
|
277
277
|
wraparound: boolean; // defaults: xterm - true, vt100 - false
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Kitty keyboard protocol state.
|
|
282
|
+
* Maintains per-screen stacks of enhancement flags.
|
|
283
|
+
*/
|
|
284
|
+
export interface IKittyKeyboardState {
|
|
285
|
+
/** Current active enhancement flags (for current screen) */
|
|
286
|
+
flags: number;
|
|
287
|
+
/** Saved flags for main screen when alt is active */
|
|
288
|
+
mainFlags: number;
|
|
289
|
+
/** Saved flags for alternate screen when main is active */
|
|
290
|
+
altFlags: number;
|
|
291
|
+
/** Stack of flags for main screen */
|
|
292
|
+
mainStack: number[];
|
|
293
|
+
/** Stack of flags for alternate screen */
|
|
294
|
+
altStack: number[];
|
|
295
|
+
}
|
|
296
|
+
|
|
280
297
|
export interface IRowRange {
|
|
281
298
|
start: number;
|
|
282
299
|
end: number;
|
package/src/common/Version.ts
CHANGED