mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #5472 from Tyriar/tyriar/5466_refactor__unified
Custom glyph: Unify rendering function and move all definitions into a single ordered object
This commit is contained in:
@@ -159,14 +159,6 @@
|
||||
"@typescript-eslint/prefer-namespace-keyword": "warn",
|
||||
"@typescript-eslint/type-annotation-spacing": "warn",
|
||||
|
||||
"comma-dangle": [
|
||||
"warn",
|
||||
{
|
||||
"objects": "never",
|
||||
"arrays": "never",
|
||||
"functions": "never"
|
||||
}
|
||||
],
|
||||
"curly": [
|
||||
"warn",
|
||||
"multi-line"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
||||
import { blockElementDefinitions, blockShadeComboDefinitions, boxDrawingDefinitions, patternCharacterDefinitions, powerlineDefinitions, rectangularShadeDefinitions, symbolsForLegacyComputingDefinitions } from 'customGlyphs/CustomGlyphDefinitions';
|
||||
import { CustomGlyphVectorType, type CustomGlyphDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from 'customGlyphs/Types';
|
||||
import { customGlyphDefinitions } from './CustomGlyphDefinitions';
|
||||
import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type CustomGlyphRegionDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
|
||||
|
||||
/**
|
||||
* Try drawing a custom block element or box drawing character, returning whether it was
|
||||
@@ -21,46 +21,33 @@ export function tryDrawCustomGlyph(
|
||||
fontSize: number,
|
||||
devicePixelRatio: number
|
||||
): boolean {
|
||||
const blockElementDefinition = blockElementDefinitions[c];
|
||||
if (blockElementDefinition) {
|
||||
drawBlockVectorChar(ctx, blockElementDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
const symbolsForLegacyComputingDefinition = symbolsForLegacyComputingDefinitions[c];
|
||||
if (symbolsForLegacyComputingDefinition) {
|
||||
drawPathDefinitionCharacter(ctx, symbolsForLegacyComputingDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
const rectangularShadeDefinition = rectangularShadeDefinitions[c];
|
||||
if (rectangularShadeDefinition) {
|
||||
drawRectangularShadeChar(ctx, rectangularShadeDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
const blockShadeComboDefinition = blockShadeComboDefinitions[c];
|
||||
if (blockShadeComboDefinition) {
|
||||
drawBlockShadeComboChar(ctx, blockShadeComboDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
const patternDefinition = patternCharacterDefinitions[c];
|
||||
if (patternDefinition) {
|
||||
drawPatternChar(ctx, patternDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
const boxDrawingDefinition = boxDrawingDefinitions[c];
|
||||
if (boxDrawingDefinition) {
|
||||
drawBoxDrawingChar(ctx, boxDrawingDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
const powerlineDefinition = powerlineDefinitions[c];
|
||||
if (powerlineDefinition) {
|
||||
drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio);
|
||||
return true;
|
||||
const unifiedCharDefinition = customGlyphDefinitions[c];
|
||||
if (unifiedCharDefinition) {
|
||||
switch (unifiedCharDefinition.type) {
|
||||
case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR:
|
||||
drawBlockVectorChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
||||
drawPatternChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION:
|
||||
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR:
|
||||
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data.pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
drawBlockVectorChar(ctx, unifiedCharDefinition.data.vectors, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
||||
case CustomGlyphDefinitionType.PATH:
|
||||
drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.VECTOR_SHAPE:
|
||||
drawVectorShape(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio);
|
||||
return true;
|
||||
case CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT:
|
||||
drawPathDefinitionCharacterWithWeight(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -89,13 +76,13 @@ function drawBlockVectorChar(
|
||||
|
||||
function drawPathDefinitionCharacter(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
charDefinition: CustomGlyphDrawFunctionDefinition,
|
||||
charDefinition: CustomGlyphPathDrawFunctionDefinition | string,
|
||||
xOffset: number,
|
||||
yOffset: number,
|
||||
deviceCellWidth: number,
|
||||
deviceCellHeight: number
|
||||
): void {
|
||||
const instructions = charDefinition(0, 0);
|
||||
const instructions = typeof charDefinition === 'string' ? charDefinition : charDefinition(0, 0);
|
||||
ctx.beginPath();
|
||||
for (const instruction of instructions.split(' ')) {
|
||||
const type = instruction[0];
|
||||
@@ -186,9 +173,9 @@ function drawPatternChar(
|
||||
* Draws rectangular shade characters - medium shade pattern clipped to a region.
|
||||
* Uses a checkerboard pattern that shifts 1px each row (same as medium shade U+2592).
|
||||
*/
|
||||
function drawRectangularShadeChar(
|
||||
function drawBlockPatternWithRegion(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
definition: [CustomGlyphPatternDefinition, [number, number, number, number]],
|
||||
definition: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition],
|
||||
xOffset: number,
|
||||
yOffset: number,
|
||||
deviceCellWidth: number,
|
||||
@@ -216,45 +203,6 @@ function drawRectangularShadeChar(
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws block + inverse shade combo characters.
|
||||
* Fills the solid region completely, then draws inverse medium shade in the shade region.
|
||||
*/
|
||||
function drawBlockShadeComboChar(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
regions: [[number, number, number, number], [number, number, number, number]],
|
||||
xOffset: number,
|
||||
yOffset: number,
|
||||
deviceCellWidth: number,
|
||||
deviceCellHeight: number
|
||||
): void {
|
||||
const [solidRegion, shadeRegion] = regions;
|
||||
|
||||
// Draw solid block region
|
||||
const solidX = Math.round(xOffset + solidRegion[0] * deviceCellWidth);
|
||||
const solidY = Math.round(yOffset + solidRegion[1] * deviceCellHeight);
|
||||
const solidW = Math.round(solidRegion[2] * deviceCellWidth);
|
||||
const solidH = Math.round(solidRegion[3] * deviceCellHeight);
|
||||
ctx.fillRect(solidX, solidY, solidW, solidH);
|
||||
|
||||
// Draw inverse medium shade region
|
||||
const shadeX = Math.round(xOffset + shadeRegion[0] * deviceCellWidth);
|
||||
const shadeY = Math.round(yOffset + shadeRegion[1] * deviceCellHeight);
|
||||
const shadeW = Math.round(shadeRegion[2] * deviceCellWidth);
|
||||
const shadeH = Math.round(shadeRegion[3] * deviceCellHeight);
|
||||
|
||||
for (let py = 0; py < shadeH; py++) {
|
||||
const absY = shadeY + py - yOffset;
|
||||
for (let px = 0; px < shadeW; px++) {
|
||||
const absX = shadeX + px - xOffset;
|
||||
// Inverse checkerboard: fill at (x + y) % 2 === 1
|
||||
if (((absX + absY) % 2) === 1) {
|
||||
ctx.fillRect(shadeX + px, shadeY + py, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to
|
||||
* canvas draw calls.
|
||||
@@ -295,7 +243,7 @@ function drawBlockShadeComboChar(
|
||||
*
|
||||
* Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
|
||||
*/
|
||||
function drawBoxDrawingChar(
|
||||
function drawPathDefinitionCharacterWithWeight(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) },
|
||||
xOffset: number,
|
||||
@@ -334,7 +282,7 @@ function drawBoxDrawingChar(
|
||||
}
|
||||
}
|
||||
|
||||
function drawPowerlineChar(
|
||||
function drawVectorShape(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
charDefinition: ICustomGlyphVectorShape,
|
||||
xOffset: number,
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface ICustomGlyphSolidOctantBlockVector {
|
||||
* @param xp The percentage of 15% of the x axis.
|
||||
* @param yp The percentage of 15% of the x axis on the y axis.
|
||||
*/
|
||||
export type CustomGlyphDrawFunctionDefinition = (xp: number, yp: number) => string;
|
||||
export type CustomGlyphPathDrawFunctionDefinition = (xp: number, yp: number) => string;
|
||||
|
||||
export interface ICustomGlyphVectorShape {
|
||||
d: string;
|
||||
@@ -29,3 +29,29 @@ export const enum CustomGlyphVectorType {
|
||||
}
|
||||
|
||||
export type CustomGlyphPatternDefinition = number[][];
|
||||
|
||||
export const enum CustomGlyphDefinitionType {
|
||||
SOLID_OCTANT_BLOCK_VECTOR,
|
||||
BLOCK_PATTERN,
|
||||
BLOCK_PATTERN_WITH_REGION,
|
||||
BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR,
|
||||
PATH_FUNCTION,
|
||||
PATH_FUNCTION_WITH_WEIGHT,
|
||||
PATH,
|
||||
VECTOR_SHAPE,
|
||||
}
|
||||
|
||||
export type CustomGlyphRegionDefinition = [x: number, y: number, w: number, h: number];
|
||||
|
||||
export type CustomGlyphCharacterDefinition = (
|
||||
{ type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: ICustomGlyphSolidOctantBlockVector[] } |
|
||||
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: CustomGlyphPatternDefinition } |
|
||||
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION, data: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition] } |
|
||||
// TODO: Consolidate these, draws should be possible via regions/clipping instead of special
|
||||
// casing
|
||||
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { pattern: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition], vectors: ICustomGlyphSolidOctantBlockVector[] } } |
|
||||
{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } |
|
||||
{ type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [fontWeight: number]: string | CustomGlyphPathDrawFunctionDefinition } } |
|
||||
{ type: CustomGlyphDefinitionType.PATH, data: string } |
|
||||
{ type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape }
|
||||
);
|
||||
|
||||
+36
-5
@@ -234,8 +234,8 @@ if (document.location.pathname === '/test') {
|
||||
document.getElementById('create-new-window').addEventListener('click', createNewWindowButtonHandler);
|
||||
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
|
||||
document.getElementById('htmlserialize').addEventListener('click', htmlSerializeButtonHandler);
|
||||
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
|
||||
document.getElementById('symbols-for-legacy-computing').addEventListener('click', writeSymbolsForLegacyComputing);
|
||||
document.getElementById('custom-glyph-alignment').addEventListener('click', customGlyphAlignmentHandler);
|
||||
document.getElementById('custom-glyph-ranges').addEventListener('click', customGlyphRangesHandler);
|
||||
document.getElementById('load-test').addEventListener('click', loadTest);
|
||||
document.getElementById('load-test-long-lines').addEventListener('click', loadTestLongLines);
|
||||
document.getElementById('print-cjk').addEventListener('click', addCjk);
|
||||
@@ -786,7 +786,7 @@ function styleAtlasPage(e: HTMLCanvasElement): void {
|
||||
e.style.height = `${e.height / window.devicePixelRatio}px`;
|
||||
}
|
||||
|
||||
function writeCustomGlyphHandler(): void {
|
||||
function customGlyphAlignmentHandler(): void {
|
||||
term.write('\n\r');
|
||||
term.write('\n\r');
|
||||
term.write('Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐\n\r');
|
||||
@@ -839,10 +839,41 @@ function writeCustomGlyphHandler(): void {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function writeSymbolsForLegacyComputing(): void {
|
||||
function customGlyphRangesHandler(): void {
|
||||
// Box Drawing
|
||||
// 2500-257F
|
||||
// https://www.unicode.org/charts/PDF/U2500.pdf
|
||||
writeUnicodeTable(term, 'Box Drawing', 0x2500, 0x257F, [
|
||||
['Light and heavy solid lines', 0x2500, 0x2503],
|
||||
['Light and heavy dashed lines', 0x2504, 0x250B],
|
||||
['Light and heavy line box components', 0x250C, 0x254B],
|
||||
['Light and heavy dashed lines', 0x254C, 0x254F],
|
||||
['Double lines', 0x2550, 0x2551],
|
||||
['Light and double line box components', 0x2552, 0x256C],
|
||||
['Character cell arcs', 0x256D, 0x2570],
|
||||
['Character cell diagonals', 0x2571, 0x2573],
|
||||
['Light and heavy half lines', 0x2574, 0x257B],
|
||||
['Mixed light and heavy lines', 0x257C, 0x257F],
|
||||
]);
|
||||
// Box Elements
|
||||
// 2580-259F
|
||||
// https://www.unicode.org/charts/PDF/U2580.pdf
|
||||
writeUnicodeTable(term, 'Box Elements', 0x2580, 0x259F, [
|
||||
['Block elements', 0x2580, 0x2590],
|
||||
['Shade characters', 0x2591, 0x2593],
|
||||
['Block elements', 0x2594, 0x2595],
|
||||
['Terminal graphic characters', 0x2596, 0x259F],
|
||||
]);
|
||||
// Powerline Symbols
|
||||
// Range: E0A0–E0BF
|
||||
// https://github.com/ryanoasis/nerd-fonts
|
||||
writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0BF, [
|
||||
['Powerline Symbols', 0xE0A0, 0xE0B3],
|
||||
['Powerline Extra Symbols', 0xE0B4, 0xE0BF],
|
||||
]);
|
||||
// Symbols for Legacy Computing
|
||||
// Range: 1FB00–1FBFF
|
||||
// Source: The Unicode Standard, Version 17.0 https://www.unicode.org/charts/PDF/U1FB00.pdf
|
||||
// https://www.unicode.org/charts/PDF/U1FB00.pdf
|
||||
writeUnicodeTable(term, 'Symbols for Legacy Computing', 0x1FB00, 0x1FBFF, [
|
||||
['Block mosaic terminal graphic characters (Sextants)', 0x1FB00, 0x1FB3B],
|
||||
['Smooth mosaic terminal graphic characters', 0x1FB3C, 0x1FB6F],
|
||||
|
||||
+2
-2
@@ -92,8 +92,8 @@
|
||||
<dd><button id="print-cjk-sgr" title="Prints the 20977 characters from the CJK Unified Ideographs unicode block with randomized SGR attributes">CJK Unified Ideographs (random SGR)</button></dd>
|
||||
|
||||
<dt>Styles</dt>
|
||||
<dd><button id="custom-glyph" title="Write custom box drawing and block element characters to the terminal">Test custom glyphs</button></dd>
|
||||
<dd><button id="symbols-for-legacy-computing" title="Write this unicode range to the terminal">Print Symbols for Legacy Computing (Range: 1FB00–1FBFF)</button></dd>
|
||||
<dd><button id="custom-glyph-alignment" title="Write custom glyph alignment tests to the terminal">Custom glyph alignment test</button></dd>
|
||||
<dd><button id="custom-glyph-ranges" title="Write custom glyph unicode range to the terminal">Custom glyph ranges</button></dd>
|
||||
<dd><button id="powerline-symbol-test" title="Write powerline symbol characters to the terminal (\ue0a0+)">Powerline symbol test</button></dd>
|
||||
<dd><button id="underline-test" title="Write text with Kitty's extended underline sequences">Underline test</button></dd>
|
||||
<dd><button id="sgr-test" title="Write text with SGR attribute">SGR test</button></dd>
|
||||
|
||||
+11
-10
@@ -6,7 +6,7 @@ export type UnicodeRangeDefinition = [
|
||||
label: string,
|
||||
start: number,
|
||||
end: number,
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Write a unicode table to the terminal from start to end code points.
|
||||
@@ -14,29 +14,29 @@ export type UnicodeRangeDefinition = [
|
||||
* Beware: Vibe coding ahead
|
||||
*/
|
||||
export function writeUnicodeTable(term: Terminal, name: string, start: number, end: number, definitions?: UnicodeRangeDefinition[]): void {
|
||||
function bold(text: string) {
|
||||
function bold(text: string): string {
|
||||
return '\x1b[1m' + text + '\x1b[22m';
|
||||
}
|
||||
function faint(text: string) {
|
||||
function faint(text: string): string {
|
||||
return '\x1b[2m' + text + '\x1b[22m';
|
||||
}
|
||||
// Rotating colors: Red, Green, Yellow, Blue, Magenta, Cyan
|
||||
const colors = [31, 32, 33, 34, 35, 36];
|
||||
function color(text: string, colorIndex: number) {
|
||||
function color(text: string, colorIndex: number): string {
|
||||
const c = colors[colorIndex % colors.length];
|
||||
return '\x1b[' + c + 'm' + text + '\x1b[39m';
|
||||
}
|
||||
|
||||
term.write('\n\r');
|
||||
term.write('\n\r');
|
||||
term.write(bold(name) + '\n\r');
|
||||
term.write(`${bold(name)} (${start.toString(16).toUpperCase()}-${end.toString(16).toUpperCase()})\n\r`);
|
||||
term.write('\n\r');
|
||||
term.write(bold(' 0 1 2 3 4 5 6 7 8 9 A B C D E F') + '\n\r');
|
||||
|
||||
const startRow = Math.floor(start / 16);
|
||||
|
||||
// Build a map of codepoint -> label and colorIndex for start positions
|
||||
const labelStartMap = new Map<number, { label: string; colorIndex: number }>();
|
||||
const labelStartMap = new Map<number, { label: string, colorIndex: number }>();
|
||||
// Build a map of codepoint -> colorIndex for all codepoints in each range
|
||||
const codePointColorMap = new Map<number, number>();
|
||||
let lastDefinitionEnd = start; // Track the last definition's end to stop printing there
|
||||
@@ -59,7 +59,7 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e
|
||||
|
||||
for (let row = startRow; row <= endRow; row++) {
|
||||
// Collect labels for this row with their column positions and color
|
||||
const rowLabels: { col: number; label: string; colorIndex: number }[] = [];
|
||||
const rowLabels: { col: number, label: string, colorIndex: number }[] = [];
|
||||
for (let col = 0; col < 16; col++) {
|
||||
const codePoint = row * 16 + col;
|
||||
const labelInfo = labelStartMap.get(codePoint);
|
||||
@@ -71,7 +71,8 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e
|
||||
// If labels exist and don't start at column 0, first output chars before first label
|
||||
if (rowLabels.length > 0 && rowLabels[0].col > 0) {
|
||||
const rowHex = row.toString(16).toUpperCase();
|
||||
term.write(bold('U+' + rowHex + 'x '));
|
||||
const rowPrefix = `U+${rowHex}x`.padEnd(8, ' ');
|
||||
term.write(bold(rowPrefix));
|
||||
for (let col = 0; col < rowLabels[0].col; col++) {
|
||||
const codePoint = row * 16 + col;
|
||||
term.write(' ');
|
||||
@@ -93,7 +94,7 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e
|
||||
if (rowLabels.length > 0) {
|
||||
// Render label lines from top to bottom (first label on top, last label closest to row)
|
||||
for (let i = 0; i < rowLabels.length; i++) {
|
||||
const prefix = ' '; // Same width as "U+1FB0x "
|
||||
const prefix = ' '.repeat(8); // Same width as "U+1FB0x "
|
||||
let line = '';
|
||||
let visualLen = 0; // Track visual length separately from string length (escape codes don't count)
|
||||
for (let col = 0; col < 16; col++) {
|
||||
@@ -117,7 +118,7 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e
|
||||
|
||||
// Row prefix (e.g., "U+1FB0x ")
|
||||
const rowHex = row.toString(16).toUpperCase();
|
||||
const rowPrefix = 'U+' + rowHex + 'x ';
|
||||
const rowPrefix = `U+${rowHex}x`.padEnd(8, ' ');
|
||||
const isSecondPrint = rowLabels.length > 0 && rowLabels[0].col > 0;
|
||||
term.write(isSecondPrint ? ' '.repeat(rowPrefix.length) : bold(rowPrefix));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user