color.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package util
  2. import (
  3. "regexp"
  4. "strings"
  5. )
  6. var csiRE *regexp.Regexp
  7. func init() {
  8. csiRE = regexp.MustCompile(`\x1b\[([0-9;]+)m`)
  9. }
  10. var targetFGMap = map[string]string{
  11. "0;0;0": "\x1b[30m", // Black
  12. "128;0;0": "\x1b[31m", // Red
  13. "0;128;0": "\x1b[32m", // Green
  14. "128;128;0": "\x1b[33m", // Yellow
  15. "0;0;128": "\x1b[34m", // Blue
  16. "128;0;128": "\x1b[35m", // Magenta
  17. "0;128;128": "\x1b[36m", // Cyan
  18. "192;192;192": "\x1b[37m", // White (light grey)
  19. "128;128;128": "\x1b[90m", // Bright Black (dark grey)
  20. "255;0;0": "\x1b[91m", // Bright Red
  21. "0;255;0": "\x1b[92m", // Bright Green
  22. "255;255;0": "\x1b[93m", // Bright Yellow
  23. "0;0;255": "\x1b[94m", // Bright Blue
  24. "255;0;255": "\x1b[95m", // Bright Magenta
  25. "0;255;255": "\x1b[96m", // Bright Cyan
  26. "255;255;255": "\x1b[97m", // Bright White
  27. }
  28. var targetBGMap = map[string]string{
  29. "0;0;0": "\x1b[40m",
  30. "128;0;0": "\x1b[41m",
  31. "0;128;0": "\x1b[42m",
  32. "128;128;0": "\x1b[43m",
  33. "0;0;128": "\x1b[44m",
  34. "128;0;128": "\x1b[45m",
  35. "0;128;128": "\x1b[46m",
  36. "192;192;192": "\x1b[47m",
  37. "128;128;128": "\x1b[100m",
  38. "255;0;0": "\x1b[101m",
  39. "0;255;0": "\x1b[102m",
  40. "255;255;0": "\x1b[103m",
  41. "0;0;255": "\x1b[104m",
  42. "255;0;255": "\x1b[105m",
  43. "0;255;255": "\x1b[106m",
  44. "255;255;255": "\x1b[107m",
  45. }
  46. func ConvertRGBToAnsi16Colors(s string) string {
  47. return csiRE.ReplaceAllStringFunc(s, func(seq string) string {
  48. params := strings.Split(csiRE.FindStringSubmatch(seq)[1], ";")
  49. out := make([]string, 0, len(params))
  50. for i := 0; i < len(params); {
  51. // Detect “38 | 48 ; 2 ; r ; g ; b ( ; alpha? )”
  52. if (params[i] == "38" || params[i] == "48") &&
  53. i+4 < len(params) &&
  54. params[i+1] == "2" {
  55. key := strings.Join(params[i+2:i+5], ";")
  56. var repl string
  57. if params[i] == "38" {
  58. repl = targetFGMap[key]
  59. } else {
  60. repl = targetBGMap[key]
  61. }
  62. if repl != "" { // exact RGB hit
  63. out = append(out, repl[2:len(repl)-1])
  64. i += 5 // skip 38/48;2;r;g;b
  65. // if i == len(params)-1 && looksLikeByte(params[i]) {
  66. // i++ // swallow the alpha byte
  67. // }
  68. continue
  69. }
  70. }
  71. // Normal token — keep verbatim.
  72. out = append(out, params[i])
  73. i++
  74. }
  75. return "\x1b[" + strings.Join(out, ";") + "m"
  76. })
  77. }
  78. // func looksLikeByte(tok string) bool {
  79. // v, err := strconv.Atoi(tok)
  80. // return err == nil && v >= 0 && v <= 255
  81. // }