Repository navigation
'Render' colours in REPL #514
Description
Activity
I think this is a good idea, but the similar feature is available in ImageInTerminal.jl.
For example, if you want to copy and paste the output into a GitHub issue, the current "plain" text is more beneficial.
I think there needs to be an easy way for users to specify which output they want, and loading or not loading the package is one of the easiest ways.Also, the conversion to ANSI 256 (240) colors will be supported in Colors v0.13. (cf. issue #473)
cc: @johnnychen94
Reacted by Johnny Chen and Tim HolyI think this is a good idea, but the similar feature is available in ImageInTerminal.jl.
Ah, I hadn't heard of that. Looking at it, it seems like a fancier version of what I've done here, particularly with the Sixel support.
Given how easy it is to add this or something like it though, I personally feel like it would be nice to have some basic color preview support when just using
Colors.jl.ImageInTerminal.jlwould then be handy if you say wanted to see images with Sixels, which does seem outside what you'd want to have inColors.jl.if you want to copy and paste the output into a GitHub issue, the current "plain" text is more beneficial.
Mmm, though for copy-pasting the single-colour versions are basically the same - there are just 3 extra spaces at the start, so for that representation at least I see no downsides. It is also beneficial when seeing a range of colours, though much less compact of course.
As no information is lost here, I'd advocate for this being part of Colors.jl, if nothing else.
I think there needs to be an easy way for users to specify which output they want
I've actually just had an idea, inspired by how vectors are displayed. The above color-on-each-line-with-info is analogous to

with that in mind, I consider the adjoint

it's a rather useless way of representing colours AFAICT (usually only 2-3 are shown), so perhaps when showing them "sideways" we could squash the colour info out for the more compact view? I feel like it could be nice to trivially be able to view both representations.

All one needs to make it work like this is change the type signature in the last two
showfunctions, e.g. toshow(io::IO, ::MIME"text/plain", cs::Adjoint{<:Colorant, <:Vector{<:Colorant}}).Also, the conversion to ANSI 256 (240) colors will be supported in Colors v0.13
Ah, nice. When I implemented this in a package of my own I just copied tmux's color conversion, and that's worked well for me so I think :)
where did you learn the usage of color("#"*hex(c, :rrggbb))
Well, I want r, g, and b values for any colour and
convert(RGB, colorant"hsl(120, 100%, 25%)")didn't work so I tried?hexnoticed thehex(..., :rrbbgg)example, tried it, and so wrapped it withcolor("#"* ... ). I assume there's a better way to do it?I personally feel like it would be nice to have some basic color preview support when just using
Colors.jlI agree with you. So, I think the key issue is how to avoid annoying troubles.
As you said, the issue of loss of information (i.e. copy-pasting issue) will not be a trouble as long as the color swatches and text are written together. In the case of "sideways", why not also add one line to display both?
The main trouble here is unintentional output of ANSI escape codes in environments that do not support color display.
At least, we would need to follow the
:colorIO property (get(io, :color, false)). However, even if color display is partially supported, there is no reliable way to determine whether 256 or 24-bit colors is supported.
Referencing environment variables, as in your example, is one way to do it. Even with that way, the evaluation of the variables should be done at "run-time".Another trouble is compatibility with
ImageInTerminal.jl(e.g. the redefinition of methods), but this can be solved with additional modifications on theImageInTerminal.jlside., and so wrapped it with
color("#"* ... ). I assume there's a better way to do it?color("string")should be replaced withparse(Colorant, "string"). However, there is no need to convert a color via a string for this feature. You can just convert the color toRGB(RGB24).This doesn't do anything for 256-colors, but other than that I think I've come up with a model that should resolve all the other concerns.
When
get(io, :color, false) == trueandENV["COLORTERM"] == "truecolor"I feel like this strikes a nice balance between being compact/swatchy and informative/copy-pastable. This may not improve the experience for people on terminals that don't set
COLORTERMappropriately, or only support 256-color, but their experience is no worse, just not improved — so I'd consider this a "win" overall.Otherwise
As far as I can tell, this is identical to the current behaviour.
Code
colorcode(rgb::Vector{<:Integer}, background=false) = string("\e[", if background "48" else "38" end, ";2;", rgb[1], ";", rgb[2], ";", rgb[3], "m") colorcode(c::Union{RGB, RGB24}, background=false) = colorcode(reinterpret.([red(c), green(c), blue(c)]), background) colorcode(c::RGB{Float64}, background=false) = colorcode(round.(Int, 255 .* [red(c), green(c), blue(c)]), background) colorcode(c::Colorant, background=false) = colorcode(convert(RGB24, c), background) import Base.show function show(io::IO, ::MIME"text/plain", c::Colorant) if get(io, :color, false) && ENV["COLORTERM"] == "truecolor" print(colorcode(c, true), " \e[0m ") end show(io, c) end using LinearAlgebra # for the Adjoint type function show(io::IO, ::MIME"text/plain", cs::Adjoint{<:Colorant, <:Vector{<:Colorant}}) summary(io, cs) print(":\n") if get(io, :color, false) && ENV["COLORTERM"] == "truecolor" print(" ", join(colorcode.(cs,true), if length(cs) <= 40 " " else " " end), if length(cs) <= 40 " \e[0m" else " \e[0m" end) else Base.print_array(IOContext(io, :SHOWN_SET => cs), cs) end end function show(io::IO, ::MIME"text/plain", cs::Matrix{<:Colorant}) summary(io, cs) print(":\n") if get(io, :color, false) && ENV["COLORTERM"] == "truecolor" for csrow in eachrow(cs) println(" ", join(colorcode.(csrow,true), if size(cs,2) <= 40 " " else " " end), if size(cs,2) <= 40 " \e[0m" else " \e[0m" end) end else Base.print_array(IOContext(io, :SHOWN_SET => cs), cs) end end
I like the new ANSI color + value view. ImageInTerminal and perhaps UnicodePlots (cc: @t-bltg) could also benefit from this proposal if the color encoder is implemented here (or maybe https://github.com/KristofferC/Crayons.jl).
There are two things to note from my ImageInTerminal maintenance experience:
- We should not be too smart on the color mode by checking
ENV["COLORTERM"]. There should be an API to encode a given color to either 256 or 24bit mode codes no matter what the host terminal is. For instance, ImageInTerminal provides a dispatching typeTermColor256andTermColor24bithttps://github.com/JuliaImages/ImageInTerminal.jl/blob/4cd5bebf718196cb49a66b600b27d4023696ead4/src/colorant2ansi.jl#L36-L55 This also makes it possible to do unit test. - IIUC Windows with Julia < 1.6 doesn't support any kind of ANSI color (even with the fancy Windows Terminal), so there should be an additional check on
iswindows() && VERSION < v"1.6". https://github.com/JuliaImages/ImageInTerminal.jl/blob/4cd5bebf718196cb49a66b600b27d4023696ead4/src/ImageInTerminal.jl#L88-L97
The
showmethod, however, should perhaps live in ImageInTerminal because Colors.jl focus on pixel/colorant level, also because the sixel encoding only makes sense at the array level. If we want to haveshowmethod here then we need to provide a way to allow IIT to override the behavior using Sixel encoding.The ANSI color + value view only makes sense if we're looking at a very small image.
Reacted by kimikage- We should not be too smart on the color mode by checking
I don't have a clear opinion on whether
showfortext/plainshould be implemented inColors.jl. However, I think it is too tricky to specializeshowforAdjointanyway.
(The implementation issues of thecolorcodeandENV["COLORTERM"]above should be reviewed in the PR stage.)Sixel stuff should definitely be left to ImageInTerminal, and I think it would be nice if Colours.jl had some swatch-ing. Beyond that, I have no strong opinions. Let me know what you'd like me to put in a PR, and I'll make one.
IMO, it would be a good idea to submit a PR to add a swatch using ANSI escape codes for "single"
Colorfirst.In that PR, you could design the logic and API to determine whether or not to add a swatch, and whether it uses 24-bit colors or 256 colors.
ImageInTerminal.jl(indirectly) depends onColors.jl, soImageInTerminal.jlshould be able to reuse that API. In other words, an API that is compatible with the current API ofImageInTerminal.jlwould be preferable.
(Also, supporting the environment variables defined byCrayons.jlmay improve the usability.)Reacted by Johnny ChenPerhaps it might be useful to define a function like:
print_swatch([io,] color::Union{Color, AbstractArray{<:Color}}; width=2, mode=:auto)
to replace:
print(colorcode(c, true), " \e[0m ")
You could use Term to build a widget to visualize colors too.
See: https://fedeclaudi.github.io/Term.jl/stable/basics/colors/
Playing around with things I was able to make this:

It should be very easy to crate the kind of visualizations you're after.
Reacted by Johnny ChenCurrently, there are already multiple means of displaying colors on the terminal.
And with the addition ofStyledStringsto stdlib, the terminal will become even more colorful in the future.In view of this, wouldn't it be a hindrance to customize the
showin Colors.jl?
As mentioned above, there is no problem with adding Colors.jl "original" APIs such asprint_swatch.As I see it, it would be nice for Colors.jl to do something a bit nicer OOTB, and with
StyledStringsit is possible to do so while resolving quite a few of the concerns from earlier. For instance,At least, we would need to follow the :color IO property (
get(io, :color, false)). However, even if color display is partially supported, there is no reliable way to determine whether 256 or 24-bit colors is supported.is now fully handled by
StyledStrings+ the new terminfo handling.This doesn't preclude other packages from doing even fancier colour rendering at all, they can implement custom
displaymethods and engage in method overwriting. What we do get though is a prettier OOTB experience 🙂they can implement custom display methods and engage in method overwriting.
Generally they cannot. That is my concern.
One possibility would be to use Preferences.jl. (cf. #535)they can implement custom display methods and engage in method overwriting.
Generally they cannot. That is my concern.
Hmmm? You absolutely can, just not during precompilation.
One possibility would be to use Preferences.jl
If there's really a need to be able to turn it off, then that does seem like a reasonable approach 👍.



Hello, I was having trouble imagining what various colours looked like in the REPL so I cooked up a little code to show me the colours. Should there be interest, I'd be happy to turn this into a PR.
Result
Code