general fixes & enhancements

This commit is contained in:
2026-08-11 19:08:16 -03:00
parent e08853fe54
commit 31de8265d9
25 changed files with 561 additions and 625 deletions
+2 -6
View File
@@ -2,6 +2,7 @@ import React from 'react';
import Dropdown from '../Dropdown';
import ToggleSwitch from '../ToggleSwitch';
import { Config } from '../../types';
import { SIZE_OPTIONS } from '../utils/styleUtils';
interface ClockTabProps {
config: Config;
@@ -28,12 +29,7 @@ const ClockTab: React.FC<ClockTabProps> = ({ config, onChange }) => {
name="clock.size"
value={config.clock.size}
onChange={(e) => updateClock({ size: e.target.value as string })}
options={[
{ value: 'tiny', label: 'Tiny' },
{ value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' },
]}
options={SIZE_OPTIONS}
/>
</div>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
+2 -6
View File
@@ -1,6 +1,7 @@
import React from 'react';
import Dropdown from '../Dropdown';
import { Config } from '../../types';
import { SIZE_OPTIONS } from '../utils/styleUtils';
interface GeneralTabProps {
config: Config;
@@ -25,12 +26,7 @@ const GeneralTab: React.FC<GeneralTabProps> = ({ config, onChange }) => {
name="titleSize"
value={config.titleSize}
onChange={(e) => onChange({ titleSize: e.target.value as string })}
options={[
{ value: 'tiny', label: 'Tiny' },
{ value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' },
]}
options={SIZE_OPTIONS}
/>
</div>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
+53
View File
@@ -0,0 +1,53 @@
import React from 'react';
type RangeStyle = React.CSSProperties & { '--range-progress': string };
export const getRangeStyle = (value: number, min: number, max: number): RangeStyle => {
const progress = Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100));
return { '--range-progress': `${progress}%` };
};
interface RangeSliderProps {
label: string;
value: number;
min: number;
max: number;
step?: number;
valueSuffix?: string;
formatValue?: (value: number) => string;
onChange: (value: number) => void;
}
const RangeSlider: React.FC<RangeSliderProps> = ({
label,
value,
min,
max,
step = 1,
valueSuffix,
formatValue,
onChange,
}) => {
return (
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">{label}</label>
<div className="flex items-center gap-4">
<input
type="range"
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
className="liquid-range"
style={getRangeStyle(value, min, max)}
/>
<span className="min-w-20 text-right text-sm text-slate-200">
{formatValue ? formatValue(value) : `${value}${valueSuffix ?? ''}`}
</span>
</div>
</div>
);
};
export default RangeSlider;
+12 -37
View File
@@ -2,19 +2,14 @@ import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
import ToggleSwitch from '../ToggleSwitch';
import { Config, Server } from '../../types';
import RangeSlider from './RangeSlider';
import { TrashIcon } from '../icons';
interface ServerWidgetTabProps {
config: Config;
onChange: (updates: Partial<Config>) => void;
}
type RangeStyle = React.CSSProperties & { '--range-progress': string };
const getRangeStyle = (value: number, min: number, max: number): RangeStyle => {
const progress = Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100));
return { '--range-progress': `${progress}%` };
};
const ServerWidgetTab: React.FC<ServerWidgetTabProps> = ({ config, onChange }) => {
const [newServerName, setNewServerName] = useState('');
const [newServerAddress, setNewServerAddress] = useState('');
@@ -60,21 +55,14 @@ const ServerWidgetTab: React.FC<ServerWidgetTabProps> = ({ config, onChange }) =
</div>
{config.serverWidget.enabled && (
<>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">Ping Frequency</label>
<div className="flex items-center gap-4">
<input
type="range"
min="5"
max="60"
value={config.serverWidget.pingFrequency}
onChange={(e) => updateServerWidget({ pingFrequency: Number(e.target.value) })}
className="liquid-range"
style={getRangeStyle(config.serverWidget.pingFrequency, 5, 60)}
/>
<span className="w-12 text-right text-sm text-slate-200">{config.serverWidget.pingFrequency}s</span>
</div>
</div>
<RangeSlider
label="Ping Frequency"
value={config.serverWidget.pingFrequency}
min={5}
max={60}
valueSuffix="s"
onChange={(value) => updateServerWidget({ pingFrequency: value })}
/>
<div>
<h3 className="text-slate-300 text-sm font-semibold mb-2">Servers</h3>
<DragDropContext onDragEnd={onDragEnd}>
@@ -103,20 +91,7 @@ const ServerWidgetTab: React.FC<ServerWidgetTabProps> = ({ config, onChange }) =
className="liquid-edit-action liquid-focus text-red-300 hover:text-red-100"
aria-label={`Remove ${server.name}`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-trash"
viewBox="0 0 16 16"
>
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
<path
fillRule="evenodd"
d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"
/>
</svg>
<TrashIcon size={16} />
</button>
</div>
)}
@@ -156,4 +131,4 @@ const ServerWidgetTab: React.FC<ServerWidgetTabProps> = ({ config, onChange }) =
);
};
export default ServerWidgetTab;
export default ServerWidgetTab;
+45 -112
View File
@@ -1,6 +1,14 @@
import React, { useRef, useState } from 'react';
import React, { useState } from 'react';
import Dropdown from '../Dropdown';
import { Config, Wallpaper } from '../../types';
import RangeSlider from './RangeSlider';
import { TrashIcon } from '../icons';
import {
getWallpaperFrequencyHours,
formatWallpaperFrequency,
MIN_WALLPAPER_FREQUENCY_HOURS,
MAX_WALLPAPER_FREQUENCY_HOURS,
} from '../utils/wallpaperUtils';
interface ThemeTabProps {
config: Config;
@@ -14,31 +22,6 @@ interface ThemeTabProps {
onRandomWallpaper: () => void;
}
type RangeStyle = React.CSSProperties & { '--range-progress': string };
const getRangeStyle = (value: number, min: number, max: number): RangeStyle => {
const progress = Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100));
return { '--range-progress': `${progress}%` };
};
const MIN_WALLPAPER_FREQUENCY_HOURS = 1;
const MAX_WALLPAPER_FREQUENCY_HOURS = 48;
const DEFAULT_WALLPAPER_FREQUENCY_HOURS = 24;
const clampWallpaperFrequencyHours = (hours: number): number =>
Math.min(MAX_WALLPAPER_FREQUENCY_HOURS, Math.max(MIN_WALLPAPER_FREQUENCY_HOURS, Math.round(hours)));
const getWallpaperFrequencyHours = (frequency: string): number => {
const match = frequency.match(/^(\d+)(h|d)$/);
if (!match) return DEFAULT_WALLPAPER_FREQUENCY_HOURS;
const value = Number(match[1]);
const hours = match[2] === 'd' ? value * 24 : value;
return clampWallpaperFrequencyHours(hours);
};
const formatWallpaperFrequency = (hours: number): string =>
`${hours} ${hours === 1 ? 'hour' : 'hours'}`;
const ThemeTab: React.FC<ThemeTabProps> = ({
config,
onChange,
@@ -52,7 +35,6 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
}) => {
const [newWallpaperName, setNewWallpaperName] = useState('');
const [newWallpaperUrl, setNewWallpaperUrl] = useState('');
const fileInputRef = useRef<HTMLInputElement>(null);
const wallpaperFrequencyHours = getWallpaperFrequencyHours(config.wallpaperFrequency);
const handleAddWallpaper = async () => {
@@ -76,8 +58,8 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
if (!file) return;
try {
await onAddWallpaperFile(file);
} catch (error: any) {
alert(error?.message || 'Error adding wallpaper. Please try again.');
} catch (error) {
alert(error instanceof Error ? error.message : 'Error adding wallpaper. Please try again.');
console.error(error);
}
e.target.value = '';
@@ -96,74 +78,39 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
/>
</div>
{Array.isArray(config.currentWallpapers) && config.currentWallpapers.length > 1 && (
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">Change Frequency</label>
<div className="flex items-center gap-4">
<input
type="range"
min={MIN_WALLPAPER_FREQUENCY_HOURS}
max={MAX_WALLPAPER_FREQUENCY_HOURS}
step="1"
value={wallpaperFrequencyHours}
onChange={(e) => onChange({ wallpaperFrequency: `${Number(e.target.value)}h` })}
className="liquid-range"
style={getRangeStyle(
wallpaperFrequencyHours,
MIN_WALLPAPER_FREQUENCY_HOURS,
MAX_WALLPAPER_FREQUENCY_HOURS,
)}
/>
<span className="w-20 text-right text-sm text-slate-200">
{formatWallpaperFrequency(wallpaperFrequencyHours)}
</span>
</div>
</div>
<RangeSlider
label="Change Frequency"
value={wallpaperFrequencyHours}
min={MIN_WALLPAPER_FREQUENCY_HOURS}
max={MAX_WALLPAPER_FREQUENCY_HOURS}
formatValue={formatWallpaperFrequency}
onChange={(value) => onChange({ wallpaperFrequency: `${value}h` })}
/>
)}
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">Wallpaper Blur</label>
<div className="flex items-center gap-4">
<input
type="range"
min="0"
max="50"
value={config.wallpaperBlur}
onChange={(e) => onChange({ wallpaperBlur: Number(e.target.value) })}
className="liquid-range"
style={getRangeStyle(config.wallpaperBlur, 0, 50)}
/>
<span className="w-12 text-right text-sm text-slate-200">{config.wallpaperBlur}px</span>
</div>
</div>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">Wallpaper Brightness</label>
<div className="flex items-center gap-4">
<input
type="range"
min="0"
max="200"
value={config.wallpaperBrightness}
onChange={(e) => onChange({ wallpaperBrightness: Number(e.target.value) })}
className="liquid-range"
style={getRangeStyle(config.wallpaperBrightness, 0, 200)}
/>
<span className="w-12 text-right text-sm text-slate-200">{config.wallpaperBrightness}%</span>
</div>
</div>
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<label className="text-slate-300 text-sm font-semibold">Wallpaper Opacity</label>
<div className="flex items-center gap-4">
<input
type="range"
min="1"
max="100"
value={config.wallpaperOpacity}
onChange={(e) => onChange({ wallpaperOpacity: Number(e.target.value) })}
className="liquid-range"
style={getRangeStyle(config.wallpaperOpacity, 1, 100)}
/>
<span className="w-12 text-right text-sm text-slate-200">{config.wallpaperOpacity}%</span>
</div>
</div>
<RangeSlider
label="Wallpaper Blur"
value={config.wallpaperBlur}
min={0}
max={50}
valueSuffix="px"
onChange={(value) => onChange({ wallpaperBlur: value })}
/>
<RangeSlider
label="Wallpaper Brightness"
value={config.wallpaperBrightness}
min={0}
max={200}
valueSuffix="%"
onChange={(value) => onChange({ wallpaperBrightness: value })}
/>
<RangeSlider
label="Wallpaper Opacity"
value={config.wallpaperOpacity}
min={1}
max={100}
valueSuffix="%"
onChange={(value) => onChange({ wallpaperOpacity: value })}
/>
<div>
<h3 className="text-slate-300 text-sm font-semibold mb-2">User Wallpapers</h3>
<div className="flex flex-col gap-2">
@@ -178,20 +125,7 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
className="liquid-edit-action liquid-focus text-red-300 hover:text-red-100"
aria-label={`Delete ${wallpaper.name}`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-trash"
viewBox="0 0 16 16"
>
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
<path
fillRule="evenodd"
d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"
/>
</svg>
<TrashIcon size={16} />
</button>
</div>
))}
@@ -254,7 +188,6 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
type="file"
className="hidden"
onChange={handleFileUpload}
ref={fileInputRef}
/>
</label>
</div>
@@ -283,4 +216,4 @@ const ThemeTab: React.FC<ThemeTabProps> = ({
);
};
export default ThemeTab;
export default ThemeTab;