const colors = ['#ff0000', '#00ff00', '#0000ff']; let colorsChanged = true; // Copied from https://github.com/revoltchat/backend/blob/master/crates/quark/src/util/regex.rs const RE_COLOUR = "^(?:[a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|(repeating-)?(linear|conic|radial)-gradient\(([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|\d+deg)([ ]+(\d{1,3}%|0))?(,[ ]*([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+)([ ]+(\d{1,3}%|0))?)+\))$"; function updateUi() { const radios = document.getElementsByName('gradient_type'); document.getElementById('config_linear').style.display = radios[0].checked ? 'unset' : 'none'; document.getElementById('config_radial').style.display = radios[1].checked ? 'unset' : 'none'; document.getElementById('config_conic').style.display = radios[2].checked ? 'unset' : 'none'; if (colorsChanged) { colorsChanged = false; const list = document.getElementById('color-list'); while (list.lastChild) list.lastChild.remove(); for (let i in colors) { i = Number(i); const color = colors[i]; let colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = color; colorInput.onchange = (e) => changeColor(i, e.currentTarget.value); list.appendChild(colorInput); let button1 = document.createElement('button'); button1.innerText = '^'; button1.disabled = i == 0; button1.onclick = () => moveUp(i); list.appendChild(button1); let button2 = document.createElement('button'); button2.innerText = 'v'; button2.disabled = i == colors.length - 1; button2.onclick = () => moveDown(i); list.appendChild(button2); let button3 = document.createElement('button'); button3.innerText = 'Delete'; button3.onclick = () => removeColor(i); list.appendChild(button3); // Dynamic distance scaling between color nodes, too much effort to implement rn /* let sliderValue = document.createElement('span'); sliderValue.innerText = '50%'; let slider = document.createElement('input'); slider.type = 'range'; slider.min = 0; slider.max = 100; slider.value = 50; slider.autocomplete = 'off'; slider.oninput = (e) => sliderValue.innerText = `${e.currentTarget.value}%`; list.appendChild(slider); list.appendChild(sliderValue); */ list.appendChild(document.createElement('br')); } } document.getElementById('custom_pos_inputs').hidden = !document.getElementById('conic_custom_position').checked; const css = generateCss(); if (css) { document.getElementById('preview').style.backgroundImage = css; document.getElementById('output').value = css; if (css.length > 128) { document.getElementById('output_error').innerText = `Warning: The generated CSS is ${css.length} characters long, which exceeds Revolt's limit of 128 characters. You will not be able to use this gradient on Revolt.`; } // for some reason this won't work //else if (!css.match(new RegExp(RE_COLOUR, 'i'))) { // document.getElementById('output_error').innerText = `Warning: The generated CSS doesn't match against the validation regex. This means that Revolt will not accept one of the set parameters.`; //} else document.getElementById('output_error').innerText = ''; } } function removeColor(index) { colorsChanged = true; colors.splice(index, 1); updateUi(); } function appendColor() { colorsChanged = true; colors.push('#ffbbaa'); updateUi(); } function changeColor(index, color) { colorsChanged = true; colors[index] = color; updateUi(); } function moveUp(index) { colorsChanged = true; [colors[index], colors[index - 1]] = [colors[index - 1], colors[index]]; updateUi(); } function moveDown(index) { colorsChanged = true; [colors[index], colors[index + 1]] = [colors[index + 1], colors[index]]; updateUi(); } /** * @returns {string} */ function generateColorList() { const noTransition = document.getElementById('no_transition').checked; if (noTransition) { const distance = 100 / colors.length; const colorCodes = []; for (let i = 0; i < colors.length; i++) { colorCodes.push(i == 0 ? colors[i] : `${colors[i]} ${Math.round(distance * i * 10) / 10}%`); colorCodes.push(i == colors.length - 1 ? colors[i] : `${colors[i]} ${Math.round(distance * (i+1) * 10) / 10}%`); } return colorCodes.join(','); } else return colors.join(','); } /** * @returns {string} */ function generateCss() { const radios = document.getElementsByName('gradient_type'); if (radios[0].checked) return generateCssLinear(); if (radios[1].checked) return generateCssRadial(); if (radios[2].checked) return generateCssConic(); return ''; } /** * @returns {string} */ function generateCssLinear() { const rotation = document.getElementById('rotation').value; return `linear-gradient(${rotation}deg,${generateColorList()})`; } /** * @returns {string} */ function generateCssRadial() { let radios = document.getElementsByName('radial_mode'); const mode = Array.from(radios).find(r => r.checked).value ?? 'circle'; radios = document.getElementsByName('radial_pos'); const pos = Array.from(radios).find(r => r.checked).value ?? 'center'; return `radial-gradient(${mode} at ${pos}, ${generateColorList()})`; } /** * @returns {string} */ function generateCssConic() { const rotation = document.getElementById('rotation_conic').value; const showCustomPos = document.getElementById('conic_custom_position').checked; const x = document.getElementById('conic_x').value; const y = document.getElementById('conic_y').value; // have to remove all of this because Revolt doesn't allow it, smh // from ${rotation}deg${showCustomPos ? ` at ${x}% ${y}%` : ''}, return `conic-gradient(${generateColorList()})`; } // Initial update window.onload = () => { updateUi(); };