cli-pride-flags
Version:
Displays pride flags in your terminal~
180 lines (165 loc) • 6.66 kB
JavaScript
const chalk = require('chalk')
const { parseArgs } = require('node:util')
// Turn an input array of Numbers into an array of cumulative weights
function toCumulativeWeights(inputArray, mode = null) {
/// copy the input
let input = [...inputArray]
let result = []
let accumulator = 0
if (mode === 'gradient') {
/// needs to start at 0 and lose the last weight,
/// or you lose the first and have a w i d e last
input.unshift(0)
input.pop()
}
const sum = input.reduce((a, x) => a + x)
for (const x of input) {
/// likewise, order matters here for block and gradient modes
/// precision of 3 is needed for smaller flags
if (mode === 'block') {
result.push((accumulator / sum).toFixed(3))
accumulator += x
} else {
accumulator += x
result.push((accumulator / sum).toFixed(3))
}
}
return result
}
function interpolateColor(color1, color2, percentage) {
// Convert colors to RGB
let color1RGB = hexToRGB(color1)
let color2RGB = hexToRGB(color2)
// Determine which color is lighter and adjust the percentage
const lightness1 = color1RGB.r * 0.299 + color1RGB.g * 0.587 + color1RGB.b * 0.114
const lightness2 = color2RGB.r * 0.299 + color2RGB.g * 0.587 + color2RGB.b * 0.114
if (lightness1 > lightness2) {
// flip
;[color2RGB, color1RGB] = [color1RGB, color2RGB]
percentage = 1 - percentage
}
// Interpolate colors using the adjusted percentage
const r = Math.round(color1RGB.r + (color2RGB.r - color1RGB.r) * percentage)
const g = Math.round(color1RGB.g + (color2RGB.g - color1RGB.g) * percentage)
const b = Math.round(color1RGB.b + (color2RGB.b - color1RGB.b) * percentage)
return RGBToHex(
...[r, g, b].map((v) => {
/// clamp dem numbers!
v = Math.max(0, Math.min(v, 255))
return v
})
)
}
function hexToRGB(hex) {
const r = parseInt(hex.slice(1, 3), 16)
const g = parseInt(hex.slice(3, 5), 16)
const b = parseInt(hex.slice(5, 7), 16)
return { r: r, g: g, b: b }
}
function RGBToHex(r, g, b) {
const hexR = r.toString(16).padStart(2, '0')
const hexG = g.toString(16).padStart(2, '0')
const hexB = b.toString(16).padStart(2, '0')
return `#${hexR}${hexG}${hexB}`
}
function randNum(max) {
return Math.floor(Math.random() * Math.floor(max++))
}
class ColorStop {
constructor(data) {
this.pos = data[0]
this.colorCode = data[1]
}
}
class FlagColors {
constructor(flag) {
/// grab the weights furst
let weights = flag.weights
if (!weights) {
/// no weights? its a uniform flag!
weights = Array(flag.stripes.length).fill(1, -flag.stripes.length)
}
/// create the cumulative weights~
this.blockColors = toCumulativeWeights(weights, 'block').map((x, i) => new ColorStop([x, flag.stripes[i]]))
this.gradientColors = toCumulativeWeights(weights, 'gradient').map(
(x, i) => new ColorStop([x, flag.stripes[i]])
)
/// Sort color stops in reverse order, so getColor checks for the highest value that the input is "to the right" of.
this.blockColors.sort((a, b) => (a.pos > b.pos ? -1 : a.pos < b.pos ? 1 : 0))
this.gradientColors.sort((a, b) => (a.pos > b.pos ? -1 : a.pos < b.pos ? 1 : 0))
}
getColor(pos, mode = 'block') {
if (mode === 'gradient') {
for (const i in this.gradientColors) {
if (pos === this.gradientColors[i].pos) {
return this.gradientColors[i].colorCode
} else if (pos >= this.gradientColors[i].pos) {
const leftColorStop = this.gradientColors[i]
const rightColorStop = this.gradientColors[i - 1] ?? leftColorStop
// + 0.01 cause dPos = 0 at the end of the flag
const dPos = 0.01 + (rightColorStop.pos - leftColorStop.pos)
const percentage = (pos - leftColorStop.pos) / dPos
return interpolateColor(leftColorStop.colorCode, rightColorStop.colorCode, percentage)
}
}
}
// not a gradient, default to blocc
for (const stop of this.blockColors) {
if (pos > stop.pos) {
return stop.colorCode
}
}
return null
}
}
/// tfw nobody has what you need so you roll your own
class ArgParser {
#options = {}
constructor(options) {
this.#options = options
}
listOptions() {
let output = []
const SPACES = 20 /// option starting distance from the left
/// keep this
for (const [long, info] of Object.entries(this.#options).sort((a,b) => a[0].localeCompare(b[0]))) {
let optionHelpString = ' ' /// prepend 2 spaces
if (info.short) {
optionHelpString += `-${info.short}, `
}
else {
optionHelpString += ' '.repeat(4) /// bruh
/// so it doesnt like when a option doesnt have a short flag
/// give it spacing of the same amount so its happy
}
optionHelpString += `--${long}`
optionHelpString = chalk.blueBright(optionHelpString) // make the options blue before continuing
if (info.type !== 'boolean') {
optionHelpString += chalk.yellow(` ${info.argName}`)
}
/// funky math
optionHelpString = optionHelpString.padEnd(
optionHelpString.length + (SPACES - (long.length + (info.argName ? info.argName.length + 1 : 0))),
' '
)
optionHelpString += `${info.description}`
output.push(optionHelpString)
}
return output.join('\n').trim()
}
parse() {
const inputArgs = process.argv.slice(2)
try {
const { values, positionals } = parseArgs({
args: inputArgs,
options: this.#options,
allowPositionals: true,
})
return { args: positionals, options: values }
} catch (e) {
console.log(e.message)
process.exit(1)
}
}
}
module.exports = { randNum, interpolateColor, FlagColors, ArgParser }