@xterm/addon-image 0.10.0-beta.252 → 0.10.0-beta.253
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/README.md +20 -31
- package/lib/addon-image.js +1 -1
- package/lib/addon-image.js.map +1 -1
- package/lib/addon-image.mjs +1 -1
- package/lib/addon-image.mjs.map +3 -3
- package/package.json +3 -3
- package/src/IIPHandler.ts +69 -15
- package/src/IIPHeaderParser.ts +86 -11
- package/src/IIPImageStorage.ts +3 -1
- package/src/ImageAddon.ts +1 -6
- package/src/ImageStorage.ts +22 -12
- package/src/SixelImageStorage.ts +4 -2
- package/src/Types.ts +8 -0
- package/src/kitty/KittyImageStorage.ts +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xterm/addon-image",
|
|
3
|
-
"version": "0.10.0-beta.
|
|
3
|
+
"version": "0.10.0-beta.253",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "The xterm.js authors",
|
|
6
6
|
"url": "https://xtermjs.org/"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"sixel": "^0.16.0",
|
|
28
28
|
"xterm-wasm-parts": "^0.4.1"
|
|
29
29
|
},
|
|
30
|
-
"commit": "
|
|
30
|
+
"commit": "b2003861ecb4acf5b07f49eaadf8403e3e89826c",
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@xterm/xterm": "^6.1.0-beta.
|
|
32
|
+
"@xterm/xterm": "^6.1.0-beta.253"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/IIPHandler.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { IIPImageStorage } from './IIPImageStorage';
|
|
|
8
8
|
import { CELL_SIZE_DEFAULT } from './ImageStorage';
|
|
9
9
|
import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
|
|
10
10
|
import QoiDecoder from 'xterm-wasm-parts/lib/qoi/QoiDecoder.wasm';
|
|
11
|
-
import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser';
|
|
11
|
+
import { HeaderParser, IHeaderFields, HeaderState, SequenceType } from './IIPHeaderParser';
|
|
12
12
|
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';
|
|
13
13
|
|
|
14
14
|
// Local const enum mirror - esbuild can't inline const enums from external packages
|
|
@@ -23,6 +23,7 @@ const enum DecoderConst {
|
|
|
23
23
|
|
|
24
24
|
// default IIP header values
|
|
25
25
|
const DEFAULT_HEADER: IHeaderFields = {
|
|
26
|
+
type: SequenceType.INVALID,
|
|
26
27
|
name: 'Unnamed file',
|
|
27
28
|
size: 0,
|
|
28
29
|
width: 'auto',
|
|
@@ -39,6 +40,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|
|
39
40
|
private _dec: Base64Decoder;
|
|
40
41
|
private _qoiDec: QoiDecoder;
|
|
41
42
|
private _metrics = UNSUPPORTED_TYPE;
|
|
43
|
+
private _isMultipart = false;
|
|
44
|
+
private _abortMulti = false;
|
|
42
45
|
|
|
43
46
|
constructor(
|
|
44
47
|
private readonly _opts: IImageAddonOptions,
|
|
@@ -52,11 +55,14 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|
|
52
55
|
this._qoiDec = new QoiDecoder(DecoderConst.KEEP_DATA);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
public reset(): void {
|
|
58
|
+
public reset(): void {
|
|
59
|
+
this._hp.reset();
|
|
60
|
+
this._dec.release();
|
|
61
|
+
this._qoiDec.release();
|
|
62
|
+
}
|
|
56
63
|
|
|
57
64
|
public start(): void {
|
|
58
65
|
this._aborted = false;
|
|
59
|
-
this._header = DEFAULT_HEADER;
|
|
60
66
|
this._metrics = UNSUPPORTED_TYPE;
|
|
61
67
|
this._hp.reset();
|
|
62
68
|
}
|
|
@@ -76,15 +82,27 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|
|
76
82
|
return;
|
|
77
83
|
}
|
|
78
84
|
if (dataPos > 0) {
|
|
79
|
-
|
|
80
|
-
if (
|
|
85
|
+
const seqType = this._hp.fields.type;
|
|
86
|
+
if (seqType === SequenceType.FILE) {
|
|
87
|
+
if (this._isMultipart) {
|
|
88
|
+
this._isMultipart = false;
|
|
89
|
+
this._abortMulti = false;
|
|
90
|
+
this._dec.release();
|
|
91
|
+
}
|
|
92
|
+
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
|
|
93
|
+
if (!this._header.inline) {
|
|
94
|
+
this._aborted = true;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this._dec.init();
|
|
98
|
+
} else if (this._abortMulti) {
|
|
81
99
|
this._aborted = true;
|
|
82
100
|
return;
|
|
83
101
|
}
|
|
84
|
-
this._dec.init();
|
|
85
102
|
if ((this._dec.put(data.subarray(dataPos, end)) as number) !== DecoderConst.OK) {
|
|
86
103
|
this._dec.release();
|
|
87
104
|
this._aborted = true;
|
|
105
|
+
if (this._isMultipart) this._abortMulti = true;
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
108
|
}
|
|
@@ -93,6 +111,44 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|
|
93
111
|
public end(success: boolean): boolean | Promise<boolean> {
|
|
94
112
|
if (this._aborted) return true;
|
|
95
113
|
|
|
114
|
+
if (this._hp.state !== HeaderState.END) {
|
|
115
|
+
if (this._hp.end()) return true;
|
|
116
|
+
}
|
|
117
|
+
const seqType = this._hp.fields.type;
|
|
118
|
+
|
|
119
|
+
if (seqType === SequenceType.FILEPART) return true;
|
|
120
|
+
|
|
121
|
+
if (seqType === SequenceType.REPORTCELLSIZE) {
|
|
122
|
+
// OSC 1337 ; ReportCellSize=[height];[width];[scale] ST
|
|
123
|
+
let width = CELL_SIZE_DEFAULT.width;
|
|
124
|
+
let height = CELL_SIZE_DEFAULT.height;
|
|
125
|
+
if (this._renderer.dimensions) {
|
|
126
|
+
width = this._renderer.dimensions.css.canvas.width / this._coreTerminal.cols;
|
|
127
|
+
height = this._renderer.dimensions.css.canvas.height / this._coreTerminal.rows;
|
|
128
|
+
}
|
|
129
|
+
const scale = this._coreTerminal._core._coreBrowserService?.dpr ?? 1;
|
|
130
|
+
const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`;
|
|
131
|
+
this._coreTerminal.input(report, false);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (seqType === SequenceType.MULTIPARTFILE) {
|
|
136
|
+
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
|
|
137
|
+
this._isMultipart = true;
|
|
138
|
+
this._abortMulti = false;
|
|
139
|
+
this._dec.release();
|
|
140
|
+
this._dec.init();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (seqType === SequenceType.FILEEND) {
|
|
145
|
+
if (!this._isMultipart) return true;
|
|
146
|
+
this._isMultipart = false;
|
|
147
|
+
if (this._abortMulti || this._header.type !== SequenceType.MULTIPARTFILE) return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// fallthrough for SequenceType.FILE & SequenceType.FILEEND
|
|
151
|
+
|
|
96
152
|
let w = 0;
|
|
97
153
|
let h = 0;
|
|
98
154
|
|
|
@@ -100,15 +156,13 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|
|
100
156
|
let cond: number | boolean;
|
|
101
157
|
if (cond = success) {
|
|
102
158
|
if (cond = !this._dec.end()) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
cond = w && h && w * h < this._opts.pixelLimit;
|
|
111
|
-
}
|
|
159
|
+
this._metrics = imageType(this._dec.data8);
|
|
160
|
+
if (cond = this._metrics.mime !== 'unsupported') {
|
|
161
|
+
w = this._metrics.width;
|
|
162
|
+
h = this._metrics.height;
|
|
163
|
+
if (cond = w && h && w * h < this._opts.pixelLimit) {
|
|
164
|
+
[w, h] = this._resize(w, h).map(Math.floor);
|
|
165
|
+
cond = w && h && w * h < this._opts.pixelLimit;
|
|
112
166
|
}
|
|
113
167
|
}
|
|
114
168
|
}
|
package/src/IIPHeaderParser.ts
CHANGED
|
@@ -6,9 +6,27 @@
|
|
|
6
6
|
// eslint-disable-next-line
|
|
7
7
|
declare const Buffer: any;
|
|
8
8
|
|
|
9
|
+
export const enum HeaderState {
|
|
10
|
+
START = 0,
|
|
11
|
+
ABORT = 1,
|
|
12
|
+
KEY = 2,
|
|
13
|
+
VALUE = 3,
|
|
14
|
+
END = 4
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const enum SequenceType {
|
|
18
|
+
INVALID = 0,
|
|
19
|
+
FILE = 1,
|
|
20
|
+
MULTIPARTFILE = 2,
|
|
21
|
+
FILEPART = 3,
|
|
22
|
+
FILEEND = 4,
|
|
23
|
+
REPORTCELLSIZE = 5
|
|
24
|
+
}
|
|
9
25
|
|
|
10
26
|
export interface IHeaderFields {
|
|
11
27
|
[key: string]: number | string | Uint32Array | null | undefined;
|
|
28
|
+
// sequence type
|
|
29
|
+
type: SequenceType;
|
|
12
30
|
// base-64 encoded filename. Defaults to "Unnamed file".
|
|
13
31
|
name: string;
|
|
14
32
|
// File size in bytes. The file transfer will be canceled if this size is exceeded.
|
|
@@ -29,14 +47,6 @@ export interface IHeaderFields {
|
|
|
29
47
|
inline?: number;
|
|
30
48
|
}
|
|
31
49
|
|
|
32
|
-
export const enum HeaderState {
|
|
33
|
-
START = 0,
|
|
34
|
-
ABORT = 1,
|
|
35
|
-
KEY = 2,
|
|
36
|
-
VALUE = 3,
|
|
37
|
-
END = 4
|
|
38
|
-
}
|
|
39
|
-
|
|
40
50
|
// field value decoders
|
|
41
51
|
|
|
42
52
|
// ASCII bytes to string
|
|
@@ -92,7 +102,19 @@ const DECODERS: {[key: string]: (v: Uint32Array) => number | string} = {
|
|
|
92
102
|
};
|
|
93
103
|
|
|
94
104
|
|
|
105
|
+
// sequence type markers
|
|
106
|
+
// File
|
|
95
107
|
const FILE_MARKER = [70, 105, 108, 101];
|
|
108
|
+
// MultipartFile
|
|
109
|
+
const MULTIPARTFILE_MARKER = [77, 117, 108, 116, 105, 112, 97, 114, 116, 70, 105, 108, 101];
|
|
110
|
+
// FilePart
|
|
111
|
+
const FILEPART_MARKER = [70, 105, 108, 101, 80, 97, 114, 116];
|
|
112
|
+
// FileEnd
|
|
113
|
+
const FILEEND_MARKER = [70, 105, 108, 101, 69, 110, 100];
|
|
114
|
+
// ReportCellSize
|
|
115
|
+
const REPORTCELLSIZE_MARKER = [82, 101, 112, 111, 114, 116, 67, 101, 108, 108, 83, 105, 122, 101];
|
|
116
|
+
|
|
117
|
+
// max allowed chars for sequence header
|
|
96
118
|
const MAX_FIELDCHARS = 1024;
|
|
97
119
|
|
|
98
120
|
|
|
@@ -111,12 +133,43 @@ export class HeaderParser {
|
|
|
111
133
|
this._key = '';
|
|
112
134
|
}
|
|
113
135
|
|
|
136
|
+
public end(): number {
|
|
137
|
+
if (this.state === HeaderState.START) {
|
|
138
|
+
if (this._position === FILEEND_MARKER.length) {
|
|
139
|
+
for (let k = 0; k < FILEEND_MARKER.length; ++k) {
|
|
140
|
+
if (this._buffer[k] !== FILEEND_MARKER[k]) return this._a();
|
|
141
|
+
}
|
|
142
|
+
this.fields['type'] = SequenceType.FILEEND;
|
|
143
|
+
this.state = HeaderState.END;
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
if (this._position === REPORTCELLSIZE_MARKER.length) {
|
|
147
|
+
for (let k = 0; k < REPORTCELLSIZE_MARKER.length; ++k) {
|
|
148
|
+
if (this._buffer[k] !== REPORTCELLSIZE_MARKER[k]) return this._a();
|
|
149
|
+
}
|
|
150
|
+
this.fields['type'] = SequenceType.REPORTCELLSIZE;
|
|
151
|
+
this.state = HeaderState.END;
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
return this._a();
|
|
155
|
+
}
|
|
156
|
+
if (this.state === HeaderState.END) return 0;
|
|
157
|
+
if (this.state === HeaderState.VALUE
|
|
158
|
+
&& this.fields.type === SequenceType.MULTIPARTFILE
|
|
159
|
+
) {
|
|
160
|
+
if (!this._storeValue(this._position)) return this._a();
|
|
161
|
+
this.state = HeaderState.END;
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
return this._a();
|
|
165
|
+
}
|
|
166
|
+
|
|
114
167
|
public parse(data: Uint32Array, start: number, end: number): number {
|
|
115
168
|
let state = this.state;
|
|
116
169
|
let pos = this._position;
|
|
117
170
|
const buffer = this._buffer;
|
|
118
171
|
if (state === HeaderState.ABORT || state === HeaderState.END) return -1;
|
|
119
|
-
if (state === HeaderState.START && pos >
|
|
172
|
+
if (state === HeaderState.START && pos > 14) return -1;
|
|
120
173
|
for (let i = start; i < end; ++i) {
|
|
121
174
|
const c = data[i];
|
|
122
175
|
switch (c) {
|
|
@@ -127,8 +180,29 @@ export class HeaderParser {
|
|
|
127
180
|
break;
|
|
128
181
|
case 61: // =
|
|
129
182
|
if (state === HeaderState.START) {
|
|
130
|
-
|
|
131
|
-
|
|
183
|
+
if (buffer[0] === 70) {
|
|
184
|
+
// 'File' or 'FilePart'
|
|
185
|
+
let k = 0;
|
|
186
|
+
for (; k < FILE_MARKER.length; ++k) {
|
|
187
|
+
if (buffer[k] !== FILE_MARKER[k]) return this._a();
|
|
188
|
+
}
|
|
189
|
+
this.fields['type'] = SequenceType.FILE;
|
|
190
|
+
if (pos === FILEPART_MARKER.length) {
|
|
191
|
+
for (; k < FILEPART_MARKER.length; ++k) {
|
|
192
|
+
if (buffer[k] !== FILEPART_MARKER[k]) return this._a();
|
|
193
|
+
}
|
|
194
|
+
this.fields['type'] = SequenceType.FILEPART;
|
|
195
|
+
this.state = HeaderState.END;
|
|
196
|
+
return i + 1;
|
|
197
|
+
}
|
|
198
|
+
} else if (buffer[0] === 77) {
|
|
199
|
+
// 'MultipartFile'
|
|
200
|
+
for (let k = 0; k < MULTIPARTFILE_MARKER.length; ++k) {
|
|
201
|
+
if (buffer[k] !== MULTIPARTFILE_MARKER[k]) return this._a();
|
|
202
|
+
}
|
|
203
|
+
this.fields['type'] = SequenceType.MULTIPARTFILE;
|
|
204
|
+
} else {
|
|
205
|
+
return this._a();
|
|
132
206
|
}
|
|
133
207
|
state = HeaderState.KEY;
|
|
134
208
|
pos = 0;
|
|
@@ -158,6 +232,7 @@ export class HeaderParser {
|
|
|
158
232
|
}
|
|
159
233
|
|
|
160
234
|
private _a(): number {
|
|
235
|
+
this.fields.type = SequenceType.INVALID;
|
|
161
236
|
this.state = HeaderState.ABORT;
|
|
162
237
|
return -1;
|
|
163
238
|
}
|
package/src/IIPImageStorage.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @license MIT
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IAddImageOpts } from 'Types';
|
|
6
7
|
import { ImageStorage } from './ImageStorage';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -12,6 +13,7 @@ import { ImageStorage } from './ImageStorage';
|
|
|
12
13
|
* - Always uses scrolling mode (cursor advances with image)
|
|
13
14
|
*/
|
|
14
15
|
export class IIPImageStorage {
|
|
16
|
+
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' };
|
|
15
17
|
constructor(
|
|
16
18
|
private readonly _storage: ImageStorage
|
|
17
19
|
) {}
|
|
@@ -21,6 +23,6 @@ export class IIPImageStorage {
|
|
|
21
23
|
* Always uses scrolling mode — cursor advances past the image.
|
|
22
24
|
*/
|
|
23
25
|
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
|
24
|
-
this._storage.addImage(img,
|
|
26
|
+
this._storage.addImage(img, this._addImageOpts);
|
|
25
27
|
}
|
|
26
28
|
}
|
package/src/ImageAddon.ts
CHANGED
|
@@ -137,11 +137,6 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
|
|
|
137
137
|
|
|
138
138
|
// enable size reports
|
|
139
139
|
if (this._opts.enableSizeReports) {
|
|
140
|
-
// const windowOptions = terminal.getOption('windowOptions');
|
|
141
|
-
// windowOptions.getWinSizePixels = true;
|
|
142
|
-
// windowOptions.getCellSizePixels = true;
|
|
143
|
-
// windowOptions.getWinSizeChars = true;
|
|
144
|
-
// terminal.setOption('windowOptions', windowOptions);
|
|
145
140
|
const windowOps = terminal.options.windowOptions ?? {};
|
|
146
141
|
windowOps.getWinSizePixels = true;
|
|
147
142
|
windowOps.getCellSizePixels = true;
|
|
@@ -260,7 +255,7 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
|
|
|
260
255
|
}
|
|
261
256
|
|
|
262
257
|
private _report(s: string): void {
|
|
263
|
-
this._terminal?._core.
|
|
258
|
+
this._terminal?._core.input(s, false);
|
|
264
259
|
}
|
|
265
260
|
|
|
266
261
|
private _decset(params: (number | number[])[]): boolean {
|
package/src/ImageStorage.ts
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import { IDisposable } from '@xterm/xterm';
|
|
7
7
|
import { ImageRenderer } from './ImageRenderer';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec,
|
|
10
|
+
IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes,
|
|
11
|
+
UnderlineStyle, IAddImageOpts
|
|
12
|
+
} from './Types';
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
// fallback default cell size
|
|
@@ -235,13 +239,15 @@ export class ImageStorage implements IDisposable {
|
|
|
235
239
|
/**
|
|
236
240
|
* Method to add an image to the storage.
|
|
237
241
|
* @param img - The image to add (canvas or bitmap).
|
|
238
|
-
* @param
|
|
239
|
-
* When
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
+
* @param opts - Options for addImage:
|
|
243
|
+
* - scrolling: When true, cursor advances with the image.
|
|
244
|
+
* When false, image is placed at ORIGIN and cursor does not move.
|
|
245
|
+
* - layer: Which canvas layer to render on ('top' or 'bottom').
|
|
246
|
+
* - zIndex: Z-index for image layering within the same layer.
|
|
247
|
+
* - cursorPos: 'vt340' for bottom-left, 'iip' for bottom.right.
|
|
242
248
|
* @returns The internal image ID assigned to the stored image.
|
|
243
249
|
*/
|
|
244
|
-
public addImage(img: HTMLCanvasElement | ImageBitmap,
|
|
250
|
+
public addImage(img: HTMLCanvasElement | ImageBitmap, opts: IAddImageOpts): number {
|
|
245
251
|
// never allow storage to exceed memory limit
|
|
246
252
|
this._evictOldest(img.width * img.height);
|
|
247
253
|
|
|
@@ -263,7 +269,7 @@ export class ImageStorage implements IDisposable {
|
|
|
263
269
|
let offset = originX;
|
|
264
270
|
let tileCount = 0;
|
|
265
271
|
|
|
266
|
-
if (!scrolling) {
|
|
272
|
+
if (!opts.scrolling) {
|
|
267
273
|
buffer.x = 0;
|
|
268
274
|
buffer.y = 0;
|
|
269
275
|
offset = 0;
|
|
@@ -277,7 +283,7 @@ export class ImageStorage implements IDisposable {
|
|
|
277
283
|
this._writeToCell(line as IBufferLineExt, offset + col, imageId, row * cols + col);
|
|
278
284
|
tileCount++;
|
|
279
285
|
}
|
|
280
|
-
if (scrolling) {
|
|
286
|
+
if (opts.scrolling) {
|
|
281
287
|
if (row < rows - 1) this._terminal._core._inputHandler.lineFeed();
|
|
282
288
|
} else {
|
|
283
289
|
if (++buffer.y >= termRows) break;
|
|
@@ -287,8 +293,12 @@ export class ImageStorage implements IDisposable {
|
|
|
287
293
|
this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y);
|
|
288
294
|
|
|
289
295
|
// cursor positioning modes
|
|
290
|
-
if (scrolling) {
|
|
291
|
-
|
|
296
|
+
if (opts.scrolling) {
|
|
297
|
+
if (opts.cursorPos === 'iip') {
|
|
298
|
+
buffer.x = Math.min(offset + cols, termCols);
|
|
299
|
+
} else {
|
|
300
|
+
buffer.x = offset;
|
|
301
|
+
}
|
|
292
302
|
} else {
|
|
293
303
|
buffer.x = originX;
|
|
294
304
|
buffer.y = originY;
|
|
@@ -331,8 +341,8 @@ export class ImageStorage implements IDisposable {
|
|
|
331
341
|
marker: endMarker || undefined,
|
|
332
342
|
tileCount,
|
|
333
343
|
bufferType: this._terminal.buffer.active.type,
|
|
334
|
-
layer,
|
|
335
|
-
zIndex
|
|
344
|
+
layer: opts.layer,
|
|
345
|
+
zIndex: opts.zIndex
|
|
336
346
|
};
|
|
337
347
|
|
|
338
348
|
// finally add the image
|
package/src/SixelImageStorage.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
|
|
7
|
-
import { IImageAddonOptions, ITerminalExt } from './Types';
|
|
7
|
+
import { IImageAddonOptions, ITerminalExt, IAddImageOpts } from './Types';
|
|
8
8
|
import { ImageRenderer } from './ImageRenderer';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -15,6 +15,7 @@ import { ImageRenderer } from './ImageRenderer';
|
|
|
15
15
|
* - advanceCursor for empty sixels carrying only height
|
|
16
16
|
*/
|
|
17
17
|
export class SixelImageStorage {
|
|
18
|
+
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'vt340' };
|
|
18
19
|
constructor(
|
|
19
20
|
private readonly _storage: ImageStorage,
|
|
20
21
|
private readonly _opts: IImageAddonOptions,
|
|
@@ -27,7 +28,8 @@ export class SixelImageStorage {
|
|
|
27
28
|
* Cursor behavior depends on the sixelScrolling option (DECSET 80).
|
|
28
29
|
*/
|
|
29
30
|
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
|
30
|
-
this.
|
|
31
|
+
this._addImageOpts.scrolling = this._opts.sixelScrolling;
|
|
32
|
+
this._storage.addImage(img, this._addImageOpts);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
package/src/Types.ts
CHANGED
|
@@ -100,6 +100,14 @@ export interface ICellSize {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export type ImageLayer = 'top' | 'bottom';
|
|
103
|
+
export type CursorPos = 'vt340' | 'iip';
|
|
104
|
+
|
|
105
|
+
export interface IAddImageOpts {
|
|
106
|
+
scrolling: boolean;
|
|
107
|
+
layer: ImageLayer;
|
|
108
|
+
zIndex: number;
|
|
109
|
+
cursorPos: CursorPos;
|
|
110
|
+
}
|
|
103
111
|
|
|
104
112
|
export interface IImageSpec {
|
|
105
113
|
orig: HTMLCanvasElement | ImageBitmap | undefined;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { IDisposable } from '@xterm/xterm';
|
|
7
7
|
import { ImageStorage } from '../ImageStorage';
|
|
8
|
-
import { ImageLayer } from '../Types';
|
|
8
|
+
import { ImageLayer, IAddImageOpts } from '../Types';
|
|
9
9
|
import { IKittyImageData } from './KittyGraphicsTypes';
|
|
10
10
|
|
|
11
11
|
// Kitty-specific image storage controller.
|
|
@@ -42,6 +42,7 @@ export class KittyImageStorage implements IDisposable {
|
|
|
42
42
|
this._images.delete(kittyId);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
+
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' };
|
|
45
46
|
|
|
46
47
|
constructor(
|
|
47
48
|
private readonly _storage: ImageStorage
|
|
@@ -98,7 +99,10 @@ export class KittyImageStorage implements IDisposable {
|
|
|
98
99
|
if (oldStorageId !== undefined) {
|
|
99
100
|
this._storageIdToKittyId.delete(oldStorageId);
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
+
this._addImageOpts.scrolling = scrolling;
|
|
103
|
+
this._addImageOpts.layer = layer;
|
|
104
|
+
this._addImageOpts.zIndex = zIndex;
|
|
105
|
+
const storageId = this._storage.addImage(image, this._addImageOpts);
|
|
102
106
|
this._kittyIdToStorageId.set(kittyId, storageId);
|
|
103
107
|
this._storageIdToKittyId.set(storageId, kittyId);
|
|
104
108
|
}
|