mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
add inaccurate color picker; small folder restructure for sidebar tools
This commit is contained in:
parent
bb28c9089c
commit
0c68ec8293
5 changed files with 451 additions and 18 deletions
|
|
@ -455,6 +455,7 @@ $onChatgpt: $onPrimary;
|
|||
background-color: $l_l_t_surfaceVariant;
|
||||
min-width: 1.705rem;
|
||||
min-height: 1.705rem;
|
||||
|
||||
&:hover {
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
|
@ -466,9 +467,74 @@ $onChatgpt: $onPrimary;
|
|||
background-color: $l_l_t_surfaceVariant;
|
||||
min-width: 1.705rem;
|
||||
min-height: 1.705rem;
|
||||
|
||||
&:hover {
|
||||
background-color: $hovercolor;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $activecolor;
|
||||
}
|
||||
}
|
||||
|
||||
$colorpicker_rounding: 0.341rem;
|
||||
|
||||
.sidebar-module-colorpicker-wrapper {
|
||||
padding: 0.341rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-cursorwrapper {
|
||||
padding: 0.341rem 0.136rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-hue {
|
||||
min-height: 13.636rem;
|
||||
min-width: 1.091rem;
|
||||
border-radius: $colorpicker_rounding;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-hue-cursor {
|
||||
background-color: $onBackground;
|
||||
border: 0.136rem solid $onBackground;
|
||||
min-height: 0.136rem;
|
||||
margin-top: -0.136rem;
|
||||
border-radius: $colorpicker_rounding;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-saturationandlightness-wrapper {
|
||||
padding: 0.341rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-saturationandlightness {
|
||||
min-height: 13.636rem;
|
||||
min-width: 13.636rem;
|
||||
border-radius: $colorpicker_rounding;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-saturationandlightness-cursorwrapper {
|
||||
padding: 0.341rem;
|
||||
margin-top: -0.409rem;
|
||||
margin-left: -0.409rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-saturationandlightness-cursor {
|
||||
@include full-rounding;
|
||||
border: 0.136rem solid white;
|
||||
min-width: 0.682rem;
|
||||
min-height: 0.682rem;
|
||||
margin-top: -0.409rem;
|
||||
margin-left: -0.409rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-result-area {
|
||||
padding: 0.341rem;
|
||||
}
|
||||
|
||||
.sidebar-module-colorpicker-result-box {
|
||||
border-radius: $colorpicker_rounding;
|
||||
min-width: 2.045rem;
|
||||
min-height: 0.682rem;
|
||||
padding: 0.341rem;
|
||||
}
|
||||
|
||||
.sidebar-chat-apiswitcher {
|
||||
|
|
@ -778,9 +844,7 @@ $waifu_image_overlay_transparency: 0.7;
|
|||
@include full-rounding;
|
||||
min-width: 1.875rem;
|
||||
min-height: 1.875rem;
|
||||
background-color: rgba(0,
|
||||
0,
|
||||
0,
|
||||
background-color: rgba(0, 0, 0,
|
||||
$waifu_image_overlay_transparency ); // Fixed cuz on image
|
||||
color: rgba(255, 255, 255, $waifu_image_overlay_transparency);
|
||||
}
|
||||
|
|
@ -792,4 +856,4 @@ $waifu_image_overlay_transparency: 0.7;
|
|||
|
||||
.sidebar-waifu-image-action:active {
|
||||
background-color: rgba(60, 60, 60, $waifu_image_overlay_transparency);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { Box, Button, EventBox, Label, Revealer, Scrollable, Stack } = Widget;
|
||||
const { execAsync, exec } = Utils;
|
||||
import { QuickScripts } from './quickscripts.js';
|
||||
import QuickScripts from './tools/quickscripts.js';
|
||||
import ColorPicker from './tools/colorpicker.js';
|
||||
|
||||
export default Scrollable({
|
||||
hscroll: "never",
|
||||
vscroll: "automatic",
|
||||
child: Box({
|
||||
vertical: true,
|
||||
className: 'spacing-v-10',
|
||||
children: [
|
||||
QuickScripts(),
|
||||
ColorPicker(),
|
||||
]
|
||||
})
|
||||
});
|
||||
|
|
|
|||
365
.config/ags/widgets/sideleft/tools/colorpicker.js
Normal file
365
.config/ags/widgets/sideleft/tools/colorpicker.js
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
const { Gtk } = imports.gi;
|
||||
import App from 'resource:///com/github/Aylur/ags/app.js';
|
||||
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
||||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Button, Entry, EventBox, Icon, Label, Overlay, Scrollable } = Widget;
|
||||
import SidebarModule from './module.js';
|
||||
import { MaterialIcon } from '../../../lib/materialicon.js';
|
||||
import { setupCursorHover } from '../../../lib/cursorhover.js';
|
||||
|
||||
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||
function hslToRgbValues(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = (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;
|
||||
};
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const to255 = x => Math.round(x * 255);
|
||||
r = to255(r);
|
||||
g = to255(g);
|
||||
b = to255(b);
|
||||
return `${Math.round(r)},${Math.round(g)},${Math.round(b)}`;
|
||||
// return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
function hslToHex(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = (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;
|
||||
};
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const toHex = x => {
|
||||
const hex = Math.round(x * 255).toString(16);
|
||||
return hex.length === 1 ? "0" + hex : hex;
|
||||
};
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const hue = Variable(198);
|
||||
const xAxis = Variable(94);
|
||||
const yAxis = Variable(80);
|
||||
const alpha = Variable(1);
|
||||
function shouldUseBlackColor() {
|
||||
return ((xAxis.value < 40 || (45 <= hue.value && hue.value <= 195)) &&
|
||||
yAxis.value > 60);
|
||||
}
|
||||
const colorBlack = 'rgba(0,0,0,0.9)';
|
||||
const colorWhite = 'rgba(255,255,255,0.9)';
|
||||
const hueRange = Box({
|
||||
homogeneous: true,
|
||||
className: 'sidebar-module-colorpicker-wrapper',
|
||||
children: [Box({
|
||||
className: 'sidebar-module-colorpicker-hue',
|
||||
css: `background: linear-gradient(to bottom, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);`,
|
||||
})],
|
||||
});
|
||||
const hueSlider = Box({
|
||||
vpack: 'start',
|
||||
className: 'sidebar-module-colorpicker-cursorwrapper',
|
||||
homogeneous: true,
|
||||
children: [Box({
|
||||
className: 'sidebar-module-colorpicker-hue-cursor',
|
||||
})],
|
||||
setup: (self) => self.hook(hue, () => {
|
||||
const widgetHeight = hueRange.children[0].get_allocated_height();
|
||||
self.setCss(`margin-top: ${widgetHeight * hue.value / 360}px;`)
|
||||
}),
|
||||
});
|
||||
const hueSelector = Box({
|
||||
children: [EventBox({
|
||||
child: Overlay({
|
||||
child: hueRange,
|
||||
overlays: [hueSlider],
|
||||
}),
|
||||
attribute: {
|
||||
clicked: false,
|
||||
setHue: (self, event) => {
|
||||
const widgetHeight = hueRange.children[0].get_allocated_height();
|
||||
const [_, cursorX, cursorY] = event.get_coords();
|
||||
const cursorYPercent = clamp(cursorY / widgetHeight, 0, 1);
|
||||
hue.value = Math.round(cursorYPercent * 360);
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.on('motion-notify-event', (self, event) => {
|
||||
if (!self.attribute.clicked) return;
|
||||
self.attribute.setHue(self, event);
|
||||
})
|
||||
.on('button-press-event', (self, event) => {
|
||||
if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
|
||||
self.attribute.clicked = true;
|
||||
self.attribute.setHue(self, event);
|
||||
})
|
||||
.on('button-release-event', (self) => self.attribute.clicked = false)
|
||||
,
|
||||
})]
|
||||
});
|
||||
const saturationAndLightnessRange = Box({
|
||||
homogeneous: true,
|
||||
children: [Box({
|
||||
className: 'sidebar-module-colorpicker-saturationandlightness',
|
||||
setup: (self) => self.hook(hue, () => {
|
||||
// css: `background: linear-gradient(to right, #ffffff, color);`,
|
||||
self.setCss(`background:
|
||||
linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,1)),
|
||||
linear-gradient(to right, #ffffff, ${hslToHex(hue.value, 100, 50)});
|
||||
`);
|
||||
}),
|
||||
})],
|
||||
});
|
||||
const saturationAndLightnessCursor = Box({
|
||||
className: 'sidebar-module-colorpicker-saturationandlightness-cursorwrapper',
|
||||
children: [Box({
|
||||
vpack: 'start',
|
||||
hpack: 'start',
|
||||
homogeneous: true,
|
||||
css: `
|
||||
margin-left: ${13.636 * xAxis.value / 100}rem;
|
||||
margin-top: ${13.636 * (100 - yAxis.value) / 100}rem;
|
||||
`, // Why 13.636rem? see class name in stylesheet
|
||||
children: [Box({
|
||||
className: 'sidebar-module-colorpicker-saturationandlightness-cursor',
|
||||
css: `
|
||||
background-color: ${hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))};
|
||||
border-color: ${shouldUseBlackColor() ? colorBlack : colorWhite};
|
||||
`,
|
||||
attribute: {
|
||||
updateCursorColor: (self) => {
|
||||
self.setCss(`
|
||||
background-color: ${hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))};
|
||||
border-color: ${shouldUseBlackColor() ? colorBlack : colorWhite};
|
||||
`);
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(yAxis, (self) => self.attribute.updateCursorColor(self))
|
||||
.hook(hue, (self) => self.attribute.updateCursorColor(self))
|
||||
,
|
||||
})],
|
||||
attribute: {
|
||||
update: (self) => {
|
||||
const allocation = saturationAndLightnessRange.children[0].get_allocation();
|
||||
self.setCss(`
|
||||
margin-left: ${13.636 * xAxis.value / 100}rem;
|
||||
margin-top: ${13.636 * (100 - yAxis.value) / 100}rem;
|
||||
`); // Why 13.636rem? see class name in stylesheet
|
||||
}
|
||||
},
|
||||
setup: (self) => self.hook(yAxis, (self) => { // And saturation, but both are updated at once so we only need to connect to one
|
||||
self.attribute.update(self);
|
||||
}),
|
||||
})]
|
||||
});
|
||||
const saturationAndLightnessSelector = Box({
|
||||
homogeneous: true,
|
||||
className: 'sidebar-module-colorpicker-saturationandlightness-wrapper',
|
||||
children: [EventBox({
|
||||
child: Overlay({
|
||||
child: saturationAndLightnessRange,
|
||||
overlays: [saturationAndLightnessCursor],
|
||||
}),
|
||||
attribute: {
|
||||
clicked: false,
|
||||
setSaturationAndLightness: (self, event) => {
|
||||
const allocation = saturationAndLightnessRange.children[0].get_allocation();
|
||||
const [_, cursorX, cursorY] = event.get_coords();
|
||||
const cursorXPercent = clamp(cursorX / allocation.width, 0, 1);
|
||||
const cursorYPercent = clamp(cursorY / allocation.height, 0, 1);
|
||||
xAxis.value = Math.round(cursorXPercent * 100);
|
||||
yAxis.value = Math.round(100 - cursorYPercent * 100);
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.on('motion-notify-event', (self, event) => {
|
||||
if (!self.attribute.clicked) return;
|
||||
self.attribute.setSaturationAndLightness(self, event);
|
||||
})
|
||||
.on('button-press-event', (self, event) => {
|
||||
if (!(event.get_button()[1] === 1)) return; // We're only interested in left-click here
|
||||
self.attribute.clicked = true;
|
||||
self.attribute.setSaturationAndLightness(self, event);
|
||||
})
|
||||
.on('button-release-event', (self) => self.attribute.clicked = false)
|
||||
,
|
||||
})]
|
||||
});
|
||||
const resultColorBox = Box({
|
||||
className: 'sidebar-module-colorpicker-result-box',
|
||||
homogeneous: true,
|
||||
css: `background-color: ${hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))};`,
|
||||
children: [Label({
|
||||
className: 'txt txt-small',
|
||||
label: 'Result',
|
||||
}),],
|
||||
attribute: {
|
||||
updateColor: (self) => {
|
||||
self.setCss(`background-color: ${hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))};`);
|
||||
self.children[0].setCss(`color: ${shouldUseBlackColor() ? colorBlack : colorWhite};`)
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(yAxis, (self) => self.attribute.updateColor(self))
|
||||
.hook(hue, (self) => self.attribute.updateColor(self))
|
||||
,
|
||||
});
|
||||
const resultHex = Box({
|
||||
children: [
|
||||
Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Label({
|
||||
xalign: 0,
|
||||
className: 'txt-tiny',
|
||||
label: 'Hex',
|
||||
}),
|
||||
Entry({
|
||||
widthChars: 10,
|
||||
className: 'txt-small techfont',
|
||||
css: 'min-width: 0rem;',
|
||||
attribute: {
|
||||
updateColor: (self) => {
|
||||
self.text = hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100));
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(yAxis, (self) => self.attribute.updateColor(self))
|
||||
.hook(hue, (self) => self.attribute.updateColor(self))
|
||||
,
|
||||
})
|
||||
]
|
||||
}),
|
||||
Button({
|
||||
child: MaterialIcon('content_copy', 'norm'),
|
||||
onClicked: () => Utils
|
||||
.execAsync(['wl-copy', `${hslToHex(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))}`])
|
||||
})
|
||||
]
|
||||
});
|
||||
const resultRgb = Box({
|
||||
children: [
|
||||
Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Label({
|
||||
xalign: 0,
|
||||
className: 'txt-tiny',
|
||||
label: 'RGB',
|
||||
}),
|
||||
Entry({
|
||||
widthChars: 10,
|
||||
className: 'txt-small techfont',
|
||||
css: 'min-width: 0rem;',
|
||||
attribute: {
|
||||
updateColor: (self) => {
|
||||
self.text = hslToRgbValues(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100));
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(yAxis, (self) => self.attribute.updateColor(self))
|
||||
.hook(hue, (self) => self.attribute.updateColor(self))
|
||||
,
|
||||
})
|
||||
]
|
||||
}),
|
||||
Button({
|
||||
child: MaterialIcon('content_copy', 'norm'),
|
||||
onClicked: () => Utils
|
||||
.execAsync(['wl-copy', `rgb(${hslToRgbValues(hue.value, xAxis.value, yAxis.value / (1 + xAxis.value / 100))})`])
|
||||
})
|
||||
]
|
||||
});
|
||||
const resultHsl = Box({
|
||||
children: [
|
||||
Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Label({
|
||||
xalign: 0,
|
||||
className: 'txt-tiny',
|
||||
label: 'HSL',
|
||||
}),
|
||||
Entry({
|
||||
widthChars: 10,
|
||||
className: 'txt-small techfont',
|
||||
css: 'min-width: 0rem;',
|
||||
attribute: {
|
||||
updateColor: (self) => {
|
||||
self.text = `${hue.value},${xAxis.value}%,${Math.round(yAxis.value / (1 + xAxis.value / 100))}%`;
|
||||
}
|
||||
},
|
||||
setup: (self) => self
|
||||
.hook(yAxis, (self) => self.attribute.updateColor(self))
|
||||
.hook(hue, (self) => self.attribute.updateColor(self))
|
||||
,
|
||||
})
|
||||
]
|
||||
}),
|
||||
Button({
|
||||
child: MaterialIcon('content_copy', 'norm'),
|
||||
onClicked: () => Utils
|
||||
.execAsync(['wl-copy', `hsl(${hue.value},${xAxis.value}%,${Math.round(yAxis.value / (1 + xAxis.value / 100))}%)`])
|
||||
})
|
||||
]
|
||||
});
|
||||
const result = Box({
|
||||
className: 'sidebar-module-colorpicker-result-area spacing-v-5 txt',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
resultColorBox,
|
||||
resultHex,
|
||||
resultRgb,
|
||||
resultHsl,
|
||||
]
|
||||
})
|
||||
return SidebarModule({
|
||||
icon: MaterialIcon('colorize', 'norm'),
|
||||
name: 'Color picker',
|
||||
// revealChild: false,
|
||||
child: Box({
|
||||
className: 'spacing-h-5',
|
||||
children: [
|
||||
hueSelector,
|
||||
saturationAndLightnessSelector,
|
||||
result,
|
||||
]
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
@ -1,27 +1,28 @@
|
|||
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
||||
import { setupCursorHover } from '../../lib/cursorhover.js';
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { setupCursorHover } from '../../../lib/cursorhover.js';
|
||||
import { MaterialIcon } from '../../../lib/materialicon.js';
|
||||
const { Box, Button, Icon, Label, Revealer } = Widget;
|
||||
|
||||
export const SidebarModule = ({
|
||||
export default ({
|
||||
icon,
|
||||
name,
|
||||
child
|
||||
child,
|
||||
revealChild = true,
|
||||
}) => {
|
||||
const headerButtonIcon = MaterialIcon('expand_more', 'norm');
|
||||
const headerButtonIcon = MaterialIcon(revealChild ? 'expand_less' : 'expand_more', 'norm');
|
||||
const header = Box({
|
||||
className: 'spacing-h-10',
|
||||
className: 'txt spacing-h-10',
|
||||
children: [
|
||||
icon,
|
||||
Label({
|
||||
className: 'txt-norm txt',
|
||||
className: 'txt-norm',
|
||||
label: `${name}`,
|
||||
}),
|
||||
Box({
|
||||
hexpand: true,
|
||||
}),
|
||||
Button({
|
||||
className: 'sidebar-module-btn-arrow txt',
|
||||
className: 'sidebar-module-btn-arrow',
|
||||
child: headerButtonIcon,
|
||||
onClicked: () => {
|
||||
console.log('clicked');
|
||||
|
|
@ -33,7 +34,7 @@ export const SidebarModule = ({
|
|||
]
|
||||
});
|
||||
const content = Revealer({
|
||||
revealChild: true,
|
||||
revealChild: revealChild,
|
||||
transition: 'slide_down',
|
||||
transitionDuration: 200,
|
||||
child: Box({
|
||||
|
|
@ -4,9 +4,9 @@ import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|||
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
|
||||
const { execAsync, exec } = Utils;
|
||||
const { Box, Button, EventBox, Icon, Label, Scrollable } = Widget;
|
||||
import { SidebarModule } from './module.js';
|
||||
import { MaterialIcon } from '../../lib/materialicon.js';
|
||||
import { setupCursorHover } from '../../lib/cursorhover.js';
|
||||
import SidebarModule from './module.js';
|
||||
import { MaterialIcon } from '../../../lib/materialicon.js';
|
||||
import { setupCursorHover } from '../../../lib/cursorhover.js';
|
||||
|
||||
Gtk.IconTheme.get_default().append_search_path(`${App.configDir}/assets`);
|
||||
const distroID = exec(`bash -c 'cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2'`).trim();
|
||||
|
|
@ -53,7 +53,7 @@ const scripts = [
|
|||
},
|
||||
];
|
||||
|
||||
export const QuickScripts = () => SidebarModule({
|
||||
export default () => SidebarModule({
|
||||
icon: MaterialIcon('code', 'norm'),
|
||||
name: 'Quick scripts',
|
||||
child: Box({
|
||||
Loading…
Reference in a new issue