UPD: Change HSLInput to HSV
Ported to color library
This commit is contained in:
35
package-lock.json
generated
35
package-lock.json
generated
@@ -18,6 +18,7 @@
|
||||
"axios": "^1.6.5",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"color": "^4.2.3",
|
||||
"concurrently": "^8.2.2",
|
||||
"lucide-vue-next": "^0.309.0",
|
||||
"radix-vue": "^1.3.0",
|
||||
@@ -4254,6 +4255,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1",
|
||||
"color-string": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -4270,6 +4283,15 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/color-string": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||
"dependencies": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||
@@ -11287,6 +11309,19 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle/node_modules/is-arrayish": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"axios": "^1.6.5",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"color": "^4.2.3",
|
||||
"concurrently": "^8.2.2",
|
||||
"lucide-vue-next": "^0.309.0",
|
||||
"radix-vue": "^1.3.0",
|
||||
|
||||
@@ -1,152 +1,63 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import Color from 'color'
|
||||
import { SliderRoot, SliderThumb, SliderTrack } from 'radix-vue'
|
||||
|
||||
const hueSliderValue = ref(0)
|
||||
const saturationSliderValue = ref(100)
|
||||
const valueSliderValue = ref(50)
|
||||
|
||||
const hueSliderModel = computed({
|
||||
get() {
|
||||
return [hue.value]
|
||||
return [hueSliderValue.value]
|
||||
},
|
||||
set(value) {
|
||||
hue.value = value[0]
|
||||
set(hue) {
|
||||
hueSliderValue.value = hue[0]
|
||||
color.value = color.value.hue(hue[0])
|
||||
},
|
||||
})
|
||||
|
||||
const saturationSliderModel = computed({
|
||||
get() {
|
||||
return [saturation.value]
|
||||
return [saturationSliderValue.value]
|
||||
},
|
||||
set(value) {
|
||||
saturation.value = value[0]
|
||||
set(saturation) {
|
||||
saturationSliderValue.value = saturation[0]
|
||||
color.value = color.value.saturationv(saturation[0])
|
||||
},
|
||||
})
|
||||
|
||||
const lightnessSliderModel = computed({
|
||||
const valueSliderModel = computed({
|
||||
get() {
|
||||
return [lightness.value]
|
||||
return [valueSliderValue.value]
|
||||
},
|
||||
set(value) {
|
||||
lightness.value = value[0]
|
||||
valueSliderValue.value = value[0]
|
||||
color.value = color.value.value(value[0])
|
||||
},
|
||||
})
|
||||
|
||||
const hue = ref(0)
|
||||
const saturation = ref(100)
|
||||
const lightness = ref(50)
|
||||
const color = ref(Color('#FF0000'))
|
||||
|
||||
const sliderColor = computed(() => {
|
||||
return Color.hsv(hueSliderModel.value[0], 100, valueSliderModel.value[0])
|
||||
})
|
||||
|
||||
const hexInput = ref('FF0000')
|
||||
const hueInput = ref('000')
|
||||
const saturationInput = ref('100')
|
||||
const lightnessInput = ref('050')
|
||||
const valueInput = ref('050')
|
||||
const rInput = ref('255')
|
||||
const gInput = ref('000')
|
||||
const bInput = ref('000')
|
||||
|
||||
function hueToRGB(p, q, t) {
|
||||
if (t < 0) t += 1
|
||||
if (t > 1) t -= 1
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t
|
||||
if (t < 1 / 2) return q
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6
|
||||
return p
|
||||
}
|
||||
|
||||
function RGBtoHSL(r, g, b) {
|
||||
r /= 255
|
||||
g /= 255
|
||||
b /= 255
|
||||
|
||||
const max = Math.max(r, g, b)
|
||||
const min = Math.min(r, g, b)
|
||||
let h, s, l = (max + min) / 2
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0 // achromatic
|
||||
} else {
|
||||
const d = max - min
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
|
||||
switch (max) {
|
||||
case r:
|
||||
h = (g - b) / d + (g < b ? 6 : 0)
|
||||
break
|
||||
case g:
|
||||
h = (b - r) / d + 2
|
||||
break
|
||||
case b:
|
||||
h = (r - g) / d + 4
|
||||
break
|
||||
}
|
||||
h /= 6
|
||||
}
|
||||
|
||||
return [h * 360, s * 100, l * 100]
|
||||
}
|
||||
|
||||
function HSLtoRGB(h, s, l) {
|
||||
h /= 360
|
||||
s /= 100
|
||||
l /= 100
|
||||
|
||||
let r, g, b
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l // achromatic
|
||||
} else {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
||||
const p = 2 * l - q
|
||||
r = hueToRGB(p, q, h + 1 / 3)
|
||||
g = hueToRGB(p, q, h)
|
||||
b = hueToRGB(p, q, h - 1 / 3)
|
||||
}
|
||||
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]
|
||||
}
|
||||
|
||||
function hexToRGB(hex) {
|
||||
const r = parseInt(hex.substring(1, 3), 16)
|
||||
const g = parseInt(hex.substring(3, 5), 16)
|
||||
const b = parseInt(hex.substring(5, 7), 16)
|
||||
|
||||
return [r, g, b]
|
||||
}
|
||||
|
||||
function rgbToHex(r, g, b) {
|
||||
const componentToHex = (c) => {
|
||||
const hex = c.toString(16)
|
||||
return hex.length === 1 ? '0' + hex : hex
|
||||
}
|
||||
|
||||
return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b)
|
||||
}
|
||||
|
||||
const rgb = computed({
|
||||
get() {
|
||||
return HSLtoRGB(hue.value, saturation.value, lightness.value)
|
||||
},
|
||||
set([r, g, b]) {
|
||||
const [h, s, l] = RGBtoHSL(r, g, b)
|
||||
hue.value = h
|
||||
saturation.value = s
|
||||
lightness.value = l
|
||||
},
|
||||
})
|
||||
|
||||
const hexCode = computed({
|
||||
get() {
|
||||
return rgbToHex(rgb.value[0], rgb.value[1], rgb.value[2])
|
||||
},
|
||||
set(hex) {
|
||||
const [r, g, b] = hexToRGB(hex)
|
||||
rgb.value = [r, g, b]
|
||||
},
|
||||
})
|
||||
|
||||
function onSubmitHexInput() {
|
||||
let input = hexInput.value
|
||||
if (input[0] !== '#') {
|
||||
input = '#' + input
|
||||
}
|
||||
if (input.match(/^#[0-9A-F]{6}$/i)) {
|
||||
hexCode.value = input
|
||||
color.value = Color(input)
|
||||
} else
|
||||
shake()
|
||||
}
|
||||
@@ -157,11 +68,11 @@ function onSubmitHueInput() {
|
||||
shake()
|
||||
return
|
||||
}
|
||||
const newValue = Math.max(0, Math.min(input, 360))
|
||||
if (newValue === hue.value) {
|
||||
updateHueInput(newValue)
|
||||
const newHue = Math.max(0, Math.min(input, 360))
|
||||
if (newHue === color.value.hue()) {
|
||||
updateInputs()
|
||||
}
|
||||
hue.value = newValue
|
||||
color.value = color.value.hue(newHue)
|
||||
}
|
||||
|
||||
function onSubmitSaturationInput() {
|
||||
@@ -170,24 +81,24 @@ function onSubmitSaturationInput() {
|
||||
shake()
|
||||
return
|
||||
}
|
||||
const newValue = Math.max(0, Math.min(input, 100))
|
||||
if (newValue === saturation.value) {
|
||||
updateSaturationInput(newValue)
|
||||
const newSaturation = Math.max(0, Math.min(input, 100))
|
||||
if (newSaturation === color.value.saturationv()) {
|
||||
updateInputs()
|
||||
}
|
||||
saturation.value = newValue
|
||||
color.value = color.value.saturationv(newSaturation)
|
||||
}
|
||||
|
||||
function onSubmitLightnessInput() {
|
||||
const input = parseInt(lightnessInput.value)
|
||||
function onSubmitValueInput() {
|
||||
const input = parseInt(valueInput.value)
|
||||
if (isNaN(input)) {
|
||||
shake()
|
||||
return
|
||||
}
|
||||
const newValue = Math.max(0, Math.min(input, 100))
|
||||
if (newValue === lightness.value) {
|
||||
updateLightnessInput(newValue)
|
||||
if (newValue === color.value.value()) {
|
||||
updateInputs()
|
||||
}
|
||||
lightness.value = newValue
|
||||
color.value = color.value.value(newValue)
|
||||
}
|
||||
|
||||
function onSubmitRGBInput() {
|
||||
@@ -198,68 +109,27 @@ function onSubmitRGBInput() {
|
||||
shake()
|
||||
return
|
||||
}
|
||||
const newValue = [
|
||||
Math.max(0, Math.min(r, 255)),
|
||||
Math.max(0, Math.min(g, 255)),
|
||||
Math.max(0, Math.min(b, 255)),
|
||||
]
|
||||
if (newValue[0] === rgb.value[0] && newValue[1] === rgb.value[1] && newValue[2] === rgb.value[2]) {
|
||||
updateRInput(newValue[0])
|
||||
updateGInput(newValue[1])
|
||||
updateBInput(newValue[2])
|
||||
const newColor = Color.rgb(r, g, b)
|
||||
if (newColor.hex() === color.value.hex()) {
|
||||
updateInputs()
|
||||
}
|
||||
rgb.value = newValue
|
||||
color.value = newColor
|
||||
}
|
||||
|
||||
function foregroundBlack(r, g, b) {
|
||||
const bgColor = [r / 255, g / 255, b / 255]
|
||||
const c = bgColor.map((col) => {
|
||||
if (col <= 0.03928) {
|
||||
return col / 12.92
|
||||
}
|
||||
return Math.pow((col + 0.055) / 1.055, 2.4)
|
||||
})
|
||||
const l = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2])
|
||||
return l > 0.179
|
||||
function updateInputs() {
|
||||
hexInput.value = color.value.hex().substring(1, 7)
|
||||
hueInput.value = String(parseInt(color.value.hue())).padStart(3, '0')
|
||||
saturationInput.value = String(parseInt(color.value.saturationv())).padStart(3, '0')
|
||||
valueInput.value = String(parseInt(color.value.value())).padStart(3, '0')
|
||||
rInput.value = String(parseInt(color.value.red())).padStart(3, '0')
|
||||
gInput.value = String(parseInt(color.value.green())).padStart(3, '0')
|
||||
bInput.value = String(parseInt(color.value.blue())).padStart(3, '0')
|
||||
hueSliderValue.value = color.value.hue()
|
||||
saturationSliderValue.value = color.value.saturationv()
|
||||
valueSliderValue.value = color.value.value()
|
||||
}
|
||||
|
||||
function updateHexInput(hex) {
|
||||
hexInput.value = hex.substring(1, 7)
|
||||
}
|
||||
|
||||
function updateHueInput(hue) {
|
||||
hueInput.value = String(parseInt(hue)).padStart(3, '0')
|
||||
}
|
||||
|
||||
function updateSaturationInput(saturation) {
|
||||
saturationInput.value = String(parseInt(saturation)).padStart(3, '0')
|
||||
}
|
||||
|
||||
function updateLightnessInput(lightness) {
|
||||
lightnessInput.value = String(parseInt(lightness)).padStart(3, '0')
|
||||
}
|
||||
|
||||
function updateRInput(r) {
|
||||
rInput.value = String(parseInt(r)).padStart(3, '0')
|
||||
}
|
||||
|
||||
function updateGInput(g) {
|
||||
gInput.value = String(parseInt(g)).padStart(3, '0')
|
||||
}
|
||||
|
||||
function updateBInput(b) {
|
||||
bInput.value = String(parseInt(b)).padStart(3, '0')
|
||||
}
|
||||
|
||||
watch(hexCode, updateHexInput)
|
||||
watch(hue, updateHueInput)
|
||||
watch(saturation, updateSaturationInput)
|
||||
watch(lightness, updateLightnessInput)
|
||||
watch(rgb, ([r, g, b]) => {
|
||||
updateRInput(r)
|
||||
updateGInput(g)
|
||||
updateBInput(b)
|
||||
}, { deep: true })
|
||||
watch(color, updateInputs)
|
||||
|
||||
const colorFieldText = ref(null)
|
||||
|
||||
@@ -275,10 +145,10 @@ function shake() {
|
||||
<div>
|
||||
<div
|
||||
class="w-full flex p-4 font-heading"
|
||||
:style="{backgroundColor: `hsl(${hue},${saturation}%,${lightness}%)`}">
|
||||
:style="{backgroundColor: `hsl(${color.hue()},${color.saturationl()}%,${color.lightness()}%)`}">
|
||||
<div
|
||||
ref="colorFieldText" class="w-full flex opacity-50"
|
||||
:class="foregroundBlack(...rgb) ? 'text-black selection:bg-black selection:text-white' : 'selection:bg-white selection:text-black'"
|
||||
:class="color.lighten(0.37).isLight() ? 'text-black selection:bg-black selection:text-white' : 'selection:bg-white selection:text-black'"
|
||||
style="transition: color 0.2s ease-in-out">
|
||||
<div>
|
||||
<form @submit.prevent="onSubmitHueInput">
|
||||
@@ -288,7 +158,7 @@ function shake() {
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateHueInput(hue)">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
<form @submit.prevent="onSubmitSaturationInput">
|
||||
<label for="saturationInput">S: </label><input
|
||||
@@ -297,16 +167,16 @@ function shake() {
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateSaturationInput(saturation)">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
<form @submit.prevent="onSubmitLightnessInput">
|
||||
<label for="lightnessInput">L: </label><input
|
||||
id="lightnessInput"
|
||||
v-model="lightnessInput"
|
||||
<form @submit.prevent="onSubmitValueInput">
|
||||
<label for="valueInput">V: </label><input
|
||||
id="valueInput"
|
||||
v-model="valueInput"
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateLightnessInput(lightness)">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
</div>
|
||||
<div class="mx-auto">
|
||||
@@ -316,7 +186,7 @@ function shake() {
|
||||
v-model="hexInput" maxlength="6"
|
||||
onfocus="this.select()"
|
||||
class="w-16 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateHexInput(hexCode)">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
@@ -327,7 +197,7 @@ function shake() {
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateRInput(rgb[0])">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
<form @submit.prevent="onSubmitRGBInput">
|
||||
<label for="gInput">G: </label><input
|
||||
@@ -336,7 +206,7 @@ function shake() {
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateGInput(rgb[1])">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
<form @submit.prevent="onSubmitRGBInput">
|
||||
<label for="bInput">B: </label><input
|
||||
@@ -345,17 +215,17 @@ function shake() {
|
||||
onfocus="this.select()"
|
||||
type="number" maxlength="3"
|
||||
class="w-8 bg-transparent focus-visible:ring-0 focus-visible:outline-none"
|
||||
@blur="updateBInput(rgb[2])">
|
||||
@blur="updateInputs">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:style="{background: `linear-gradient(180deg, hsla(${hue}, ${saturation}%, ${lightness}%, 30%) 0%, transparent 30%`}">
|
||||
:style="{background: `linear-gradient(180deg, hsla(${color.hue()}, ${color.saturationl()}%, ${color.lightness()}%, 30%) 0%, transparent 30%`}">
|
||||
<div class="px-6 py-4 flex">
|
||||
<p class="font-heading text-muted-foreground w-24">HUE</p>
|
||||
<SliderRoot
|
||||
v-model="hueSliderModel" :max="360"
|
||||
v-model="hueSliderModel" :max="359"
|
||||
class="relative flex w-full touch-none select-none items-center">
|
||||
<SliderTrack
|
||||
class="relative h-2.5 w-full grow overflow-hidden rounded-full border-2 border-zinc-900"
|
||||
@@ -371,19 +241,19 @@ function shake() {
|
||||
class="relative flex w-full touch-none select-none items-center">
|
||||
<SliderTrack
|
||||
class="relative h-2.5 w-full grow overflow-hidden rounded-full border-2 border-zinc-900"
|
||||
:style="{background: `linear-gradient(90deg, hsl(${hue}, 0%, ${lightness}%) 0%, hsl(${hue}, 100%, ${lightness}%) 100%)`}" />
|
||||
:style="{background: `linear-gradient(90deg, hsl(0, 0%, ${sliderColor.lightness()}%) 0%, hsl(${sliderColor.hue()}, 100%, ${sliderColor.lightness()}%) 100%)`}" />
|
||||
<SliderThumb
|
||||
class="block h-5 w-5 rounded-full border hover:bg-zinc-900 border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 cursor-pointer focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderRoot>
|
||||
</div>
|
||||
<div class="px-6 py-4 flex">
|
||||
<p class="font-heading text-muted-foreground w-24">LIT</p>
|
||||
<p class="font-heading text-muted-foreground w-24">VAL</p>
|
||||
<SliderRoot
|
||||
v-model="lightnessSliderModel" :max="100"
|
||||
v-model="valueSliderModel" :max="100"
|
||||
class="relative flex w-full touch-none select-none items-center">
|
||||
<SliderTrack
|
||||
class="relative h-2.5 w-full grow overflow-hidden rounded-full border-2 border-zinc-900"
|
||||
:style="{background: `linear-gradient(90deg, hsl(${hue}, ${saturation}%, 0%) 0%, hsl(${hue}, ${saturation}%, 50%) 50%, hsl(${hue}, ${saturation}%, 100%) 100%)`}" />
|
||||
:style="{background: `linear-gradient(90deg, black, hsl(${sliderColor.hue()}, ${color.saturationl()}%, 50%) 100%`}" />
|
||||
<SliderThumb
|
||||
class="block h-5 w-5 rounded-full border hover:bg-zinc-900 border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 cursor-pointer focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderRoot>
|
||||
@@ -10,13 +10,13 @@
|
||||
<div class="h-6" />
|
||||
<!-- TODO: Instead of 3 color pickers, add a context select above -->
|
||||
<ConfigSection :title="$t('config_options.light_designer.primary_color')" :icon-component="Paintbrush">
|
||||
<HSLInput />
|
||||
<HSVInput />
|
||||
</ConfigSection>
|
||||
<ConfigSection :title="$t('config_options.light_designer.secondary_color')" :icon-component="Brush">
|
||||
<HSLInput />
|
||||
<HSVInput />
|
||||
</ConfigSection>
|
||||
<ConfigSection :title="$t('config_options.light_designer.pointer_color')" :icon-component="Pencil">
|
||||
<HSLInput />
|
||||
<HSVInput />
|
||||
</ConfigSection>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
@@ -26,5 +26,5 @@
|
||||
import { ScrollArea } from '@/components/ui/scroll-area/index.js'
|
||||
import { Lightbulb, Brush, Pencil, Paintbrush } from 'lucide-vue-next'
|
||||
import ConfigSection from '@/components/config/ConfigSection.vue'
|
||||
import HSLInput from '@/components/HSLInput.vue'
|
||||
import HSVInput from '@/components/HSVInput.vue'
|
||||
</script>
|
||||
Reference in New Issue
Block a user