webdiscus/ansis

[FEATURE REQUEST] Fallbacks when true color or ANSI 256 is not supported

Closed this issue · 13 comments

Current behaviour

When using hex() method in a terminal having only 256 colors, the following things happen:

  • There is no fallback to the closest color from 256-color palette,
  • The following output has distorted colors that can not even be forced to normal.

Expected behaviour

  • There should be fallback to the lower palette level, similar to Chalk behaviour, it should find a color closest to the requested one;
  • If a certain color is not supported, that should not alter the next output.

Reproduction Example

import { ansi256, hex, white } from "ansis";

const pink = hex("#F5A9B8");
const blue = hex("#5BCEFA");

// running in 256 colors mode
console.log(ansi256(199)("Color 199")); // works
console.log(pink("This should be pink")); // no fallback to 256
console.log(blue("This should be blue")); // no fallback to 256, wrong color
console.log("This should be default"); // does not return to default color
console.log(white("Forced white")); // can not even force white

image

Environment

  • OS: macOS
  • version of Node.js: v20.11.1
  • version of Webpack: not applicable
  • version of the Plugin: ansis@2.3.0

Additional context

Using built-in Terminal on MacOS having 256 colors.

For comparison, this is how it looks when running in iTerm having true color support

image

Here is a similar code, but using chalk v4.1.2

import chalk from "chalk";

const pink = chalk.hex("#F5A9B8");
const blue = chalk.hex("#5BCEFA");

console.log(chalk.ansi256(199)("Color 199"));
console.log(pink("This should be pink"));
console.log(blue("This should be blue"));
console.log("This should be default");
console.log(chalk.whiteBright("Forced white"));

Running it in built-in Terminal on MacOS having 256 colors gives the desirable output:

image

Hallo @RobinTail,

Thanks for the feature request!

  1. I will check the issue with default color in the next output.
  2. Yes, it is very important to have a fallback for a terminal supported 256 colors only.
    I will implement it in the next version.

Grüß aus Köln

Yes, it is very important to have a fallback for a terminal supported 256 colors only.
I will implement it in the next version.

Thank you, @webdiscus
Ideally it should have fallbacks down to the minimal supported palette.

True color —> 256 colors —> 16 colors —> no colors.

For example, doing hex("#5BCEFA") in environment having 16 colors only, it should fall back approximately to cyan.

@RobinTail
I have implemented the detection of color spaces: truecolor, 256 colors, 16 colors, mono.
The fallback truecolor —> 256 colors —> 16 colors —> no colors works too.
I'm by testing and refactoring.

I'm glad to read it, @webdiscus , great news!
Let me know if you need any help.

@RobinTail

the new version 3.0.0 is released with fallback to supported color space.

Awesome! I'm going to try it today, @webdiscus

Dear @webdiscus ,
It works perfectly! Thank you so much for all the effort you applied to this feature and your attention to details.
I highly appreciate that you measured the performance and updated documentation.
Fallbacks, named imports, CommonJS compatibility — everything works well.
I tested it in iTerm, Terminal of MacOS and GitHub CI.

By the way, @webdiscus
I noticed you also switched to vitest, and I'd like to inform you that vitest now also has benchmarking tools:
https://vitest.dev/api/#bench

So that you can have a single dependency both for testing and benchmarking.
I use it myself and I like how it works.

@RobinTail

Thank You for the very useful feature request!
This makes the library even better.

I have invested a time to optimize performance and size for the new features.
New features adds in bundle only ~600 bytes and the code bundle is still < 4 KB.

vitest now also has benchmarking tools: https://vitest.dev/api/#bench

Thanks @RobinTail for the info.

Yes, I know, and I will switch the benchmark to vitest/bench, but in next step. First I wanted to release the feature and then optimize the benchmark.

Vitest is a very cool tools. This allow to test ESM modules "out of the box". Jest doesn't work with ESM in JetBrains IDEA, so I switched to vitest.

New features adds in bundle only ~600 bytes and the code bundle is still < 4 KB.

That's a great result, @webdiscus !
I highly appreciate your passion.
I think the issue is resolved, so I'm going to close it.
Thank you so much for all the support :)

@RobinTail

I have create some simple tests suits using vitest bench.

I am saddened :-(

  1. it's still very raw and is not yet released:
    Benchmarking is an experimental feature.
    Breaking changes might not follow SemVer, please pin Vitest's version when using it.
    
  2. it's very very buggy and does not work stably. Using only 4 suits occurs fatal error:
    Allocation failed - JavaScript heap out of memory
    
  3. test results are false and cannot be trusted, e.g., the simple bench for chalk.red('foo') and ansis.red('foo') have results:
    chalk  7.000.000
    ansis 23.000.000 (x3 faster than chalk - this is unreal/FALSE result)
    
    To compare with the result of the benchmark:
    chalk 73.000.000
    ansis 69.000.000 (this is TRUE result)
    

So, we cannot trust the results of the vitest bench.
We need to wait until the first release appears.

Alright, you seem to made a deep research on that topic.
Perhaps it's not yet ready for the project of your scale.
Regarding the accuracy of the measurements.
I think it's different because vitest runs the tests against the transformed code (using esbuild and rollup under the hood). Perhaps it does that transformation differently for ansis and chalk.
I hope it will get better. Thanks for letting me know, @webdiscus