@xterm/addon-webgl 0.20.0-beta.17 → 0.20.0-beta.170

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.
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
7
7
  import { customGlyphDefinitions } from './CustomGlyphDefinitions';
8
- import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
8
+ import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
9
9
 
10
10
  /**
11
11
  * Try drawing a custom block element or box drawing character, returning whether it was
@@ -18,16 +18,19 @@ export function tryDrawCustomGlyph(
18
18
  yOffset: number,
19
19
  deviceCellWidth: number,
20
20
  deviceCellHeight: number,
21
+ deviceCharWidth: number,
22
+ deviceCharHeight: number,
21
23
  fontSize: number,
22
24
  devicePixelRatio: number,
23
- backgroundColor?: string
25
+ backgroundColor?: string,
26
+ variantOffset: number = 0
24
27
  ): boolean {
25
28
  const unifiedCharDefinition = customGlyphDefinitions[c];
26
29
  if (unifiedCharDefinition) {
27
30
  // Normalize to array for uniform handling
28
31
  const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
29
32
  for (const part of parts) {
30
- drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio, backgroundColor);
33
+ drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor, variantOffset);
31
34
  }
32
35
  return true;
33
36
  }
@@ -42,37 +45,53 @@ function drawDefinitionPart(
42
45
  yOffset: number,
43
46
  deviceCellWidth: number,
44
47
  deviceCellHeight: number,
48
+ deviceCharWidth: number,
49
+ deviceCharHeight: number,
45
50
  fontSize: number,
46
51
  devicePixelRatio: number,
47
- backgroundColor?: string
52
+ backgroundColor?: string,
53
+ variantOffset: number = 0
48
54
  ): void {
55
+ // Handle scaleType - adjust dimensions and offset when scaling to character area
56
+ let drawWidth = deviceCellWidth;
57
+ let drawHeight = deviceCellHeight;
58
+ let drawXOffset = xOffset;
59
+ let drawYOffset = yOffset;
60
+ if (part.scaleType === CustomGlyphScaleType.CHAR) {
61
+ drawWidth = deviceCharWidth;
62
+ drawHeight = deviceCharHeight;
63
+ // Center the character within the cell
64
+ drawXOffset = xOffset + (deviceCellWidth - deviceCharWidth) / 2;
65
+ drawYOffset = yOffset + (deviceCellHeight - deviceCharHeight) / 2;
66
+ }
67
+
49
68
  // Handle clipPath generically for any definition type
50
69
  if (part.clipPath) {
51
70
  ctx.save();
52
- applyClipPath(ctx, part.clipPath, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
71
+ applyClipPath(ctx, part.clipPath, drawXOffset, drawYOffset, drawWidth, drawHeight);
53
72
  }
54
73
 
55
74
  switch (part.type) {
56
75
  case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR:
57
- drawBlockVectorChar(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
76
+ drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
58
77
  break;
59
78
  case CustomGlyphDefinitionType.BLOCK_PATTERN:
60
- drawPatternChar(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
79
+ drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset);
61
80
  break;
62
81
  case CustomGlyphDefinitionType.PATH_FUNCTION:
63
- drawPathFunctionCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, part.strokeWidth);
82
+ drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
64
83
  break;
65
84
  case CustomGlyphDefinitionType.PATH:
66
- drawPathDefinitionCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
85
+ drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
67
86
  break;
68
87
  case CustomGlyphDefinitionType.PATH_NEGATIVE:
69
- drawPathNegativeDefinitionCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, backgroundColor);
88
+ drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor);
70
89
  break;
71
90
  case CustomGlyphDefinitionType.VECTOR_SHAPE:
72
- drawVectorShape(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio);
91
+ drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio);
73
92
  break;
74
93
  case CustomGlyphDefinitionType.BRAILLE:
75
- drawBrailleCharacter(ctx, part.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
94
+ drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
76
95
  break;
77
96
  }
78
97
 
@@ -154,7 +173,9 @@ function drawPathDefinitionCharacter(
154
173
  xOffset: number,
155
174
  yOffset: number,
156
175
  deviceCellWidth: number,
157
- deviceCellHeight: number
176
+ deviceCellHeight: number,
177
+ devicePixelRatio: number,
178
+ strokeWidth?: number
158
179
  ): void {
159
180
  const instructions = typeof charDefinition === 'string' ? charDefinition : charDefinition(0, 0);
160
181
  ctx.beginPath();
@@ -255,7 +276,13 @@ function drawPathDefinitionCharacter(
255
276
  }
256
277
  lastCommand = type;
257
278
  }
258
- ctx.fill();
279
+ if (strokeWidth !== undefined) {
280
+ ctx.strokeStyle = ctx.fillStyle;
281
+ ctx.lineWidth = devicePixelRatio * strokeWidth;
282
+ ctx.stroke();
283
+ } else {
284
+ ctx.fill();
285
+ }
259
286
  }
260
287
 
261
288
  /**
@@ -412,7 +439,8 @@ function drawPatternChar(
412
439
  xOffset: number,
413
440
  yOffset: number,
414
441
  deviceCellWidth: number,
415
- deviceCellHeight: number
442
+ deviceCellHeight: number,
443
+ variantOffset: number = 0
416
444
  ): void {
417
445
  let patternSet = cachedPatterns.get(charDefinition);
418
446
  if (!patternSet) {
@@ -461,6 +489,15 @@ function drawPatternChar(
461
489
  pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
462
490
  patternSet.set(fillStyle, pattern);
463
491
  }
492
+ // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd.
493
+ // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift.
494
+ const dx = (variantOffset >> 1) & 1;
495
+ const dy = variantOffset & 1;
496
+ if (dx !== 0 || dy !== 0) {
497
+ pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy));
498
+ } else {
499
+ pattern.setTransform(new DOMMatrix());
500
+ }
464
501
  ctx.fillStyle = pattern;
465
502
  ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
466
503
  }
@@ -475,6 +512,11 @@ function drawPathFunctionCharacter(
475
512
  devicePixelRatio: number,
476
513
  strokeWidth?: number
477
514
  ): void {
515
+ ctx.save();
516
+ ctx.beginPath();
517
+ ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
518
+ ctx.clip();
519
+
478
520
  ctx.beginPath();
479
521
  let actualInstructions: string;
480
522
  if (typeof charDefinition === 'function') {
@@ -501,7 +543,7 @@ function drawPathFunctionCharacter(
501
543
  if (!args[0] || !args[1]) {
502
544
  continue;
503
545
  }
504
- f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
546
+ f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state);
505
547
  state.lastCommand = type;
506
548
  }
507
549
  if (strokeWidth !== undefined) {
@@ -512,6 +554,7 @@ function drawPathFunctionCharacter(
512
554
  ctx.fill();
513
555
  }
514
556
  ctx.closePath();
557
+ ctx.restore();
515
558
  }
516
559
 
517
560
  /**
@@ -660,7 +703,7 @@ const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext
660
703
  }
661
704
  };
662
705
 
663
- function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
706
+ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0, clampToCell: boolean = true): number[] {
664
707
  const result = args.map(e => parseFloat(e) || parseInt(e));
665
708
 
666
709
  if (result.length < 2) {
@@ -670,10 +713,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
670
713
  for (let x = 0; x < result.length; x += 2) {
671
714
  // Translate from 0-1 to 0-cellWidth
672
715
  result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
673
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
674
- // line at 100% devicePixelRatio
716
+ // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
717
+ // clamp to the cell bounds.
675
718
  if (doClamp && result[x] !== 0) {
676
- result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
719
+ const rounded = Math.round(result[x] + 0.5) - 0.5;
720
+ result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded;
677
721
  }
678
722
  // Apply the cell's offset (ie. x*cellWidth)
679
723
  result[x] += xOffset + (leftPadding * devicePixelRatio);
@@ -682,10 +726,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
682
726
  for (let y = 1; y < result.length; y += 2) {
683
727
  // Translate from 0-1 to 0-cellHeight
684
728
  result[y] *= cellHeight;
685
- // Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
686
- // line at 100% devicePixelRatio
729
+ // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
730
+ // clamp to the cell bounds.
687
731
  if (doClamp && result[y] !== 0) {
688
- result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);
732
+ const rounded = Math.round(result[y] + 0.5) - 0.5;
733
+ result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded;
689
734
  }
690
735
  // Apply the cell's offset (ie. x*cellHeight)
691
736
  result[y] += yOffset;
@@ -50,6 +50,17 @@ export type CustomGlyphDefinitionPartRaw = (
50
50
  { type: CustomGlyphDefinitionType.BRAILLE, data: number }
51
51
  );
52
52
 
53
+ export const enum CustomGlyphScaleType {
54
+ /**
55
+ * Scale to the entire cell, including letter spacing and line height.
56
+ */
57
+ CELL,
58
+ /**
59
+ * Scale to only the character area, excluding letter spacing and line height.
60
+ */
61
+ CHAR,
62
+ }
63
+
53
64
  export interface ICustomGlyphDefinitionCommon {
54
65
  /**
55
66
  * A custom clip path for the draw definition, restricting the area it can draw to.
@@ -59,6 +70,11 @@ export interface ICustomGlyphDefinitionCommon {
59
70
  * The stroke width to use when drawing the path. Defaults to 1.
60
71
  */
61
72
  strokeWidth?: number;
73
+ /**
74
+ * Defines how to scale the draw. Defaults to scaling to the full cell including letter spacing
75
+ * and line height.
76
+ */
77
+ scaleType?: CustomGlyphScaleType;
62
78
  }
63
79
 
64
80
  export type CustomGlyphDefinitionPart = CustomGlyphDefinitionPartRaw & ICustomGlyphDefinitionCommon;
@@ -7,7 +7,7 @@ import { ReadonlyColorSet } from 'browser/Types';
7
7
  import { acquireTextureAtlas } from '../CharAtlasCache';
8
8
  import { IRenderDimensions } from 'browser/renderer/shared/Types';
9
9
  import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
10
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
10
+ import { Disposable, toDisposable } from 'common/Lifecycle';
11
11
  import { CellData } from 'common/buffer/CellData';
12
12
  import { IOptionsService } from 'common/services/Services';
13
13
  import { Terminal } from '@xterm/xterm';
@@ -57,21 +57,26 @@ declare module '@xterm/addon-webgl' {
57
57
  * unicode ranges:
58
58
  *
59
59
  * - Box Drawing (U+2500-U+257F)
60
- * - Box Elements (U+2580-U+259F)
60
+ * - Block Elements (U+2580-U+259F)
61
61
  * - Braille Patterns (U+2800-U+28FF)
62
- * - Powerline Symbols (U+E0A0U+E0D4)
63
- * - Symbols for Legacy Computing (U+1FB00–U+1FBFF)
62
+ * - Powerline Symbols (U+E0A0-U+E0D4, Private Use Area with widespread
63
+ * adoption)
64
+ * - Progress Indicators (U+EE00-U+EE0B, Private Use Area initially added in
65
+ * [Fira Code](https://github.com/tonsky/FiraCode) and later
66
+ * [Nerd Fonts](https://github.com/ryanoasis/nerd-fonts/pull/1733)).
67
+ * - Git Branch Symbols (U+F5D0-U+F60D, Private Use Area initially adopted
68
+ * in [Kitty in 2024](https://github.com/kovidgoyal/kitty/pull/7681) by
69
+ * author of [vim-flog](https://github.com/rbong/vim-flog))
70
+ * - Symbols for Legacy Computing (U+1FB00-U+1FBFF)
64
71
  *
65
72
  * This will typically result in better rendering with continuous lines,
66
- * even when line height and letter spacing is used. Note that this doesn't
67
- * work with the DOM renderer which renders all characters using the font.
68
- * The default is true.
73
+ * even when line height and letter spacing is used. The default is true.
69
74
  */
70
75
  customGlyphs?: boolean;
71
76
 
72
77
  /**
73
78
  * Whether to enable the preserveDrawingBuffer flag when creating the WebGL
74
- * context. This may be useful in tests. This defaults to false.
79
+ * context. This may be useful in tests. This default is false.
75
80
  */
76
81
  preserveDrawingBuffer?: boolean
77
82
  }