@@ -102,7 +102,6 @@ test.afterAll(async () => await ctx.page.close());
102102
103103test . describe ( 'Kitty Graphics Protocol' , ( ) => {
104104 // TODO: Add tests for larger images with various dimensions
105- // TODO: Add tests for image placement keys (x, y, w, h, X, Y, c, r)
106105 // TODO: Add tests for virtual placement (U=1)
107106 // TODO: Add tests for animation frames
108107 // TODO: Add performance tests for streaming large images
@@ -1398,6 +1397,89 @@ test.describe('Kitty Graphics Protocol', () => {
13981397 deepStrictEqual ( pixels ?. slice ( 4 , 8 ) , [ 255 , 0 , 0 , 255 ] ) ; // x=19: Red
13991398 deepStrictEqual ( pixels ?. slice ( 8 , 12 ) , [ 255 , 128 , 0 , 255 ] ) ; // x=20: Orange
14001399 } ) ;
1400+
1401+ test ( 'applies source crop via x/y/w/h before display' , async ( ) => {
1402+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,x=20,y=0,w=20,h=50;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1403+ await timeout ( 200 ) ;
1404+
1405+ deepStrictEqual ( await getOrigSize ( 1 ) , [ 20 , 50 ] ) ;
1406+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 255 , 128 , 0 , 255 ] ) ;
1407+ deepStrictEqual ( await getPixel ( 0 , 0 , 19 , 49 ) , [ 255 , 128 , 0 , 255 ] ) ;
1408+ } ) ;
1409+
1410+ test ( 'scales cropped source region to c/r placement rectangle' , async ( ) => {
1411+ // Firefox's createImageBitmap uses different resize sampling, producing
1412+ // slightly off pixel values compared to Chromium, so skip on Firefox.
1413+ if ( ctx . browser . browserType ( ) . name ( ) === 'firefox' ) {
1414+ test . skip ( ) ;
1415+ }
1416+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,x=1,y=0,w=1,h=1,c=4,r=2;${ KITTY_RGB_3X1_BASE64 } \x1b\\` ) ;
1417+ await timeout ( 200 ) ;
1418+
1419+ deepStrictEqual ( await getCursor ( ) , [ 4 , 1 ] ) ;
1420+ const left = await getPixel ( 0 , 0 , 2 , 10 ) ;
1421+ const right = await getPixel ( 0 , 0 , 25 , 10 ) ;
1422+ deepStrictEqual ( left , [ 0 , 255 , 0 , 255 ] ) ;
1423+ deepStrictEqual ( right , [ 0 , 255 , 0 , 255 ] ) ;
1424+ } ) ;
1425+
1426+ test ( 'applies sub-cell offset via X/Y within first cell' , async ( ) => {
1427+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,X=5,Y=3;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
1428+ await timeout ( 100 ) ;
1429+
1430+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 0 , 0 , 0 , 0 ] ) ;
1431+ deepStrictEqual ( await getPixel ( 0 , 0 , 4 , 2 ) , [ 0 , 0 , 0 , 0 ] ) ;
1432+ deepStrictEqual ( await getPixel ( 0 , 0 , 5 , 3 ) , [ 0 , 0 , 0 , 255 ] ) ;
1433+ } ) ;
1434+
1435+ test ( 'w=0 is treated as unset (displays full width)' , async ( ) => {
1436+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,w=0;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1437+ await timeout ( 200 ) ;
1438+
1439+ deepStrictEqual ( await getOrigSize ( 1 ) , [ 200 , 100 ] ) ;
1440+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 255 , 0 , 0 , 255 ] ) ;
1441+ deepStrictEqual ( await getPixel ( 0 , 0 , 199 , 99 ) , [ 255 , 255 , 255 , 255 ] ) ;
1442+ } ) ;
1443+
1444+ test ( 'h=0 is treated as unset (displays full height)' , async ( ) => {
1445+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,h=0;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1446+ await timeout ( 200 ) ;
1447+
1448+ deepStrictEqual ( await getOrigSize ( 1 ) , [ 200 , 100 ] ) ;
1449+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 255 , 0 , 0 , 255 ] ) ;
1450+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 50 ) , [ 255 , 192 , 203 , 255 ] ) ;
1451+ } ) ;
1452+
1453+ test ( 'x exceeding image width produces no display' , async ( ) => {
1454+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,x=999;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1455+ await timeout ( 200 ) ;
1456+
1457+ strictEqual ( await getImageStorageLength ( ) , 0 ) ;
1458+ } ) ;
1459+
1460+ test ( 'negative x/y values are clamped to 0' , async ( ) => {
1461+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,x=-10,y=-10;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1462+ await timeout ( 200 ) ;
1463+
1464+ deepStrictEqual ( await getOrigSize ( 1 ) , [ 200 , 100 ] ) ;
1465+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 255 , 0 , 0 , 255 ] ) ;
1466+ } ) ;
1467+
1468+ test ( 'combined crop and sub-cell offset' , async ( ) => {
1469+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,x=20,y=0,w=20,h=50,X=5,Y=3;${ KITTY_MULTICOLOR_200X100_BASE64 } \x1b\\` ) ;
1470+ await timeout ( 200 ) ;
1471+
1472+ deepStrictEqual ( await getPixel ( 0 , 0 , 0 , 0 ) , [ 0 , 0 , 0 , 0 ] ) ;
1473+ deepStrictEqual ( await getPixel ( 0 , 0 , 4 , 2 ) , [ 0 , 0 , 0 , 0 ] ) ;
1474+ deepStrictEqual ( await getPixel ( 0 , 0 , 5 , 3 ) , [ 255 , 128 , 0 , 255 ] ) ;
1475+ } ) ;
1476+
1477+ test ( 'sub-cell offset with explicit c/r advances cursor correctly' , async ( ) => {
1478+ await ctx . proxy . write ( `\x1b_Ga=T,f=100,X=5,Y=3,c=4,r=2;${ KITTY_BLACK_1X1_BASE64 } \x1b\\` ) ;
1479+ await timeout ( 100 ) ;
1480+
1481+ deepStrictEqual ( await getCursor ( ) , [ 4 , 1 ] ) ;
1482+ } ) ;
14011483 } ) ;
14021484
14031485 test . describe ( 'Query support' , ( ) => {
0 commit comments