From 51121f19135986701eed1abd5f13040c55a304b9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 13:42:38 -0800 Subject: [PATCH 01/26] Stub legacy computing custom glyph ranges Part of #5466 --- .../customGlyphs/CustomGlyphDefinitions.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index ccfe43bb..1097f84f 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -489,6 +489,70 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi { x: 0, y: 2, w: 8, h: 2 }, { x: 0, y: 6, w: 8, h: 2 } ] }, + // Diagonal fill characters (1FB98-1FB99) + + // TODO: Implement + + // Smooth mosaic terminal graphic characters (1FB9A-1FB9B) + + // TODO: Implement + + // Triangular shade characters (1FB9C-1FB9F) + + // TODO: Implement + + // Character cell diagonals (1FBA0-1FBAE) + + // TODO: Implement + + // Light solid line with stroke (1FBAF-1FBAF) + + // TODO: Implement + + // Terminal graphic characters (1FBB0-1FBB3) + + // TODO: Consider implementing + + // Arrows (1FBB4-1FBB8) + + // TODO: Consider implementing + + // Terminal graphic characters (1FBB9-1FBBC) + + // TODO: Consider implementing + + // Negative terminal graphic characters (1FBBD-1FBBF) + + // TODO: Implement + + // Terminal graphic characters (1FBC0-1FBCA) + + // TODO: Consider implementing + + // Terminal graphic characters (1FBCB-1FBCD) + + // TODO: Consider implementing + + // Block elements (1FBCE-1FBCF) + + // TODO: Implementing + + // Character cell diagonals (1FBD0-1FBDF) + + // TODO: Implement + + // Geometric shapes (1FBE0-1FBEF) + + // TODO: Implement + + // Segmented digits (1FBF0-1FBF9) + + // TODO: Consider implementing + + // Terminal graphic character (1FBFA-1FBFA) + + // TODO: Consider implementing + // #endregion }; From 717399ceb687c98a0440e19639b5a46c0eeebc0e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:04:51 -0800 Subject: [PATCH 02/26] Implement diagonal fill chars and light solid line with stroke --- .../customGlyphs/CustomGlyphDefinitions.ts | 7 +++---- demo/client.ts | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 1097f84f..3b3968cb 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -490,8 +490,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi ] }, // Diagonal fill characters (1FB98-1FB99) - - // TODO: Implement + '\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0, 0 L1,1 M0,.25 L.75,1 M0,.5 L.5,1 M0,.75 L.25,1 M.25,0 L1,.75 M.5,0 L1,.5 M.75,0 L1,.25' } }, // UPPER LEFT TO LOWER RIGHT FILL + '\u{1FB99}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.25 L.25,0 M0,.5 L.5,0 M0,.75 L.75,0 M0,1 L1,0 M.25,1 L1,.25 M.5,1 L1,.5 M.75,1 L1,.75' } }, // UPPER RIGHT TO LOWER LEFT FILL // Smooth mosaic terminal graphic characters (1FB9A-1FB9B) @@ -506,8 +506,7 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Implement // Light solid line with stroke (1FBAF-1FBAF) - - // TODO: Implement + '\u{1FBAF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: `${Shapes.LEFT_TO_RIGHT} M.5,.35 L.5,.65` } }, // BOX DRAWINGS LIGHT HORIZONTAL WITH VERTICAL STROKE // Terminal graphic characters (1FBB0-1FBB3) diff --git a/demo/client.ts b/demo/client.ts index 6643f6be..f08cd1e3 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -828,13 +828,31 @@ function customGlyphAlignmentHandler(): void { term.write(' ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎\n\r'); term.write(' ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n\r'); term.write(' ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n\r'); - term.write('Smooth mosaic terminal graphic characters alignment tests:\x1b[33m\n\r'); + term.write('\x1b[0mSmooth mosaic terminal graphic characters alignment tests:\x1b[33m\n\r'); term.write('🭇🬼 🭈🬽 🭉🬾 🭊🬿 🭋🭀 🭁🭌 🭂🭍 🭃🭎 🭄🭏 🭅🭐 🭆🭑 🭨🭪 🭩 🭯 🭮🭬\n\r'); term.write('🭢🭗 🭣🭘 🭤🭙 🭥🭚 🭦🭛 🭒🭝 🭓🭞 🭔🭟 🭕🭠 🭖🭡 🭧🭜 🭫 🭭\n\r'); term.write(' 🭇🬼 🭉🬾 🭋🭀\n\r'); term.write('🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r'); term.write('🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); + + term.write('\x1b[0mFill tests:\x1b[34m\n\r'); + const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}']; + while (fillChars.length > 0) { + const batch = fillChars.splice(0, 10); + for (const fillChar of batch) { + term.write(`${fillChar.codePointAt(0).toString(16).toUpperCase().padEnd(5, ' ')} `); + } + term.write('\n\r'); + for (let i = 0; i < 3; i++) { + for (const fillChar of batch) { + term.write(fillChar.repeat(5)); + term.write(' '); + } + term.write('\n\r'); + } + } + term.write('\x1b[0m'); window.scrollTo(0, 0); } From bde3680cd1c64a9a26b0569080b69104028a5e4d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:15:26 -0800 Subject: [PATCH 03/26] Implement 2 more parts --- .../customGlyphs/CustomGlyphDefinitions.ts | 23 +++++++++++++++---- .../src/customGlyphs/CustomGlyphRasterizer.ts | 8 +++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 3b3968cb..14b17a79 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -490,20 +490,33 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi ] }, // Diagonal fill characters (1FB98-1FB99) - '\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0, 0 L1,1 M0,.25 L.75,1 M0,.5 L.5,1 M0,.75 L.25,1 M.25,0 L1,.75 M.5,0 L1,.5 M.75,0 L1,.25' } }, // UPPER LEFT TO LOWER RIGHT FILL + '\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L1,1 M0,.25 L.75,1 M0,.5 L.5,1 M0,.75 L.25,1 M.25,0 L1,.75 M.5,0 L1,.5 M.75,0 L1,.25' } }, // UPPER LEFT TO LOWER RIGHT FILL '\u{1FB99}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.25 L.25,0 M0,.5 L.5,0 M0,.75 L.75,0 M0,1 L1,0 M.25,1 L1,.25 M.5,1 L1,.5 M.75,1 L1,.75' } }, // UPPER RIGHT TO LOWER LEFT FILL // Smooth mosaic terminal graphic characters (1FB9A-1FB9B) - - // TODO: Implement + '\u{1FB9A}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L0,1 L1,1 L.5,.5 L1,0', type: CustomGlyphVectorType.FILL } }, // UPPER AND LOWER TRIANGULAR HALF BLOCK + '\u{1FB9B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1', type: CustomGlyphVectorType.FILL } }, // LEFT AND RIGHT TRIANGULAR HALF BLOCK // Triangular shade characters (1FB9C-1FB9F) // TODO: Implement // Character cell diagonals (1FBA0-1FBAE) - - // TODO: Implement + '\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT + '\u{1FBA1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT + '\u{1FBA2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE + '\u{1FBA3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE + '\u{1FBA5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT + '\u{1FBA7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT + '\u{1FBA8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT AND MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT AND MIDDLE LEFT TO LOWER CENTRE + '\u{1FBAA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE TO MIDDLE LEFT + '\u{1FBAB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT + '\u{1FBAC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBAD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE + '\u{1FBAE}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1 L0,.5 Z' } }, // BOX DRAWINGS LIGHT DIAGONAL DIAMOND // Light solid line with stroke (1FBAF-1FBAF) '\u{1FBAF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: `${Shapes.LEFT_TO_RIGHT} M.5,.35 L.5,.65` } }, // BOX DRAWINGS LIGHT HORIZONTAL WITH VERTICAL STROKE diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 3d59f710..549230ad 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -266,6 +266,10 @@ function drawPathDefinitionCharacterWithWeight( } for (const instruction of actualInstructions.split(' ')) { const type = instruction[0]; + if (type === 'Z') { + ctx.closePath(); + continue; + } const f = svgToCanvasInstructionMap[type]; if (!f) { console.error(`Could not find drawing instructions for "${type}"`); @@ -303,6 +307,10 @@ function drawVectorShape( ctx.lineWidth = devicePixelRatio * cssLineWidth; for (const instruction of charDefinition.d.split(' ')) { const type = instruction[0]; + if (type === 'Z') { + ctx.closePath(); + continue; + } const f = svgToCanvasInstructionMap[type]; if (!f) { console.error(`Could not find drawing instructions for "${type}"`); From 2bae212f833da8bf181b9a801a1ec5442480ed72 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 14:19:02 -0800 Subject: [PATCH 04/26] Implement Character cell diagonals (1FBD0-1FBDF) --- .../src/customGlyphs/CustomGlyphDefinitions.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 14b17a79..5725411f 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -550,8 +550,22 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Implementing // Character cell diagonals (1FBD0-1FBDF) - - // TODO: Implement + '\u{1FBD0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER LEFT + '\u{1FBD1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE LEFT + '\u{1FBD2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT + '\u{1FBD3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER RIGHT + '\u{1FBD4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER CENTRE + '\u{1FBD5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO LOWER RIGHT + '\u{1FBD6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,0 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER CENTRE + '\u{1FBD7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO LOWER LEFT + '\u{1FBD8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L.5,.5 L1,0' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE CENTRE TO UPPER RIGHT + '\u{1FBD9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,0 L.5,.5 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE CENTRE TO LOWER RIGHT + '\u{1FBDA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,1 L.5,.5 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL LOWER LEFT TO MIDDLE CENTRE TO LOWER RIGHT + '\u{1FBDB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L.5,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE CENTRE TO LOWER LEFT + '\u{1FBDC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L.5,1 L1,0' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER CENTRE TO UPPER RIGHT + '\u{1FBDD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,0 L0,.5 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE LEFT TO LOWER RIGHT + '\u{1FBDE}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,1 L.5,0 L1,1' } }, // BOX DRAWINGS LIGHT DIAGONAL LOWER LEFT TO UPPER CENTRE TO LOWER RIGHT + '\u{1FBDF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L1,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT TO LOWER LEFT // Geometric shapes (1FBE0-1FBEF) From 5a995e55959636804f2062e0b0f8aec6ad6d9b32 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:14:40 -0800 Subject: [PATCH 05/26] Implement Geometric shapes (1FBE0-1FBEF) --- .../customGlyphs/CustomGlyphDefinitions.ts | 141 +++++++++++++++++- 1 file changed, 138 insertions(+), 3 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 5725411f..b12ff1a0 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -568,12 +568,39 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBDF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L1,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT TO LOWER LEFT // Geometric shapes (1FBE0-1FBEF) - - // TODO: Implement + // Half circles (white = stroked outline) - each semicircle fits in half the cell + '\u{1FBE0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0', type: CustomGlyphVectorType.STROKE } }, // TOP JUSTIFIED LOWER HALF WHITE CIRCLE + '\u{1FBE1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1', type: CustomGlyphVectorType.STROKE } }, // RIGHT JUSTIFIED LEFT HALF WHITE CIRCLE + '\u{1FBE2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1', type: CustomGlyphVectorType.STROKE } }, // BOTTOM JUSTIFIED UPPER HALF WHITE CIRCLE + '\u{1FBE3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1', type: CustomGlyphVectorType.STROKE } }, // LEFT JUSTIFIED RIGHT HALF WHITE CIRCLE + // Quarter blocks at edges + '\u{1FBE4}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 4, h: 4 }] }, // UPPER CENTRE ONE QUARTER BLOCK + '\u{1FBE5}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 4, w: 4, h: 4 }] }, // LOWER CENTRE ONE QUARTER BLOCK + '\u{1FBE6}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 4, h: 4 }] }, // MIDDLE LEFT ONE QUARTER BLOCK + '\u{1FBE7}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 2, w: 4, h: 4 }] }, // MIDDLE RIGHT ONE QUARTER BLOCK + // Half circles (filled) - each semicircle fits in half the cell + '\u{1FBE8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP JUSTIFIED LOWER HALF BLACK CIRCLE + '\u{1FBE9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1 Z', type: CustomGlyphVectorType.FILL } }, // RIGHT JUSTIFIED LEFT HALF BLACK CIRCLE + '\u{1FBEA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM JUSTIFIED UPPER HALF BLACK CIRCLE + '\u{1FBEB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1 Z', type: CustomGlyphVectorType.FILL } }, // LEFT JUSTIFIED RIGHT HALF BLACK CIRCLE + // Quarter circles (filled) - radius 0.5 quarter circles in cell quadrants + '\u{1FBEC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L.5,0 C.5,.276,.724,.5,1,.5 Z', type: CustomGlyphVectorType.FILL } }, // TOP RIGHT JUSTIFIED LOWER LEFT QUARTER BLACK CIRCLE + '\u{1FBED}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 L.5,1 C.5,.724,.276,.5,0,.5 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM LEFT JUSTIFIED UPPER RIGHT QUARTER BLACK CIRCLE + '\u{1FBEE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,1 L1,.5 C.724,.5,.5,.724,.5,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM RIGHT JUSTIFIED UPPER LEFT QUARTER BLACK CIRCLE + '\u{1FBEF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L0,.5 C.276,.5,.5,.276,.5,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE // Segmented digits (1FBF0-1FBF9) - // TODO: Consider implementing + '\u{1FBF0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111110), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT ZERO (abcdef) + '\u{1FBF1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b0110000), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT ONE (bc) + '\u{1FBF2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1101101), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT TWO (abdeg) + '\u{1FBF3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111001), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT THREE (abcdg) + '\u{1FBF4}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b0110011), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT FOUR (bcfg) + '\u{1FBF5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1011011), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT FIVE (acdfg) + '\u{1FBF6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1011111), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT SIX (acdefg) + '\u{1FBF7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1110010), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT SEVEN (abcf) + '\u{1FBF8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111111), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT EIGHT (abcdefg) + '\u{1FBF9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111011), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT NINE (abcdfg) // Terminal graphic character (1FBFA-1FBFA) @@ -618,6 +645,114 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI }; } +/** + * Generates SVG path data for a 7-segment display digit. + * Segment mapping (bit positions): + * - bit 6: a (top) + * - bit 5: b (upper right) + * - bit 4: c (lower right) + * - bit 3: d (bottom) + * - bit 2: e (lower left) + * - bit 1: f (upper left) + * - bit 0: g (middle) + * + * ``` + * ─a─ + * │ │ + * f b + * ─g─ + * e c + * │ │ + * ─d─ + * ``` + */ +function segmentedDigit(pattern: number): string { + const paths: string[] = []; + + // Dimensions (in normalized 0-1 coordinates) + // Terminal cells are typically ~2:1 (height:width), so we use different values + // for horizontal vs vertical to make segments appear the same thickness + const segW = 0.15; // Width of vertical segments (fraction of cell width) + const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) + const padX = 0.05; // Horizontal padding from edge + const padY = 0.175; // Vertical padding from edge (35% total = 65% height) + const gap = 0.015; // Gap between segments + const taperX = segW / 2; // Horizontal taper for vertical segments + const taperY = segH / 2; // Vertical taper for horizontal segments + + const left = padX; + const right = 1 - padX; + const top = padY; + const bottom = 1 - padY; + const midY = 0.5; + + // Segment a (top horizontal) - hexagonal with pointed left/right ends + if (pattern & 0b1000000) { + const y1 = top; + const y2 = top + segH / 2; + const y3 = top + segH; + const x1 = left + segW + gap; + const x2 = right - segW - gap; + paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); + } + // Segment b (upper right vertical) - hexagonal with pointed top/bottom ends + if (pattern & 0b0100000) { + const x1 = right - segW; + const x2 = right - segW / 2; + const x3 = right; + const y1 = top + segH + gap; + const y2 = midY - gap; + paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); + } + // Segment c (lower right vertical) - hexagonal with pointed top/bottom ends + if (pattern & 0b0010000) { + const x1 = right - segW; + const x2 = right - segW / 2; + const x3 = right; + const y1 = midY + gap; + const y2 = bottom - segH - gap; + paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); + } + // Segment d (bottom horizontal) - hexagonal with pointed left/right ends + if (pattern & 0b0001000) { + const y1 = bottom - segH; + const y2 = bottom - segH / 2; + const y3 = bottom; + const x1 = left + segW + gap; + const x2 = right - segW - gap; + paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); + } + // Segment e (lower left vertical) - hexagonal with pointed top/bottom ends + if (pattern & 0b0000100) { + const x1 = left; + const x2 = left + segW / 2; + const x3 = left + segW; + const y1 = midY + gap; + const y2 = bottom - segH - gap; + paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); + } + // Segment f (upper left vertical) - hexagonal with pointed top/bottom ends + if (pattern & 0b0000010) { + const x1 = left; + const x2 = left + segW / 2; + const x3 = left + segW; + const y1 = top + segH + gap; + const y2 = midY - gap; + paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); + } + // Segment g (middle horizontal) - hexagonal with pointed left/right ends + if (pattern & 0b0000001) { + const y1 = midY - segH / 2; + const y2 = midY; + const y3 = midY + segH / 2; + const x1 = left + segW + gap; + const x2 = right - segW - gap; + paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); + } + + return paths.join(' '); +} + const enum Shapes { /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1', /** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5', From f5edba0b64b66a1bdb255e0708e7578af6bbff42 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:18:42 -0800 Subject: [PATCH 06/26] Improve docs on segmentedDigit --- .../addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index b12ff1a0..8c094148 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -647,7 +647,9 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI /** * Generates SVG path data for a 7-segment display digit. + * * Segment mapping (bit positions): + * * - bit 6: a (top) * - bit 5: b (upper right) * - bit 4: c (lower right) @@ -669,8 +671,9 @@ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTI function segmentedDigit(pattern: number): string { const paths: string[] = []; - // Dimensions (in normalized 0-1 coordinates) - // Terminal cells are typically ~2:1 (height:width), so we use different values + // Each segment should have approximately the same stroke width, this is somewhat difficult to be + // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the + // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. // for horizontal vs vertical to make segments appear the same thickness const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) From e12aef92876aaef4f126f5f61b62da4c7175dd14 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:21:10 -0800 Subject: [PATCH 07/26] Implement Block elements (1FBCE-1FBCF) --- addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 8c094148..da6e2837 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -546,8 +546,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Consider implementing // Block elements (1FBCE-1FBCF) - - // TODO: Implementing + '\u{1FBCE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.6667,0 L0.6667,1 L0,1 Z' }, // LEFT TWO THIRDS BLOCK + '\u{1FBCF}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.3333,0 L0.3333,1 L0,1 Z' }, // LEFT ONE THIRD BLOCK // Character cell diagonals (1FBD0-1FBDF) '\u{1FBD0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER LEFT From 1c3813f3d43058fade16383158f403f215e69ad6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:28:51 -0800 Subject: [PATCH 08/26] Implement Negative terminal graphic characters (1FBBD-1FBBF) --- .../addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index da6e2837..1a77d86f 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -534,8 +534,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Consider implementing // Negative terminal graphic characters (1FBBD-1FBBF) - - // TODO: Implement + '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH, data: 'M.07,0 L.93,0 L.5,.43 Z M1,.07 L1,.93 L.57,.5 Z M.93,1 L.07,1 L.5,.57 Z M0,.93 L0,.07 L.43,.5 Z' }, // NEGATIVE DIAGONAL CROSS + '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.43 L.43,1 L0,1 Z M1,.57 L.57,1 L1,1 Z' }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L.43,0 L0,.43 Z M.57,0 L1,0 L1,.43 Z M1,.57 L1,1 L.57,1 Z M0,.57 L.43,1 L0,1 Z M.5,.07 L.93,.5 L.5,.93 L.07,.5 Z' }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) From e1803ed5a587806d982443dd404e6eb9932874e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:30:55 -0800 Subject: [PATCH 09/26] Implement Triangular shade characters (1FB9C-1FB9F) --- .../customGlyphs/CustomGlyphDefinitions.ts | 17 ++++++- .../src/customGlyphs/CustomGlyphRasterizer.ts | 46 +++++++++++++++++++ addons/addon-webgl/src/customGlyphs/Types.ts | 2 + 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 1a77d86f..601ff6a7 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -499,7 +499,22 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Triangular shade characters (1FB9C-1FB9F) - // TODO: Implement + '\u{1FB9C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // UPPER LEFT TRIANGULAR MEDIUM SHADE + [1, 0], + [0, 1] + ], 'M0,0 L1,0 L0,1 Z'] }, + '\u{1FB9D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // UPPER RIGHT TRIANGULAR MEDIUM SHADE + [1, 0], + [0, 1] + ], 'M0,0 L1,0 L1,1 Z'] }, + '\u{1FB9E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // LOWER RIGHT TRIANGULAR MEDIUM SHADE + [1, 0], + [0, 1] + ], 'M1,0 L1,1 L0,1 Z'] }, + '\u{1FB9F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // LOWER LEFT TRIANGULAR MEDIUM SHADE + [1, 0], + [0, 1] + ], 'M0,0 L1,1 L0,1 Z'] }, // Character cell diagonals (1FBA0-1FBAE) '\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 549230ad..495bc70e 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -37,6 +37,9 @@ export function tryDrawCustomGlyph( drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data.pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight); drawBlockVectorChar(ctx, unifiedCharDefinition.data.vectors, xOffset, yOffset, deviceCellWidth, deviceCellHeight); return true; + case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH: + drawBlockPatternWithClipPath(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + return true; case CustomGlyphDefinitionType.PATH_FUNCTION: case CustomGlyphDefinitionType.PATH: drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); @@ -286,6 +289,49 @@ function drawPathDefinitionCharacterWithWeight( } } +/** + * Draws a pattern clipped to an arbitrary path (for triangular shades, etc.) + */ +function drawBlockPatternWithClipPath( + ctx: CanvasRenderingContext2D, + definition: [pattern: CustomGlyphPatternDefinition, clipPath: string], + xOffset: number, + yOffset: number, + deviceCellWidth: number, + deviceCellHeight: number +): void { + const [pattern, clipPath] = definition; + + ctx.save(); + + // Build clip path from SVG-like instructions + ctx.beginPath(); + for (const instruction of clipPath.split(' ')) { + const type = instruction[0]; + if (type === 'Z') { + ctx.closePath(); + continue; + } + const args: string[] = instruction.substring(1).split(','); + if (!args[0] || !args[1]) { + continue; + } + const x = xOffset + parseFloat(args[0]) * deviceCellWidth; + const y = yOffset + parseFloat(args[1]) * deviceCellHeight; + if (type === 'M') { + ctx.moveTo(x, y); + } else if (type === 'L') { + ctx.lineTo(x, y); + } + } + ctx.clip(); + + // Draw the pattern + drawPatternChar(ctx, pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight); + + ctx.restore(); +} + function drawVectorShape( ctx: CanvasRenderingContext2D, charDefinition: ICustomGlyphVectorShape, diff --git a/addons/addon-webgl/src/customGlyphs/Types.ts b/addons/addon-webgl/src/customGlyphs/Types.ts index bfe5155a..4623ea46 100644 --- a/addons/addon-webgl/src/customGlyphs/Types.ts +++ b/addons/addon-webgl/src/customGlyphs/Types.ts @@ -35,6 +35,7 @@ export const enum CustomGlyphDefinitionType { BLOCK_PATTERN, BLOCK_PATTERN_WITH_REGION, BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, + BLOCK_PATTERN_WITH_CLIP_PATH, PATH_FUNCTION, PATH_FUNCTION_WITH_WEIGHT, PATH, @@ -50,6 +51,7 @@ export type CustomGlyphCharacterDefinition = ( // 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.BLOCK_PATTERN_WITH_CLIP_PATH, data: [pattern: CustomGlyphPatternDefinition, clipPath: string] } | { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } | { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [fontWeight: number]: string | CustomGlyphPathDrawFunctionDefinition } } | { type: CustomGlyphDefinitionType.PATH, data: string } | From 66c1316f874fe9a2dc0d3ce7d5a5a91aaae2438c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 15:43:53 -0800 Subject: [PATCH 10/26] Implement ARROWHEAD-SHAPED POINTER --- addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 601ff6a7..61b6b659 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -537,8 +537,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBAF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: `${Shapes.LEFT_TO_RIGHT} M.5,.35 L.5,.65` } }, // BOX DRAWINGS LIGHT HORIZONTAL WITH VERTICAL STROKE // Terminal graphic characters (1FBB0-1FBB3) - - // TODO: Consider implementing + '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z' }, // ARROWHEAD-SHAPED POINTER + // TODO: Consider implementing rest // Arrows (1FBB4-1FBB8) From e8dcf46f39c9ea1a961cec35732337a5d02d1890 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:17:02 -0800 Subject: [PATCH 11/26] Add PATH_NEGATIVE, simplify 1FBBD-1FBBF --- addons/addon-webgl/src/TextureAtlas.ts | 2 +- .../customGlyphs/CustomGlyphDefinitions.ts | 6 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 75 ++++++++++++++++++- addons/addon-webgl/src/customGlyphs/Types.ts | 2 + 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/addons/addon-webgl/src/TextureAtlas.ts b/addons/addon-webgl/src/TextureAtlas.ts index a5779afc..07488dd6 100644 --- a/addons/addon-webgl/src/TextureAtlas.ts +++ b/addons/addon-webgl/src/TextureAtlas.ts @@ -514,7 +514,7 @@ export class TextureAtlas implements ITextureAtlas { // Draw custom characters if applicable let customGlyph = false; if (this._config.customGlyphs !== false) { - customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.fontSize, this._config.devicePixelRatio); + customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css); } // Whether to clear pixels based on a threshold difference between the glyph color and the diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 61b6b659..e062d5d6 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -549,9 +549,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // TODO: Consider implementing // Negative terminal graphic characters (1FBBD-1FBBF) - '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH, data: 'M.07,0 L.93,0 L.5,.43 Z M1,.07 L1,.93 L.57,.5 Z M.93,1 L.07,1 L.5,.57 Z M0,.93 L0,.07 L.43,.5 Z' }, // NEGATIVE DIAGONAL CROSS - '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.43 L.43,1 L0,1 Z M1,.57 L.57,1 L1,1 Z' }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L.43,0 L0,.43 Z M.57,0 L1,0 L1,.43 Z M1,.57 L1,1 L.57,1 Z M0,.57 L.43,1 L0,1 Z M.5,.07 L.93,.5 L.5,.93 L.07,.5 Z' }, // NEGATIVE DIAGONAL DIAMOND + '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS + '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M1,.5 L.5,1', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 495bc70e..41994760 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -19,7 +19,8 @@ export function tryDrawCustomGlyph( deviceCellWidth: number, deviceCellHeight: number, fontSize: number, - devicePixelRatio: number + devicePixelRatio: number, + backgroundColor?: string ): boolean { const unifiedCharDefinition = customGlyphDefinitions[c]; if (unifiedCharDefinition) { @@ -44,6 +45,9 @@ export function tryDrawCustomGlyph( case CustomGlyphDefinitionType.PATH: drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight); return true; + case CustomGlyphDefinitionType.PATH_NEGATIVE: + drawPathNegativeDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, backgroundColor); + return true; case CustomGlyphDefinitionType.VECTOR_SHAPE: drawVectorShape(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio); return true; @@ -111,6 +115,75 @@ function drawPathDefinitionCharacter( ctx.fill(); } +/** + * Draws a "negative" path where the background color is used to draw the shape on top of a + * foreground-filled cell. This creates the appearance of a cutout without using actual + * transparency, which allows SPAA (subpixel anti-aliasing) to work correctly. + * + * @param ctx The canvas rendering context (fillStyle should be set to foreground color) + * @param charDefinition The vector shape definition for the negative shape + * @param xOffset The x offset to draw at + * @param yOffset The y offset to draw at + * @param deviceCellWidth The width of the cell in device pixels + * @param deviceCellHeight The height of the cell in device pixels + * @param devicePixelRatio The device pixel ratio + * @param backgroundColor The background color to use for the "cutout" portion + */ +function drawPathNegativeDefinitionCharacter( + ctx: CanvasRenderingContext2D, + charDefinition: ICustomGlyphVectorShape, + xOffset: number, + yOffset: number, + deviceCellWidth: number, + deviceCellHeight: number, + devicePixelRatio: number, + backgroundColor?: string +): void { + ctx.save(); + + // First, fill the entire cell with foreground color + ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); + + // Then draw the "negative" shape with the background color + if (backgroundColor) { + ctx.fillStyle = backgroundColor; + ctx.strokeStyle = backgroundColor; + } + + ctx.lineWidth = devicePixelRatio; + ctx.lineCap = 'square'; + ctx.beginPath(); + for (const instruction of charDefinition.d.split(' ')) { + const type = instruction[0]; + const args: string[] = instruction.substring(1).split(','); + if (!args[0] || !args[1]) { + if (type === 'Z') { + ctx.closePath(); + } + continue; + } + const translatedArgs = args.map((e, i) => { + const val = parseFloat(e); + return i % 2 === 0 + ? xOffset + val * deviceCellWidth + : yOffset + val * deviceCellHeight; + }); + if (type === 'M') { + ctx.moveTo(translatedArgs[0], translatedArgs[1]); + } else if (type === 'L') { + ctx.lineTo(translatedArgs[0], translatedArgs[1]); + } + } + + if (charDefinition.type === CustomGlyphVectorType.STROKE) { + ctx.stroke(); + } else { + ctx.fill(); + } + + ctx.restore(); +} + const cachedPatterns: Map> = new Map(); function drawPatternChar( diff --git a/addons/addon-webgl/src/customGlyphs/Types.ts b/addons/addon-webgl/src/customGlyphs/Types.ts index 4623ea46..0cc8bdc8 100644 --- a/addons/addon-webgl/src/customGlyphs/Types.ts +++ b/addons/addon-webgl/src/customGlyphs/Types.ts @@ -39,6 +39,7 @@ export const enum CustomGlyphDefinitionType { PATH_FUNCTION, PATH_FUNCTION_WITH_WEIGHT, PATH, + PATH_NEGATIVE, VECTOR_SHAPE, } @@ -55,5 +56,6 @@ export type CustomGlyphCharacterDefinition = ( { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } | { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [fontWeight: number]: string | CustomGlyphPathDrawFunctionDefinition } } | { type: CustomGlyphDefinitionType.PATH, data: string } | + { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: ICustomGlyphVectorShape } | { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape } ); From 29d91fe26427f17158753ca0c618ec1bf266aee6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:27:41 -0800 Subject: [PATCH 12/26] Fill in remaining parts of legacy computing --- .../customGlyphs/CustomGlyphDefinitions.ts | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index e062d5d6..a1323849 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -538,15 +538,22 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Terminal graphic characters (1FBB0-1FBB3) '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z' }, // ARROWHEAD-SHAPED POINTER - // TODO: Consider implementing rest + '\u{1FBB1}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.1,.55 L.35,.85 L.9,.2', type: CustomGlyphVectorType.STROKE } }, // INVERSE CHECK MARK + // 1FBB2 LEFT HALF RUNNING MAN + // 1FBB3 RIGHT HALF RUNNING MAN // Arrows (1FBB4-1FBB8) - - // TODO: Consider implementing + // 1FBB4 INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS + // 1FBB5 LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK + // 1FBB6 RIGHTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK + // 1FBB7 DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK + // 1FBB8 UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK // Terminal graphic characters (1FBB9-1FBBC) - - // TODO: Consider implementing + // 1FBB9 LEFT HALF FOLDER + // 1FBBA RIGHT HALF FOLDER + // 1FBBB VOIDED GREEK CROSS + // 1FBBC RIGHT OPEN SQUARED DOT // Negative terminal graphic characters (1FBBD-1FBBF) '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS @@ -554,12 +561,22 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) - - // TODO: Consider implementing + // 1FBC0 WHITE HEAVY SALTIRE WITH ROUNDED CORNERS + // 1FBC1 LEFT THIRD WHITE RIGHT POINTING INDEX + // 1FBC2 MIDDLE THIRD WHITE RIGHT POINTING INDEX + // 1FBC3 RIGHT THIRD WHITE RIGHT POINTING INDEX + // 1FBC4 NEGATIVE SQUARED QUESTION MARK + // 1FBC5 STICK FIGURE + // 1FBC6 STICK FIGURE WITH ARMS RAISED + // 1FBC7 STICK FIGURE LEANING LEFT + // 1FBC8 STICK FIGURE LEANING RIGHT + // 1FBC9 STICK FIGURE WITH DRESS + // 1FBCA WHITE UP-POINTING CHEVRON // Terminal graphic characters (1FBCB-1FBCD) - - // TODO: Consider implementing + // 1FBCB WHITE CROSS MARK + // 1FBCC RAISED SMALL LEFT SQUARE BRACKET + // 1FBCD BLACK SMALL UP-POINTING CHEVRON // Block elements (1FBCE-1FBCF) '\u{1FBCE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.6667,0 L0.6667,1 L0,1 Z' }, // LEFT TWO THIRDS BLOCK @@ -619,8 +636,7 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBF9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111011), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT NINE (abcdfg) // Terminal graphic character (1FBFA-1FBFA) - - // TODO: Consider implementing + // 1FBFA ALARM BELL SYMBOL // #endregion }; From eb743e0e1986b767cbb286d2908dd81cf5745a68 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:15:00 -0800 Subject: [PATCH 13/26] Implement Terminal graphic character (1FBFA-1FBFA) --- addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index a1323849..31137f95 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -636,7 +636,7 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBF9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111011), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT NINE (abcdfg) // Terminal graphic character (1FBFA-1FBFA) - // 1FBFA ALARM BELL SYMBOL + '\u{1FBFA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.5,.175 C.2,.175,.15,.305,.15,.435 L.05,.63 L.35,.63 C.35,.682,.42,.76,.5,.76 C.58,.76,.65,.682,.65,.63 L.95,.63 L.85,.435 C.85,.305,.8,.175,.5,.175 Z', type: CustomGlyphVectorType.FILL } }, // ALARM BELL SYMBOL // #endregion }; From 5c0c5539363e6201ab2fffdcbcb1e829049c5ca7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:46:35 -0800 Subject: [PATCH 14/26] Implement Arrows (1FBB4-1FBB8) --- .../src/customGlyphs/CustomGlyphDefinitions.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 31137f95..308a4df1 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -543,11 +543,11 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // 1FBB3 RIGHT HALF RUNNING MAN // Arrows (1FBB4-1FBB8) - // 1FBB4 INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS - // 1FBB5 LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK - // 1FBB6 RIGHTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK - // 1FBB7 DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK - // 1FBB8 UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK + '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.6 L.5,.4 L.5,.5 L.75,.5 L.75,.2 L.85,.2 L.85,.7 L.5,.7 L.5,.8 Z', type: CustomGlyphVectorType.FILL } }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS + '\u{1FBB5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.125 L0,.125 Z M0,.875 L1,.875 L1,1 L0,1 Z M.15,.5 L.5,.3 L.5,.4 L.85,.4 L.85,.6 L.5,.6 L.5,.7 Z', type: CustomGlyphVectorType.FILL } }, // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK + '\u{1FBB6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.125 L0,.125 Z M0,.875 L1,.875 L1,1 L0,1 Z M.85,.5 L.5,.3 L.5,.4 L.15,.4 L.15,.6 L.5,.6 L.5,.7 Z', type: CustomGlyphVectorType.FILL } }, // RIGHTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK + '\u{1FBB7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.85 L.3,.5 L.4,.5 L.4,.15 L.6,.15 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK + '\u{1FBB8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.15 L.3,.5 L.4,.5 L.4,.85 L.6,.85 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK // Terminal graphic characters (1FBB9-1FBBC) // 1FBB9 LEFT HALF FOLDER From 02a9d2b0471c89f9078eb2e3b2f15b054ceda22c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 03:30:02 -0800 Subject: [PATCH 15/26] Indicate reserved codepoints in demo --- demo/client.ts | 2 +- demo/unicodeTable.ts | 51 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index f08cd1e3..c2a51268 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -898,7 +898,7 @@ function customGlyphRangesHandler(): void { ['Block elements', 0x1FB70, 0x1FB80], ['Window title bar', 0x1FB81, 0x1FB81], ['Block elements', 0x1FB82, 0x1FB8B], - ['Rectangular shade characters', 0x1FB8C, 0x1FB94], + ['Rectangular shade characters', 0x1FB8C, 0x1FB94, [0x1FB93]], ['Fill characters', 0x1FB95, 0x1FB97], ['Diagonal fill characters', 0x1FB98, 0x1FB99], ['Smooth mosaic terminal graphic characters', 0x1FB9A, 0x1FB9B], diff --git a/demo/unicodeTable.ts b/demo/unicodeTable.ts index 23b2a9fa..3e514f75 100644 --- a/demo/unicodeTable.ts +++ b/demo/unicodeTable.ts @@ -6,6 +6,7 @@ export type UnicodeRangeDefinition = [ label: string, start: number, end: number, + reserved?: number[], ]; /** @@ -39,15 +40,23 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e const labelStartMap = new Map(); // Build a map of codepoint -> colorIndex for all codepoints in each range const codePointColorMap = new Map(); + // Build a set of reserved codepoints + const reservedSet = new Set(); let lastDefinitionEnd = start; // Track the last definition's end to stop printing there if (definitions) { for (let i = 0; i < definitions.length; i++) { - const [label, labelStart, labelEnd] = definitions[i]; + const [label, labelStart, labelEnd, reserved] = definitions[i]; labelStartMap.set(labelStart, { label, colorIndex: i }); // Map all codepoints in the range to this color for (let cp = labelStart; cp <= labelEnd; cp++) { codePointColorMap.set(cp, i); } + // Track reserved codepoints + if (reserved) { + for (const cp of reserved) { + reservedSet.add(cp); + } + } if (labelEnd > lastDefinitionEnd) { lastDefinitionEnd = labelEnd; } @@ -60,12 +69,18 @@ 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 }[] = []; + // Collect reserved codepoints for this row + const rowReserved: { col: number, colorIndex: number }[] = []; for (let col = 0; col < 16; col++) { const codePoint = row * 16 + col; const labelInfo = labelStartMap.get(codePoint); if (labelInfo) { rowLabels.push({ col, label: labelInfo.label, colorIndex: labelInfo.colorIndex }); } + if (reservedSet.has(codePoint)) { + const charColorIndex = codePointColorMap.get(codePoint); + rowReserved.push({ col, colorIndex: charColorIndex ?? -1 }); + } } // If labels exist and don't start at column 0, first output chars before first label @@ -88,6 +103,40 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e } } term.write('\n\r'); + + // Render reserved labels that appear before the first label (below the first part of row) + const earlyReserved = rowReserved.filter(r => r.col < rowLabels[0].col); + if (earlyReserved.length > 0) { + for (let i = earlyReserved.length - 1; i >= 0; i--) { + const prefix = ' '.repeat(8); + let line = ''; + let visualLen = 0; + for (let col = 0; col < rowLabels[0].col; col++) { + const colPos = col * 2 + 1; // +1 to align with character position + const reservedAtCol = earlyReserved.findIndex(r => r.col === col); + if (reservedAtCol === i) { + const padding = ' '.repeat(colPos - visualLen); + const reservedItem = earlyReserved[i]; + if (reservedItem.colorIndex >= 0) { + line += padding + color('└', reservedItem.colorIndex); + } else { + line += padding + '└'; + } + break; + } else if (reservedAtCol !== -1 && reservedAtCol < i) { + const padding = ' '.repeat(colPos - visualLen); + const prevReserved = earlyReserved[reservedAtCol]; + if (prevReserved.colorIndex >= 0) { + line += padding + color('│', prevReserved.colorIndex); + } else { + line += padding + '│'; + } + visualLen = colPos + 1; + } + } + term.write(faint(prefix + line) + '\n\r'); + } + } } // If labels exist, render them above the row From ea6385f3105d8b46cdfd9a4d111964086e0ecf27 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 03:39:37 -0800 Subject: [PATCH 16/26] Comment alignment --- .../customGlyphs/CustomGlyphDefinitions.ts | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 308a4df1..86d5f973 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -395,18 +395,18 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FB6F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L1,1 L0.5,0.5 Z' }, // LOWER TRIANGULAR ONE QUARTER BLOCK // Block elements (1FB70-1FB80) - '\u{1FB70}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 1, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-2 - '\u{1FB71}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-3 - '\u{1FB72}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 3, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-4 - '\u{1FB73}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-5 - '\u{1FB74}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 5, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-6 - '\u{1FB75}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 6, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-7 - '\u{1FB76}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 1, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-2 - '\u{1FB77}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-3 - '\u{1FB78}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 3, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-4 - '\u{1FB79}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-5 - '\u{1FB7A}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 5, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-6 - '\u{1FB7B}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 6, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-7 + '\u{1FB70}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 1, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-2 + '\u{1FB71}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-3 + '\u{1FB72}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 3, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-4 + '\u{1FB73}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-5 + '\u{1FB74}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 5, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-6 + '\u{1FB75}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 6, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-7 + '\u{1FB76}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 1, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-2 + '\u{1FB77}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-3 + '\u{1FB78}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 3, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-4 + '\u{1FB79}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-5 + '\u{1FB7A}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 5, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-6 + '\u{1FB7B}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 6, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-7 '\u{1FB7C}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }] }, // LEFT AND LOWER ONE EIGHTH BLOCK '\u{1FB7D}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }] }, // LEFT AND UPPER ONE EIGHTH BLOCK '\u{1FB7E}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }] }, // RIGHT AND UPPER ONE EIGHTH BLOCK @@ -517,37 +517,37 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi ], 'M0,0 L1,1 L0,1 Z'] }, // Character cell diagonals (1FBA0-1FBAE) - '\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT - '\u{1FBA1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT - '\u{1FBA2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE - '\u{1FBA3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBA4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE - '\u{1FBA5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBA6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT - '\u{1FBA7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT - '\u{1FBA8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT AND MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBA9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT AND MIDDLE LEFT TO LOWER CENTRE - '\u{1FBAA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE TO MIDDLE LEFT - '\u{1FBAB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT - '\u{1FBAC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBAD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE + '\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT + '\u{1FBA1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT + '\u{1FBA2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE + '\u{1FBA3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE + '\u{1FBA5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT + '\u{1FBA7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT + '\u{1FBA8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 M1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT AND MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBA9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 M0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT AND MIDDLE LEFT TO LOWER CENTRE + '\u{1FBAA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1 L0,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE TO MIDDLE LEFT + '\u{1FBAB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L0,.5 L.5,1 L1,.5' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT + '\u{1FBAC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,.5 L.5,0 L1,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE + '\u{1FBAD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M1,.5 L.5,0 L0,.5 L.5,1' } }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE '\u{1FBAE}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M.5,0 L1,.5 L.5,1 L0,.5 Z' } }, // BOX DRAWINGS LIGHT DIAGONAL DIAMOND // Light solid line with stroke (1FBAF-1FBAF) '\u{1FBAF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: `${Shapes.LEFT_TO_RIGHT} M.5,.35 L.5,.65` } }, // BOX DRAWINGS LIGHT HORIZONTAL WITH VERTICAL STROKE // Terminal graphic characters (1FBB0-1FBB3) - '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z' }, // ARROWHEAD-SHAPED POINTER + '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z' }, // ARROWHEAD-SHAPED POINTER '\u{1FBB1}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.1,.55 L.35,.85 L.9,.2', type: CustomGlyphVectorType.STROKE } }, // INVERSE CHECK MARK // 1FBB2 LEFT HALF RUNNING MAN // 1FBB3 RIGHT HALF RUNNING MAN // Arrows (1FBB4-1FBB8) - '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.6 L.5,.4 L.5,.5 L.75,.5 L.75,.2 L.85,.2 L.85,.7 L.5,.7 L.5,.8 Z', type: CustomGlyphVectorType.FILL } }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS + '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.6 L.5,.4 L.5,.5 L.75,.5 L.75,.2 L.85,.2 L.85,.7 L.5,.7 L.5,.8 Z', type: CustomGlyphVectorType.FILL } }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.125 L0,.125 Z M0,.875 L1,.875 L1,1 L0,1 Z M.15,.5 L.5,.3 L.5,.4 L.85,.4 L.85,.6 L.5,.6 L.5,.7 Z', type: CustomGlyphVectorType.FILL } }, // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK '\u{1FBB6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.125 L0,.125 Z M0,.875 L1,.875 L1,1 L0,1 Z M.85,.5 L.5,.3 L.5,.4 L.15,.4 L.15,.6 L.5,.6 L.5,.7 Z', type: CustomGlyphVectorType.FILL } }, // RIGHTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK - '\u{1FBB7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.85 L.3,.5 L.4,.5 L.4,.15 L.6,.15 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK - '\u{1FBB8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.15 L.3,.5 L.4,.5 L.4,.85 L.6,.85 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK + '\u{1FBB7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.85 L.3,.5 L.4,.5 L.4,.15 L.6,.15 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK + '\u{1FBB8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.15 L.3,.5 L.4,.5 L.4,.85 L.6,.85 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK // Terminal graphic characters (1FBB9-1FBBC) // 1FBB9 LEFT HALF FOLDER @@ -556,9 +556,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // 1FBBC RIGHT OPEN SQUARED DOT // Negative terminal graphic characters (1FBBD-1FBBF) - '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS + '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M1,.5 L.5,1', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE - '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND + '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) // 1FBC0 WHITE HEAVY SALTIRE WITH ROUNDED CORNERS From a0e658a18c31c213acd4119aac1c43932c69c9a9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 03:42:41 -0800 Subject: [PATCH 17/26] Add column number powerline symbol --- addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 86d5f973..f995e5e7 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -231,10 +231,12 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, - // L N + // LN (Line Number) '\u{E0A1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.355,.03 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: CustomGlyphVectorType.FILL } }, // Lock '\u{E0A2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.25,.94 C.16,.94,.11,.92,.11,.87 L.11,.53 C.11,.48,.15,.455,.23,.45 L.23,.3 C.23,.25,.26,.22,.31,.19 C.36,.16,.43,.15,.51,.15 C.59,.15,.66,.16,.71,.19 C.77,.22,.79,.26,.79,.3 L.79,.45 C.87,.45,.91,.48,.91,.53 L.91,.87 C.91,.92,.86,.94,.77,.94 L.24,.94 M.53,.2 C.49,.2,.45,.21,.42,.23 C.39,.25,.38,.27,.38,.3 L.38,.45 L.68,.45 L.68,.3 C.68,.27,.67,.25,.64,.23 C.61,.21,.58,.2,.53,.2 M.58,.82 L.58,.66 C.63,.65,.65,.63,.65,.6 C.65,.58,.64,.57,.61,.56 C.58,.55,.56,.54,.52,.54 C.48,.54,.46,.55,.43,.56 C.4,.57,.39,.59,.39,.6 C.39,.63,.41,.64,.46,.66 L.46,.82 L.57,.82', type: CustomGlyphVectorType.FILL } }, + // CN (Column Number) + '\u{E0A3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.7,.03 L.7,.1 L.355,.1 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: CustomGlyphVectorType.FILL } }, // Right triangle solid '\u{E0B0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,.5 L0,1', type: CustomGlyphVectorType.FILL, rightPadding: 2 } }, // Right triangle line From 16b72dfd63744b338cd0fc53d7b83deab6faaf4a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 03:50:34 -0800 Subject: [PATCH 18/26] Mark powerline symbol range as reserved --- demo/client.ts | 4 ++-- demo/unicodeTable.ts | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index c2a51268..12b0a6d0 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -29,7 +29,7 @@ import { WebglAddon } from '@xterm/addon-webgl'; import { Unicode11Addon } from '@xterm/addon-unicode11'; import { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes'; -import { writeUnicodeTable, type UnicodeRangeDefinition } from './unicodeTable'; +import { writeUnicodeTable } from './unicodeTable'; export interface IWindowWithTerminal extends Window { term: typeof Terminal; @@ -886,7 +886,7 @@ function customGlyphRangesHandler(): void { // Range: E0A0–E0BF // https://github.com/ryanoasis/nerd-fonts writeUnicodeTable(term, 'Powerline Symbols', 0xE0A0, 0xE0BF, [ - ['Powerline Symbols', 0xE0A0, 0xE0B3], + ['Powerline Symbols', 0xE0A0, 0xE0B3, [0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7, 0xE0A8, 0xE0A9, 0xE0AA, 0xE0AB, 0xE0AC, 0xE0AD, 0xE0AE, 0xE0AF]], ['Powerline Extra Symbols', 0xE0B4, 0xE0BF], ]); // Symbols for Legacy Computing diff --git a/demo/unicodeTable.ts b/demo/unicodeTable.ts index 3e514f75..844edcfc 100644 --- a/demo/unicodeTable.ts +++ b/demo/unicodeTable.ts @@ -93,11 +93,15 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e term.write(' '); if (codePoint >= start && codePoint <= end) { const charColorIndex = codePointColorMap.get(codePoint); + const isReserved = reservedSet.has(codePoint); + let char = String.fromCodePoint(codePoint); if (charColorIndex !== undefined) { - term.write(color(String.fromCodePoint(codePoint), charColorIndex)); - } else { - term.write(String.fromCodePoint(codePoint)); + char = color(char, charColorIndex); } + if (isReserved) { + char = faint(char); + } + term.write(char); } else { term.write(' '); } @@ -197,16 +201,39 @@ export function writeUnicodeTable(term: Terminal, name: string, start: number, e if (codePoint >= start && codePoint <= effectiveEnd) { // Color the character if it's part of a definition range const charColorIndex = codePointColorMap.get(codePoint); + const isReserved = reservedSet.has(codePoint); + let char = String.fromCodePoint(codePoint); if (charColorIndex !== undefined) { - term.write(color(String.fromCodePoint(codePoint), charColorIndex)); - } else { - term.write(String.fromCodePoint(codePoint)); + char = color(char, charColorIndex); } + if (isReserved) { + char = faint(char); + } + term.write(char); } else { term.write(' '); } } term.write('\n\r'); + + // Render reserved labels that appear after the first label (below the row) + // Only show one label pointing to the first reserved item when there are multiple + const lateReserved = rowLabels.length > 0 + ? rowReserved.filter(r => r.col >= rowLabels[0].col) + : rowReserved; + if (lateReserved.length > 0) { + const prefix = ' '.repeat(8); + const firstReserved = lateReserved[0]; + const colPos = firstReserved.col * 2 + 1; + const padding = ' '.repeat(colPos); + let line: string; + if (firstReserved.colorIndex >= 0) { + line = padding + color('└', firstReserved.colorIndex); + } else { + line = padding + '└'; + } + term.write(faint(prefix + line) + '\n\r'); + } } } From 268f7af5db6e0325f4673a428c9968b30e132db4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 04:15:41 -0800 Subject: [PATCH 19/26] Implement running man 1FBB2 and 1FBB3 --- .../customGlyphs/CustomGlyphDefinitions.ts | 4 +- .../src/customGlyphs/CustomGlyphRasterizer.ts | 97 +++++++++++++++++++ demo/client.ts | 5 +- 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index f995e5e7..3fac87d2 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -541,8 +541,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Terminal graphic characters (1FBB0-1FBB3) '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z' }, // ARROWHEAD-SHAPED POINTER '\u{1FBB1}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.1,.55 L.35,.85 L.9,.2', type: CustomGlyphVectorType.STROKE } }, // INVERSE CHECK MARK - // 1FBB2 LEFT HALF RUNNING MAN - // 1FBB3 RIGHT HALF RUNNING MAN + '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN + '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN // Arrows (1FBB4-1FBB8) '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.6 L.5,.4 L.5,.5 L.75,.5 L.75,.2 L.85,.2 L.85,.7 L.5,.7 L.5,.8 Z', type: CustomGlyphVectorType.FILL } }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 41994760..3cda6a9c 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -91,6 +91,8 @@ function drawPathDefinitionCharacter( ): void { const instructions = typeof charDefinition === 'string' ? charDefinition : charDefinition(0, 0); ctx.beginPath(); + let currentX = 0; + let currentY = 0; for (const instruction of instructions.split(' ')) { const type = instruction[0]; const args: string[] = instruction.substring(1).split(','); @@ -100,6 +102,20 @@ function drawPathDefinitionCharacter( } continue; } + if (type === 'A') { + // SVG arc: A rx,ry,xAxisRotation,largeArcFlag,sweepFlag,x,y + const rx = parseFloat(args[0]) * deviceCellWidth; + const ry = parseFloat(args[1]) * deviceCellHeight; + const xAxisRotation = parseFloat(args[2]) * Math.PI / 180; + const largeArcFlag = parseInt(args[3]); + const sweepFlag = parseInt(args[4]); + const x = xOffset + parseFloat(args[5]) * deviceCellWidth; + const y = yOffset + parseFloat(args[6]) * deviceCellHeight; + drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y); + currentX = x; + currentY = y; + continue; + } const translatedArgs = args.map((e, i) => { const val = parseFloat(e); return i % 2 === 0 @@ -108,13 +124,94 @@ function drawPathDefinitionCharacter( }); if (type === 'M') { ctx.moveTo(translatedArgs[0], translatedArgs[1]); + currentX = translatedArgs[0]; + currentY = translatedArgs[1]; } else if (type === 'L') { ctx.lineTo(translatedArgs[0], translatedArgs[1]); + currentX = translatedArgs[0]; + currentY = translatedArgs[1]; } } ctx.fill(); } +/** + * Converts SVG arc parameters to canvas arc/ellipse calls. + * Based on the SVG spec's endpoint to center parameterization conversion. + */ +function drawSvgArc( + ctx: CanvasRenderingContext2D, + x1: number, y1: number, + rx: number, ry: number, + phi: number, + largeArcFlag: number, + sweepFlag: number, + x2: number, y2: number +): void { + // Handle degenerate cases + if (rx === 0 || ry === 0) { + ctx.lineTo(x2, y2); + return; + } + + rx = Math.abs(rx); + ry = Math.abs(ry); + + const cosPhi = Math.cos(phi); + const sinPhi = Math.sin(phi); + + // Step 1: Compute (x1', y1') + const dx = (x1 - x2) / 2; + const dy = (y1 - y2) / 2; + const x1p = cosPhi * dx + sinPhi * dy; + const y1p = -sinPhi * dx + cosPhi * dy; + + // Step 2: Compute (cx', cy') + let rxSq = rx * rx; + let rySq = ry * ry; + const x1pSq = x1p * x1p; + const y1pSq = y1p * y1p; + + // Correct radii if necessary + const lambda = x1pSq / rxSq + y1pSq / rySq; + if (lambda > 1) { + const lambdaSqrt = Math.sqrt(lambda); + rx *= lambdaSqrt; + ry *= lambdaSqrt; + rxSq = rx * rx; + rySq = ry * ry; + } + + let sq = (rxSq * rySq - rxSq * y1pSq - rySq * x1pSq) / (rxSq * y1pSq + rySq * x1pSq); + if (sq < 0) sq = 0; + const coef = (largeArcFlag === sweepFlag ? -1 : 1) * Math.sqrt(sq); + const cxp = coef * (rx * y1p / ry); + const cyp = coef * -(ry * x1p / rx); + + // Step 3: Compute (cx, cy) from (cx', cy') + const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2; + const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2; + + // Step 4: Compute angles + const ux = (x1p - cxp) / rx; + const uy = (y1p - cyp) / ry; + const vx = (-x1p - cxp) / rx; + const vy = (-y1p - cyp) / ry; + + const startAngle = Math.atan2(uy, ux); + let dTheta = Math.atan2(vy, vx) - startAngle; + + if (sweepFlag === 0 && dTheta > 0) { + dTheta -= 2 * Math.PI; + } else if (sweepFlag === 1 && dTheta < 0) { + dTheta += 2 * Math.PI; + } + + const endAngle = startAngle + dTheta; + + ctx.ellipse(cx, cy, rx, ry, phi, startAngle, endAngle, sweepFlag === 0); +} + /** * Draws a "negative" path where the background color is used to draw the shape on top of a * foreground-filled cell. This creates the appearance of a cutout without using actual diff --git a/demo/client.ts b/demo/client.ts index 12b0a6d0..9bcf4388 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -835,8 +835,9 @@ function customGlyphAlignmentHandler(): void { term.write('🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r'); term.write('🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); - - term.write('\x1b[0mFill tests:\x1b[34m\n\r'); + term.write('\x1b[0mComposite characters:\x1b[34m\n\r'); + term.write('\u{1FBB2}\u{1FBB3}\n\r'); + term.write('\x1b[0mFill tests:\x1b[35m\n\r'); const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}']; while (fillChars.length > 0) { const batch = fillChars.splice(0, 10); From 3d3abf9cf26fc7dedf10178719af7504dce0e77a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 04:22:19 -0800 Subject: [PATCH 20/26] Implement folder 1FBB9-1FBBA --- addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 4 ++-- demo/client.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 3fac87d2..33371614 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -552,8 +552,8 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBB8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z M.5,.15 L.3,.5 L.4,.5 L.4,.85 L.6,.85 L.6,.5 L.7,.5 Z', type: CustomGlyphVectorType.FILL } }, // UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK // Terminal graphic characters (1FBB9-1FBBC) - // 1FBB9 LEFT HALF FOLDER - // 1FBBA RIGHT HALF FOLDER + '\u{1FBB9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,.89 L.11,.89 L.11,.37 L.36,.12 L.74,.12 L.96,.34 L1,.34 L1,.45 L.92,.45 L.69,.22 L.41,.22 L.21,.42 L.21,.79 L1,.79 Z', type: CustomGlyphVectorType.FILL } }, // LEFT HALF FOLDER + '\u{1FBBA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,.89 L0,.79 L.78,.79 L.78,.53 L.7,.45 L0,.45 L0,.35 L.75,.35 L.88,.48 L.88,.89 Z', type: CustomGlyphVectorType.FILL } }, // 1FBBA RIGHT HALF FOLDER // 1FBBB VOIDED GREEK CROSS // 1FBBC RIGHT OPEN SQUARED DOT diff --git a/demo/client.ts b/demo/client.ts index 9bcf4388..a6e33b20 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -835,8 +835,8 @@ function customGlyphAlignmentHandler(): void { term.write('🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r'); term.write('🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); - term.write('\x1b[0mComposite characters:\x1b[34m\n\r'); - term.write('\u{1FBB2}\u{1FBB3}\n\r'); + term.write('\x1b[0mComposite terminal graphics characters:\x1b[34m\n\r'); + term.write('\u{1FBB2}\u{1FBB3} \u{1FBB9}\u{1FBBA}\n\r'); term.write('\x1b[0mFill tests:\x1b[35m\n\r'); const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}']; while (fillChars.length > 0) { From 44d8d2c8737382e32b088e295975e58e1d13d91b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 04:28:45 -0800 Subject: [PATCH 21/26] Finish implementing Terminal graphic characters (1FBB9-1FBBC) --- .../addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 33371614..fffa2577 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -553,9 +553,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Terminal graphic characters (1FBB9-1FBBC) '\u{1FBB9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,.89 L.11,.89 L.11,.37 L.36,.12 L.74,.12 L.96,.34 L1,.34 L1,.45 L.92,.45 L.69,.22 L.41,.22 L.21,.42 L.21,.79 L1,.79 Z', type: CustomGlyphVectorType.FILL } }, // LEFT HALF FOLDER - '\u{1FBBA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,.89 L0,.79 L.78,.79 L.78,.53 L.7,.45 L0,.45 L0,.35 L.75,.35 L.88,.48 L.88,.89 Z', type: CustomGlyphVectorType.FILL } }, // 1FBBA RIGHT HALF FOLDER - // 1FBBB VOIDED GREEK CROSS - // 1FBBC RIGHT OPEN SQUARED DOT + '\u{1FBBA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,.89 L0,.79 L.78,.79 L.78,.53 L.7,.45 L0,.45 L0,.35 L.75,.35 L.88,.48 L.88,.89 Z', type: CustomGlyphVectorType.FILL } }, // RIGHT HALF FOLDER + '\u{1FBBB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.31,.05 L.44,.05 L.44,.44 L.05,.44 L.05,.31 L.31,.31 Z M.56,.05 L.69,.05 L.69,.31 L.95,.31 L.95,.44 L.56,.44 Z M.05,.56 L.44,.56 L.44,.95 L.31,.95 L.31,.69 L.05,.69 Z M.56,.56 L.95,.56 L.95,.69 L.69,.69 L.69,.95 L.56,.95 Z', type: CustomGlyphVectorType.FILL } }, // VOIDED GREEK CROSS + '\u{1FBBC}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,1 L0,1 L0,.87 L.87,.87 L.87,.13 L0,.13 Z M.67,.5 A.17,.17,0,1,1,.33,.5 A.17,.17,0,1,1,.67,.5' }, // RIGHT OPEN SQUARED DOT // Negative terminal graphic characters (1FBBD-1FBBF) '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS From b8843732bf3eaf46d90f1f760cf63ea80d2f0cab Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 04:49:46 -0800 Subject: [PATCH 22/26] Implement Terminal graphic characters (1FBCB-1FBCD) --- .../addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index fffa2577..30e2525e 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -576,9 +576,9 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // 1FBCA WHITE UP-POINTING CHEVRON // Terminal graphic characters (1FBCB-1FBCD) - // 1FBCB WHITE CROSS MARK - // 1FBCC RAISED SMALL LEFT SQUARE BRACKET - // 1FBCD BLACK SMALL UP-POINTING CHEVRON + '\u{1FBCB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.09,.32 L.32,.09 L.5,.25 L.68,.09 L.91,.32 L.75,.5 L.91,.68 L.68,.91 L.5,.75 L.32,.91 L.09,.68 L.25,.5 Z M.24,.32 L.39,.5 L.24,.68 L.32,.76 L.5,.61 L.68,.76 L.76,.68 L.61,.5 L.76,.32 L.68,.24 L.5,.39 L.32,.24 Z', type: CustomGlyphVectorType.FILL } }, // WHITE CROSS MARK + '\u{1FBCC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.55,.11 L.88,.11 L.88,.21 L.65,.21 L.65,.44, L.88,.44 L.88,.54 L.55,.54 Z', type: CustomGlyphVectorType.FILL } }, // RAISED SMALL LEFT SQUARE BRACKET + '\u{1FBCD}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.38,.28 L.5,.16 L.62,.28 L.62,.56 L.5,.44 L.38,.56 Z', type: CustomGlyphVectorType.FILL } }, // BLACK SMALL UP-POINTING CHEVRON // Block elements (1FBCE-1FBCF) '\u{1FBCE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.6667,0 L0.6667,1 L0,1 Z' }, // LEFT TWO THIRDS BLOCK From 884b667f4e1e69948cfeb6a8ef69bfe7a8f9691a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 06:46:05 -0800 Subject: [PATCH 23/26] Implement stick figures 1FBC5-1FBC9 --- .../customGlyphs/CustomGlyphDefinitions.ts | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 30e2525e..24c78fb9 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -500,7 +500,6 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FB9B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1', type: CustomGlyphVectorType.FILL } }, // LEFT AND RIGHT TRIANGULAR HALF BLOCK // Triangular shade characters (1FB9C-1FB9F) - '\u{1FB9C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [[ // UPPER LEFT TRIANGULAR MEDIUM SHADE [1, 0], [0, 1] @@ -563,17 +562,17 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) - // 1FBC0 WHITE HEAVY SALTIRE WITH ROUNDED CORNERS + '\u{1FBC0}': { type: CustomGlyphDefinitionType.PATH, data: 'M.16,.39 A.02,.02,0,0,1,.39,.16 L.5,.25 L.61,.16 A.02,.02,0,0,1,.84,.39 L.75,.5 L.84,.61 A.02,.02,0,0,1,.61,.84 L.5,.75 L.39,.84 A.02,.02,0,0,1,.16,.61 L.25,.5 Z M.24,.32 L.39,.5 L.24,.68 L.32,.76 L.5,.61 L.68,.76 L.76,.68 L.61,.5 L.76,.32 L.68,.24 L.5,.39 L.32,.24 Z' }, // WHITE HEAVY SALTIRE WITH ROUNDED CORNERS // 1FBC1 LEFT THIRD WHITE RIGHT POINTING INDEX // 1FBC2 MIDDLE THIRD WHITE RIGHT POINTING INDEX // 1FBC3 RIGHT THIRD WHITE RIGHT POINTING INDEX // 1FBC4 NEGATIVE SQUARED QUESTION MARK - // 1FBC5 STICK FIGURE - // 1FBC6 STICK FIGURE WITH ARMS RAISED - // 1FBC7 STICK FIGURE LEANING LEFT - // 1FBC8 STICK FIGURE LEANING RIGHT - // 1FBC9 STICK FIGURE WITH DRESS - // 1FBCA WHITE UP-POINTING CHEVRON + '\u{1FBC5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.52 L.44,.62 L.19,.87 L.26,.94 L.5,.71 L.74,.94 L.81,.87 L.56,.62 L.56,.52 L.56,.35 Z M.17,.42 L.17,.52 L.83,.52 L.83,.42 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE + '\u{1FBC6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.42 L.23,.27 L.19,.36 L.44,.52 L.44,.62 L.29,.92 L.38,.97 L.5,.71 L.61,.97 L.7,.92 L.56,.62 L.56,.52 L.81,.36 L.77,.27 L.56,.42 L.56,.35 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE WITH ARMS RAISED + '\u{1FBC7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.62 L.29,.92 L.38,.97 L.5,.71 L.74,.94 L.81,.87 L.56,.62 L.56,.35 Z M.18,.56 L.23,.65 L.81,.36 L.77,.27 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE LEANING LEFT + '\u{1FBC8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.62 L.19,.87 L.26,.94 L.5,.71 L.62,.97 L.71,.92 L.56,.62 L.56,.35 Z M.23,.27 L.18,.36 L.77,.65 L.81,.56 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE LEANING RIGHT + '\u{1FBC9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.45,.49 L.15,.79 L.34,.79 L.34,.9 L.44,.9 L.44,.79 L.56,.79 L.56,.9 L.66,.9 L.66,.79 L.84,.79 L.54,.49 L.56,.35 Z M.39,.7 L.5,.6 L.60,.7 Z M.17,.42 L.17,.52 L.83,.52 L.83,.42 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE WITH DRESS + '\u{1FBCA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.26,.25 L.5,.01 L.74,.25 L.74,.83 L.5,.6 L.26,.83 Z M.37,.29 L.37,.58 L.5,.45 L.63,.58 L.63,.29 L.5,.16 Z', type: CustomGlyphVectorType.FILL } }, // WHITE UP-POINTING CHEVRON // Terminal graphic characters (1FBCB-1FBCD) '\u{1FBCB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.09,.32 L.32,.09 L.5,.25 L.68,.09 L.91,.32 L.75,.5 L.91,.68 L.68,.91 L.5,.75 L.32,.91 L.09,.68 L.25,.5 Z M.24,.32 L.39,.5 L.24,.68 L.32,.76 L.5,.61 L.68,.76 L.76,.68 L.61,.5 L.76,.32 L.68,.24 L.5,.39 L.32,.24 Z', type: CustomGlyphVectorType.FILL } }, // WHITE CROSS MARK @@ -603,29 +602,24 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi '\u{1FBDF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [FontWeight.NORMAL]: 'M0,0 L1,.5 L0,1' } }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT TO LOWER LEFT // Geometric shapes (1FBE0-1FBEF) - // Half circles (white = stroked outline) - each semicircle fits in half the cell '\u{1FBE0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0', type: CustomGlyphVectorType.STROKE } }, // TOP JUSTIFIED LOWER HALF WHITE CIRCLE '\u{1FBE1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1', type: CustomGlyphVectorType.STROKE } }, // RIGHT JUSTIFIED LEFT HALF WHITE CIRCLE '\u{1FBE2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1', type: CustomGlyphVectorType.STROKE } }, // BOTTOM JUSTIFIED UPPER HALF WHITE CIRCLE '\u{1FBE3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1', type: CustomGlyphVectorType.STROKE } }, // LEFT JUSTIFIED RIGHT HALF WHITE CIRCLE - // Quarter blocks at edges - '\u{1FBE4}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 4, h: 4 }] }, // UPPER CENTRE ONE QUARTER BLOCK - '\u{1FBE5}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 4, w: 4, h: 4 }] }, // LOWER CENTRE ONE QUARTER BLOCK - '\u{1FBE6}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 4, h: 4 }] }, // MIDDLE LEFT ONE QUARTER BLOCK - '\u{1FBE7}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 2, w: 4, h: 4 }] }, // MIDDLE RIGHT ONE QUARTER BLOCK - // Half circles (filled) - each semicircle fits in half the cell + '\u{1FBE4}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 4, h: 4 }] }, // UPPER CENTRE ONE QUARTER BLOCK + '\u{1FBE5}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 4, w: 4, h: 4 }] }, // LOWER CENTRE ONE QUARTER BLOCK + '\u{1FBE6}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 4, h: 4 }] }, // MIDDLE LEFT ONE QUARTER BLOCK + '\u{1FBE7}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 2, w: 4, h: 4 }] }, // MIDDLE RIGHT ONE QUARTER BLOCK '\u{1FBE8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP JUSTIFIED LOWER HALF BLACK CIRCLE '\u{1FBE9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1 Z', type: CustomGlyphVectorType.FILL } }, // RIGHT JUSTIFIED LEFT HALF BLACK CIRCLE '\u{1FBEA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM JUSTIFIED UPPER HALF BLACK CIRCLE '\u{1FBEB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1 Z', type: CustomGlyphVectorType.FILL } }, // LEFT JUSTIFIED RIGHT HALF BLACK CIRCLE - // Quarter circles (filled) - radius 0.5 quarter circles in cell quadrants - '\u{1FBEC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L.5,0 C.5,.276,.724,.5,1,.5 Z', type: CustomGlyphVectorType.FILL } }, // TOP RIGHT JUSTIFIED LOWER LEFT QUARTER BLACK CIRCLE - '\u{1FBED}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 L.5,1 C.5,.724,.276,.5,0,.5 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM LEFT JUSTIFIED UPPER RIGHT QUARTER BLACK CIRCLE - '\u{1FBEE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,1 L1,.5 C.724,.5,.5,.724,.5,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM RIGHT JUSTIFIED UPPER LEFT QUARTER BLACK CIRCLE - '\u{1FBEF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L0,.5 C.276,.5,.5,.276,.5,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE + '\u{1FBEC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L.5,0 C.5,.276,.724,.5,1,.5 Z', type: CustomGlyphVectorType.FILL } }, // TOP RIGHT JUSTIFIED LOWER LEFT QUARTER BLACK CIRCLE + '\u{1FBED}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 L.5,1 C.5,.724,.276,.5,0,.5 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM LEFT JUSTIFIED UPPER RIGHT QUARTER BLACK CIRCLE + '\u{1FBEE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,1 L1,.5 C.724,.5,.5,.724,.5,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM RIGHT JUSTIFIED UPPER LEFT QUARTER BLACK CIRCLE + '\u{1FBEF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L0,.5 C.276,.5,.5,.276,.5,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE // Segmented digits (1FBF0-1FBF9) - '\u{1FBF0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111110), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT ZERO (abcdef) '\u{1FBF1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b0110000), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT ONE (bc) '\u{1FBF2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1101101), type: CustomGlyphVectorType.FILL } }, // SEGMENTED DIGIT TWO (abdeg) From 0976935f87964f33a5a10efab4c5010ebe6f641c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 07:42:57 -0800 Subject: [PATCH 24/26] Implement remaining Terminal graphic characters (1FBC0-1FBCA) --- .../customGlyphs/CustomGlyphDefinitions.ts | 10 +-- .../src/customGlyphs/CustomGlyphRasterizer.ts | 65 +++++++++++++++++-- demo/client.ts | 2 +- 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts index 24c78fb9..9340212c 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts @@ -5,6 +5,7 @@ import { CustomGlyphDefinitionType, CustomGlyphVectorType, type CustomGlyphCharacterDefinition, type CustomGlyphPathDrawFunctionDefinition } from './Types'; +/* eslint-disable max-len */ /* eslint-disable @typescript-eslint/naming-convention */ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefinition | undefined } = { @@ -563,10 +564,11 @@ export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefi // Terminal graphic characters (1FBC0-1FBCA) '\u{1FBC0}': { type: CustomGlyphDefinitionType.PATH, data: 'M.16,.39 A.02,.02,0,0,1,.39,.16 L.5,.25 L.61,.16 A.02,.02,0,0,1,.84,.39 L.75,.5 L.84,.61 A.02,.02,0,0,1,.61,.84 L.5,.75 L.39,.84 A.02,.02,0,0,1,.16,.61 L.25,.5 Z M.24,.32 L.39,.5 L.24,.68 L.32,.76 L.5,.61 L.68,.76 L.76,.68 L.61,.5 L.76,.32 L.68,.24 L.5,.39 L.32,.24 Z' }, // WHITE HEAVY SALTIRE WITH ROUNDED CORNERS - // 1FBC1 LEFT THIRD WHITE RIGHT POINTING INDEX - // 1FBC2 MIDDLE THIRD WHITE RIGHT POINTING INDEX - // 1FBC3 RIGHT THIRD WHITE RIGHT POINTING INDEX - // 1FBC4 NEGATIVE SQUARED QUESTION MARK + // 1FBC1-1FBC4 is dervied from the Iosevka font (SIL OFL v1.1) https://github.com/be5invis/Iosevka + '\u{1FBC1}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.142308,0.924 Q0.130770,0.924,0.120193,0.922 T0.101924,0.916 T0.089424,0.9065 T0.084616,0.895 V0.025 Q0.084616,0.019,0.089424,0.0135 T0.101924,0.004 T0.120193,-0.002 T0.142308,-0.004 Q0.150000,-0.004,0.157693,-0.003 T0.173078,0.000 L0.430770,0.085 Q0.451924,0.059,0.494232,0.041 T0.587501,0.013 T0.692309,-0.0005 T0.800001,-0.004 H1 V0.055 H0.800001 Q0.771155,0.055,0.742309,0.056 T0.685578,0.060 T0.630770,0.0685 T0.580770,0.0825 T0.543270,0.1045 T0.528847,0.133 V0.647 H0.530770 Q0.596155,0.645,0.659616,0.638 T0.782693,0.6165 T0.893270,0.5805 T1,0.531 V0.623 Q0.934616,0.644,0.880770,0.6595 T0.769232,0.685 T0.650963,0.700 T0.528847,0.707 V0.787 Q0.528847,0.802,0.543270,0.8155 T0.580770,0.8375 T0.630770,0.8515 T0.685578,0.860 T0.742309,0.864 T0.800001,0.865 H1 V0.924 H0.800001 Q0.746155,0.924,0.692309,0.9205 T0.587501,0.907 T0.494232,0.879 T0.430770,0.835 L0.173078,0.920 Q0.165386,0.922,0.157693,0.923 T0.142308,0.924 Z M0.2,0.841 L0.415385,0.770 V0.150 L0.2,0.079 V0.841 Z M1,0.698 Q0.973077,0.694,0.969231,0.6885 T0.965385,0.677 Q0.965385,0.672,0.969231,0.6665 T1,0.657 V0.698 Z' }, // LEFT THIRD WHITE RIGHT POINTING INDEX + '\u{1FBC2}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.623 V0.531 Q0.044231,0.512,0.0625,0.4905 T0.092308,0.4465 T0.108654,0.4005 T0.113462,0.354 V0.351 Q0.113462,0.345,0.117308,0.3395 T0.129808,0.3305 T0.148846,0.3245 T0.171154,0.323 H0.501923 Q0.532692,0.323,0.563462,0.3185 T0.617308,0.303 T0.651923,0.2765 T0.663462,0.2435 V0.241 Q0.663462,0.219,0.657692,0.197 T0.639423,0.1545 T0.604808,0.115 T0.549615,0.082 T0.475577,0.0615 T0.392308,0.055 H0 V-0.004 H1 V0.055 H0.667308 Q0.694231,0.071,0.713462,0.09 T0.746154,0.129 T0.766346,0.1705 T0.775,0.213 H1 V0.316 Q0.971154,0.305,0.955769,0.2965 T0.920192,0.2825 T0.876923,0.2745 T0.830769,0.272 H0.773077 Q0.767308,0.297,0.743269,0.319 T0.680769,0.355 T0.595192,0.375 T0.501923,0.381 H0.227308 Q0.223462,0.415,0.211,0.4485 T0.172692,0.5135 T0.108269,0.573 T0,0.623 Z M0.611538,0.924 H0 V0.865 H0.611538 Q0.642308,0.865,0.673077,0.8605 T0.727885,0.845 T0.762308,0.8185 T0.773077,0.787 V0.785 Q0.773077,0.769,0.762308,0.7535 T0.727885,0.727 T0.673077,0.7115 T0.611538,0.707 H0.059615 Q0.048077,0.707,0.037115,0.7045 T0,0.694 V0.653 Q0.026923,0.648,0.037115,0.646 T0.059615,0.644 H0.722692 Q0.753462,0.644,0.784231,0.6395 T0.839038,0.624 T0.873846,0.5975 T0.884615,0.566 V0.564 Q0.884615,0.548,0.873846,0.532 T0.839038,0.5055 T0.784231,0.49 T0.722692,0.4855 H0.392308 Q0.380769,0.4855,0.370192,0.483 T0.351923,0.4765 T0.339423,0.467 T0.334615,0.4555 T0.339423,0.444 T0.351923,0.4345 T0.370192,0.428 T0.392308,0.4255 H0.832692 Q0.855769,0.4255,0.878846,0.4235 T0.922115,0.416 T0.957692,0.4015 T1,0.3815 V0.4685 Q0.975,0.4705,0.966346,0.4725 T0.95,0.4765 Q0.961538,0.4835,0.969231,0.4915 T1,0.5075 V0.6205 Q0.965385,0.6455,0.926923,0.665 T0.84,0.6935 Q0.866923,0.7115,0.877596,0.7345 T0.894231,0.7855 V0.787 Q0.894231,0.815,0.876923,0.8425 T0.820192,0.8895 T0.726923,0.916 T0.611538,0.924 Z' }, // MIDDLE THIRD WHITE RIGHT POINTING INDEX + '\u{1FBC3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.473 V0.386 Q0.025490,0.378,0.028431,0.3695 T0.031373,0.352 V0.35 Q0.031373,0.342,0.028431,0.333 T0,0.316 V0.213 H0.652941 Q0.684314,0.213,0.715686,0.2085 T0.770588,0.193 T0.804902,0.1665 T0.815686,0.135 V0.133 Q0.815686,0.117,0.804902,0.101 T0.770588,0.0745 T0.715686,0.0595 T0.652941,0.055 H0 V-0.004 H0.652941 Q0.707843,-0.004,0.761765,0.004 T0.856863,0.031 T0.915686,0.0775 T0.933333,0.133 V0.135 Q0.933333,0.163,0.915686,0.1905 T0.856863,0.237 T0.761765,0.264 T0.652941,0.272 H0.109804 Q0.131373,0.29,0.140196,0.31 T0.149020,0.35 V0.352 Q0.149020,0.37,0.142157,0.388 T0.118627,0.4225 T0.076471,0.452 T0,0.473 Z M0,0.625 V0.513 Q0.029412,0.526,0.032353,0.54 T0.035294,0.568 V0.57 Q0.035294,0.584,0.032353,0.598 T0,0.625 Z' }, // RIGHT THIRD WHITE RIGHT POINTING INDEX + '\u{1FBC4}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.019231,1.085 V-0.165 H0.980769 V1.085 H0.019231 Z M0.446154,0.527 H0.553846 Q0.553846,0.511,0.5625,0.495 T0.591346,0.466 T0.631731,0.4405 T0.666346,0.4135 T0.688462,0.3825 T0.696154,0.35 Q0.696154,0.33,0.682692,0.311 T0.641346,0.2785 T0.575,0.259 T0.498077,0.253 T0.421154,0.259 T0.356731,0.279 T0.315385,0.3125 T0.301923,0.352 V0.357 H0.409615 V0.354 Q0.409615,0.345,0.414423,0.335 T0.431731,0.318 T0.4625,0.3075 T0.498077,0.304 Q0.517308,0.304,0.534615,0.307 T0.564423,0.3165 T0.582692,0.332 T0.588462,0.35 Q0.588462,0.366,0.572115,0.3805 T0.535577,0.4075 T0.496154,0.4335 T0.465385,0.4625 T0.45,0.4945 T0.446154,0.527 Z M0.5,0.667 Q0.519231,0.667,0.5375,0.664 T0.569231,0.654 T0.588462,0.637 T0.594231,0.617 T0.588462,0.5975 T0.569231,0.581 T0.5375,0.571 T0.5,0.568 T0.4625,0.571 T0.430769,0.581 T0.411538,0.5975 T0.405769,0.617 T0.411538,0.637 T0.430769,0.654 T0.4625,0.664 T0.5,0.667 Z' }, // NEGATIVE SQUARED QUESTION MARK '\u{1FBC5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.52 L.44,.62 L.19,.87 L.26,.94 L.5,.71 L.74,.94 L.81,.87 L.56,.62 L.56,.52 L.56,.35 Z M.17,.42 L.17,.52 L.83,.52 L.83,.42 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE '\u{1FBC6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.42 L.23,.27 L.19,.36 L.44,.52 L.44,.62 L.29,.92 L.38,.97 L.5,.71 L.61,.97 L.7,.92 L.56,.62 L.56,.52 L.81,.36 L.77,.27 L.56,.42 L.56,.35 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE WITH ARMS RAISED '\u{1FBC7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.35 L.44,.62 L.29,.92 L.38,.97 L.5,.71 L.74,.94 L.81,.87 L.56,.62 L.56,.35 Z M.18,.56 L.23,.65 L.81,.36 L.77,.27 Z M.67,.2 C.67,.106,.594,.03,.5,.03 C.406,.03,.33,.106,.33,.2 C.33,.294,.406,.37,.5,.37 C.594,.37,.67,.294,.67,.2 Z M.56,.2 C.56,.233,.533,.26,.5,.26 C.467,.26,.44,.233,.44,.2 C.44,.167,.467,.14,.5,.14 C.533,.14,.56,.167,.56,.2 Z', type: CustomGlyphVectorType.FILL } }, // STICK FIGURE LEANING LEFT diff --git a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts index 3cda6a9c..5e557016 100644 --- a/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts +++ b/addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts @@ -93,13 +93,36 @@ function drawPathDefinitionCharacter( ctx.beginPath(); let currentX = 0; let currentY = 0; + let lastControlX = 0; + let lastControlY = 0; + let lastCommand = ''; for (const instruction of instructions.split(' ')) { const type = instruction[0]; const args: string[] = instruction.substring(1).split(','); + if (type === 'Z') { + ctx.closePath(); + lastCommand = type; + continue; + } + if (type === 'V') { + const y = yOffset + parseFloat(args[0]) * deviceCellHeight; + ctx.lineTo(currentX, y); + currentY = y; + lastControlX = currentX; + lastControlY = currentY; + lastCommand = type; + continue; + } + if (type === 'H') { + const x = xOffset + parseFloat(args[0]) * deviceCellWidth; + ctx.lineTo(x, currentY); + currentX = x; + lastControlX = currentX; + lastControlY = currentY; + lastCommand = type; + continue; + } if (!args[0] || !args[1]) { - if (type === 'Z') { - ctx.closePath(); - } continue; } if (type === 'A') { @@ -126,11 +149,44 @@ function drawPathDefinitionCharacter( ctx.moveTo(translatedArgs[0], translatedArgs[1]); currentX = translatedArgs[0]; currentY = translatedArgs[1]; + lastControlX = currentX; + lastControlY = currentY; } else if (type === 'L') { ctx.lineTo(translatedArgs[0], translatedArgs[1]); currentX = translatedArgs[0]; currentY = translatedArgs[1]; + lastControlX = currentX; + lastControlY = currentY; + } else if (type === 'Q') { + ctx.quadraticCurveTo(translatedArgs[0], translatedArgs[1], translatedArgs[2], translatedArgs[3]); + lastControlX = translatedArgs[0]; + lastControlY = translatedArgs[1]; + currentX = translatedArgs[2]; + currentY = translatedArgs[3]; + } else if (type === 'T') { + // T uses reflection of last control point if previous command was Q or T + let cpX: number; + let cpY: number; + if (lastCommand === 'Q' || lastCommand === 'T') { + cpX = 2 * currentX - lastControlX; + cpY = 2 * currentY - lastControlY; + } else { + cpX = currentX; + cpY = currentY; + } + ctx.quadraticCurveTo(cpX, cpY, translatedArgs[0], translatedArgs[1]); + lastControlX = cpX; + lastControlY = cpY; + currentX = translatedArgs[0]; + currentY = translatedArgs[1]; + } else if (type === 'C') { + ctx.bezierCurveTo(translatedArgs[0], translatedArgs[1], translatedArgs[2], translatedArgs[3], translatedArgs[4], translatedArgs[5]); + lastControlX = translatedArgs[2]; + lastControlY = translatedArgs[3]; + currentX = translatedArgs[4]; + currentY = translatedArgs[5]; } + lastCommand = type; } ctx.fill(); } @@ -564,7 +620,8 @@ function clamp(value: number, max: number, min: number = 0): number { const svgToCanvasInstructionMap: { [index: string]: any } = { 'C': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]), 'L': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.lineTo(args[0], args[1]), - 'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1]) + 'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1]), + 'Q': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.quadraticCurveTo(args[0], args[1], args[2], args[3]) }; function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] { diff --git a/demo/client.ts b/demo/client.ts index a6e33b20..b0598ed0 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -836,7 +836,7 @@ function customGlyphAlignmentHandler(): void { term.write('🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); term.write('\x1b[0mComposite terminal graphics characters:\x1b[34m\n\r'); - term.write('\u{1FBB2}\u{1FBB3} \u{1FBB9}\u{1FBBA}\n\r'); + term.write('\u{1FBB2}\u{1FBB3} \u{1FBB9}\u{1FBBA} \u{1FBC1}\u{1FBC2}\u{1FBC3}\n\r'); term.write('\x1b[0mFill tests:\x1b[35m\n\r'); const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}']; while (fillChars.length > 0) { From eb285af11ded3f5fef4cd434243976f4f269636c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:04:51 -0800 Subject: [PATCH 25/26] Add diagonal alignment tests --- demo/client.ts | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index b0598ed0..8845a010 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-restricted-syntax */ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT @@ -8,6 +7,8 @@ /// +/* eslint-disable no-restricted-syntax */ + // HACK: Playwright/WebKit on Windows does not support WebAssembly https://stackoverflow.com/q/62311688/1156119 import type { ImageAddon as ImageAddonType, IImageAddonOptions } from '@xterm/addon-image'; let ImageAddon: typeof ImageAddonType | undefined; // eslint-disable-line @typescript-eslint/naming-convention @@ -829,15 +830,27 @@ function customGlyphAlignmentHandler(): void { term.write(' ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏\n\r'); term.write(' ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█\n\r'); term.write('\x1b[0mSmooth mosaic terminal graphic characters alignment tests:\x1b[33m\n\r'); - term.write('🭇🬼 🭈🬽 🭉🬾 🭊🬿 🭋🭀 🭁🭌 🭂🭍 🭃🭎 🭄🭏 🭅🭐 🭆🭑 🭨🭪 🭩 🭯 🭮🭬\n\r'); - term.write('🭢🭗 🭣🭘 🭤🭙 🭥🭚 🭦🭛 🭒🭝 🭓🭞 🭔🭟 🭕🭠 🭖🭡 🭧🭜 🭫 🭭\n\r'); - term.write(' 🭇🬼 🭉🬾 🭋🭀\n\r'); - term.write('🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r'); - term.write('🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); - term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); - term.write('\x1b[0mComposite terminal graphics characters:\x1b[34m\n\r'); + term.write(' 🭇🬼 🭈🬽 🭉🬾 🭊🬿 🭋🭀 🭁🭌 🭂🭍 🭃🭎 🭄🭏 🭅🭐 🭆🭑 🭨🭪 🭩 🭯 🭮🭬\n\r'); + term.write(' 🭢🭗 🭣🭘 🭤🭙 🭥🭚 🭦🭛 🭒🭝 🭓🭞 🭔🭟 🭕🭠 🭖🭡 🭧🭜 🭫 🭭\n\r'); + term.write(' 🭇🬼 🭉🬾 🭋🭀\n\r'); + term.write(' 🭊🭁🭌🬿 🭈🭆🭂🭍🭑🬽 🭇🭄🭏🬼 🭃🭎 🭅🭐 🭨🭪\n\r'); + term.write(' 🭥🭒🭝🭚 🭣🭧🭓🭞🭜🭘 🭢🭕🭠🭗 🭔🭟 🭖🭡 🭪🭨\n\r'); + term.write(' 🭢🭗 🭤🭙 🭦🭛\n\r'); + term.write('\x1b[0mCharacter cell diagonals (1FBA0-1FBAE) alignment tests:\x1b[34m\n\r'); + term.write(' \u{1FBA3}\u{1FBA7}\u{1FBA2} \u{1FBA3}\u{1FBA8}\u{1FBA0} \u{1FBAD}\u{1FBA2} \u{1FBA3}\u{1FBAC} \u{1FBAE}\n\r'); + term.write(' \u{1FBA3}\u{1FBA0} \u{1FBA1}\u{1FBA2} \u{1FBA1}\u{1FBA9}\u{1FBA2} \u{1FBA1}\u{1FBAA} \u{1FBAB}\u{1FBA0}\n\r'); + term.write(' \u{1FBA4} \u{1FBA5}\n\r'); + term.write(' \u{1FBA1}\u{1FBA2} \u{1FBA3}\u{1FBA0}\n\r'); + term.write(' \u{1FBA1}\u{1FBA6}\u{1FBA0}\n\r'); + term.write('\x1b[0mCharacter cell diagonals (1FBD0-1FBDF) alignment tests:\x1b[34m\n\r'); + term.write(' \u{1FBD6}\u{1FBD4} \u{1FBD0}\u{1FBD1}\u{1FBD2}\u{1FBD3} \u{1FBDA} \u{1FBD9}\u{1FBDB} \u{1FBDE} \u{1FBDD}\u{1FBDF}\n\r'); + term.write(' \u{1FBD7}\u{1FBD5} \u{1FBD2}\u{1FBD3}\u{1FBD0}\u{1FBD1} \u{1FBD8} \u{1FBDC}\n\r'); + term.write(' \u{1FBD4}\u{1FBD6}\n\r'); + term.write(' \u{1FBD5}\u{1FBD7}\n\r'); + term.write(''); + term.write('\x1b[0mComposite terminal graphics characters:\x1b[35m\n\r'); term.write('\u{1FBB2}\u{1FBB3} \u{1FBB9}\u{1FBBA} \u{1FBC1}\u{1FBC2}\u{1FBC3}\n\r'); - term.write('\x1b[0mFill tests:\x1b[35m\n\r'); + term.write('\x1b[0mFill tests:\x1b[36m\n\r'); const fillChars = ['\u{2591}', '\u{2592}', '\u{2593}', '\u{1FB8C}', '\u{1FB8D}', '\u{1FB8E}', '\u{1FB8F}', '\u{1FB90}', '\u{1FB91}', '\u{1FB92}', '\u{1FB94}', '\u{1FB95}', '\u{1FB96}', '\u{1FB97}', '\u{1FB98}', '\u{1FB99}']; while (fillChars.length > 0) { const batch = fillChars.splice(0, 10); From 10d6ce3805a6684d0d9e0a284f534be446686964 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 23 Dec 2025 08:09:46 -0800 Subject: [PATCH 26/26] Update customGlyphs option --- typings/xterm-headless.d.ts | 18 +++++++++++++----- typings/xterm.d.ts | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index ba5f22de..9c3effda 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -64,13 +64,21 @@ declare module '@xterm/headless' { cursorWidth?: number; /** - * Whether to draw custom glyphs for block element and box drawing - * characters instead of using the font. This should typically result in - * better rendering with continuous lines, even when line height and letter - * spacing is used. Note that this doesn't work with the DOM renderer which - * renders all characters using the font. The default is true. + * Whether to draw custom glyphs instead of using the font for the following + * unicode ranges: + * + * - Box Drawing (U+2500-U+257F) + * - Box Elements (U+2580-U+259F) + * - Powerline Symbols (U+E0A0–U+E0BF) + * - Symbols for Legacy Computing (U+1FB00–U+1FBFF) + * + * This will typically result in better rendering with continuous lines, + * even when line height and letter spacing is used. Note that this doesn't + * work with the DOM renderer which renders all characters using the font. + * The default is true. */ customGlyphs?: boolean; + /** * Whether input should be disabled. */ diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 457f1612..18709d71 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -78,11 +78,18 @@ declare module '@xterm/xterm' { cursorInactiveStyle?: 'outline' | 'block' | 'bar' | 'underline' | 'none'; /** - * Whether to draw custom glyphs for block element and box drawing - * characters instead of using the font. This should typically result in - * better rendering with continuous lines, even when line height and letter - * spacing is used. Note that this doesn't work with the DOM renderer which - * renders all characters using the font. The default is true. + * Whether to draw custom glyphs instead of using the font for the following + * unicode ranges: + * + * - Box Drawing (U+2500-U+257F) + * - Box Elements (U+2580-U+259F) + * - Powerline Symbols (U+E0A0–U+E0BF) + * - Symbols for Legacy Computing (U+1FB00–U+1FBFF) + * + * This will typically result in better rendering with continuous lines, + * even when line height and letter spacing is used. Note that this doesn't + * work with the DOM renderer which renders all characters using the font. + * The default is true. */ customGlyphs?: boolean;