package cleanimage import ( "fmt" "strings" ) // KittyImageCleaner provides methods to generate Kitty graphics protocol // commands for deleting images. type KittyImageCleaner struct{} // NewKittyImageCleaner creates a new instance of KittyImageCleaner. func NewKittyImageCleaner() *KittyImageCleaner { return &KittyImageCleaner{} } // buildCommand constructs the base Kitty graphics protocol command. func (kic *KittyImageCleaner) buildCommand(params map[string]string) string { var sb strings.Builder sb.WriteString("\033_Ga=d") // Start with the delete action if len(params) > 0 { var paramStrings []string for key, value := range params { paramStrings = append(paramStrings, fmt.Sprintf("%s=%s", key, value)) } sb.WriteString(",") sb.WriteString(strings.Join(paramStrings, ",")) } sb.WriteString("\033\\") // End the command return sb.String() } // DeleteAllVisiblePlacements deletes all images visible on screen. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteAllVisiblePlacements(freeData bool) string { if freeData { return kic.buildCommand(map[string]string{"d": "A"}) } return kic.buildCommand(map[string]string{"d": "a"}) } // DeleteByID deletes images with a specific ID. // 'imageID' is the ID of the image to delete. // 'placementID' is an optional placement ID. If 0, all placements with the imageID are deleted. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByID(imageID int, placementID int, freeData bool) string { params := make(map[string]string) if freeData { params["d"] = "I" } else { params["d"] = "i" } params["i"] = fmt.Sprintf("%d", imageID) if placementID != 0 { params["p"] = fmt.Sprintf("%d", placementID) } return kic.buildCommand(params) } // DeleteNewestByID deletes the newest image with a specified number (ID). // 'imageNumber' is the number (ID) of the newest image to delete. // 'placementID' is an optional placement ID. If 0, all placements with the imageNumber are deleted. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteNewestByID(imageNumber int, placementID int, freeData bool) string { params := make(map[string]string) if freeData { params["d"] = "N" } else { params["d"] = "n" } params["I"] = fmt.Sprintf("%d", imageNumber) // Note: Kitty uses 'I' for number here if placementID != 0 { params["p"] = fmt.Sprintf("%d", placementID) } return kic.buildCommand(params) } // DeleteByCursorPosition deletes all placements that intersect with the current cursor position. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByCursorPosition(freeData bool) string { if freeData { return kic.buildCommand(map[string]string{"d": "C"}) } return kic.buildCommand(map[string]string{"d": "c"}) } // DeleteAnimationFrames deletes animation frames. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteAnimationFrames(freeData bool) string { if freeData { return kic.buildCommand(map[string]string{"d": "F"}) } return kic.buildCommand(map[string]string{"d": "f"}) } // DeleteByCellPosition deletes all placements that intersect a specific cell. // 'x', 'y' are the coordinates of the cell (1-indexed). // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByCellPosition(x, y int, freeData bool) string { params := map[string]string{ "x": fmt.Sprintf("%d", x), "y": fmt.Sprintf("%d", y), } if freeData { params["d"] = "P" } else { params["d"] = "p" } return kic.buildCommand(params) } // DeleteByCellAndZIndex deletes all placements that intersect a specific cell // and have a specific z-index. // 'x', 'y' are the coordinates of the cell (1-indexed). // 'zIndex' is the z-index of the placements to delete. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByCellAndZIndex(x, y, zIndex int, freeData bool) string { params := map[string]string{ "x": fmt.Sprintf("%d", x), "y": fmt.Sprintf("%d", y), "z": fmt.Sprintf("%d", zIndex), } if freeData { params["d"] = "Q" } else { params["d"] = "q" } return kic.buildCommand(params) } // DeleteByIDRange deletes all images whose ID is within a specified range. // 'minID' is the minimum ID (inclusive). // 'maxID' is the maximum ID (inclusive). // 'freeData' determines if the underlying image data should also be freed. // (Requires Kitty version 0.33.0 or later) func (kic *KittyImageCleaner) DeleteByIDRange(minID, maxID int, freeData bool) string { params := map[string]string{ "x": fmt.Sprintf("%d", minID), "y": fmt.Sprintf("%d", maxID), } if freeData { params["d"] = "R" } else { params["d"] = "r" } return kic.buildCommand(params) } // DeleteByColumn deletes all placements that intersect the specified column. // 'column' is the column number (1-indexed). // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByColumn(column int, freeData bool) string { params := map[string]string{ "x": fmt.Sprintf("%d", column), } if freeData { params["d"] = "X" } else { params["d"] = "x" } return kic.buildCommand(params) } // DeleteByRow deletes all placements that intersect the specified row. // 'row' is the row number (1-indexed). // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByRow(row int, freeData bool) string { params := map[string]string{ "y": fmt.Sprintf("%d", row), } if freeData { params["d"] = "Y" } else { params["d"] = "y" } return kic.buildCommand(params) } // DeleteByZIndex deletes all placements that have the specified z-index. // 'zIndex' is the z-index of the placements to delete. // 'freeData' determines if the underlying image data should also be freed. func (kic *KittyImageCleaner) DeleteByZIndex(zIndex int, freeData bool) string { params := map[string]string{ "z": fmt.Sprintf("%d", zIndex), } if freeData { params["d"] = "Z" } else { params["d"] = "z" } return kic.buildCommand(params) } func main() { cleaner := NewKittyImageCleaner() // Example usage: fmt.Println("Kitty Image Cleaning Commands:") fmt.Println("-------------------------------") // _Ga=d\ # delete all visible placements fmt.Println("Delete all visible placements (no data free):", cleaner.DeleteAllVisiblePlacements(false)) // _Ga=d,d=A\ # delete all visible placements, freeing data fmt.Println("Delete all visible placements (with data free):", cleaner.DeleteAllVisiblePlacements(true)) // _Ga=d,d=i,i=10\ # delete the image with id=10, without freeing data fmt.Println("Delete image with ID 10 (no data free):", cleaner.DeleteByID(10, 0, false)) // _Ga=d,d=I,i=10\ # delete the image with id=10, freeing data fmt.Println("Delete image with ID 10 (with data free):", cleaner.DeleteByID(10, 0, true)) // _Ga=d,d=i,i=10,p=7\ # delete the image with id=10 and placement id=7, without freeing data fmt.Println("Delete placement 7 of image ID 10 (no data free):", cleaner.DeleteByID(10, 7, false)) // _Ga=d,d=I,i=10,p=7\ # delete the image with id=10 and placement id=7, freeing data fmt.Println("Delete placement 7 of image ID 10 (with data free):", cleaner.DeleteByID(10, 7, true)) // _Ga=d,d=Z,z=-1\ # delete the placements with z-index -1, also freeing up image data fmt.Println("Delete placements with z-index -1 (with data free):", cleaner.DeleteByZIndex(-1, true)) // _Ga=d,d=z,z=0\ # delete the placements with z-index 0, without freeing data fmt.Println("Delete placements with z-index 0 (no data free):", cleaner.DeleteByZIndex(0, false)) // _Ga=d,d=p,x=3,y=4\ # delete all placements that intersect the cell at (3, 4), without freeing data fmt.Println("Delete placements at cell (3,4) (no data free):", cleaner.DeleteByCellPosition(3, 4, false)) // _Ga=d,d=P,x=5,y=6\ # delete all placements that intersect the cell at (5, 6), freeing data fmt.Println("Delete placements at cell (5,6) (with data free):", cleaner.DeleteByCellPosition(5, 6, true)) fmt.Println("Delete placements intersecting cursor (no data free):", cleaner.DeleteByCursorPosition(false)) fmt.Println("Delete placements intersecting column 10 (with data free):", cleaner.DeleteByColumn(10, true)) fmt.Println("Delete placements intersecting row 5 (no data free):", cleaner.DeleteByRow(5, false)) fmt.Println("Delete images with ID range 100-200 (with data free):", cleaner.DeleteByIDRange(100, 200, true)) fmt.Println("Delete placements at cell (1,1) with z-index 10 (no data free):", cleaner.DeleteByCellAndZIndex(1, 1, 10, false)) }