feat(waybar): implement all dummy widgets
This commit is contained in:
32
src/App.tsx
32
src/App.tsx
@@ -3,21 +3,37 @@ import { Kitty } from "./components/Kitty";
|
||||
import { AppProvider } from "./providers/AppProvider";
|
||||
import { Music } from "./components/Music";
|
||||
import { Nvim } from "./components/Nvim";
|
||||
import { Waybar } from "./components/Waybar";
|
||||
import { useApp } from "./hooks/useApp";
|
||||
import { clamp } from "./utils/math";
|
||||
|
||||
export default function App() {
|
||||
const AppRoot = () => {
|
||||
const [loggedIn, setLoggedIn] = useState(false);
|
||||
const { brightness } = useApp();
|
||||
|
||||
const opacity = clamp(0.5 - (0.5 * brightness) / 100, 0, 0.5);
|
||||
|
||||
return (
|
||||
<AppProvider>
|
||||
<>
|
||||
<div
|
||||
className="pointer-events-none fixed inset-0 z-10 bg-black"
|
||||
style={{ opacity }}
|
||||
/>
|
||||
<main className="h-screen w-screen overflow-hidden bg-[url(/wallpaper.jpg)] bg-cover">
|
||||
{loggedIn ? (
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<Kitty className="w-full flex-1 pb-1 pl-2 pr-2 pt-2">
|
||||
<div className="h-full flex-col">
|
||||
<div className="px-2 py-2">
|
||||
<Waybar />
|
||||
</div>
|
||||
|
||||
<div className="flex h-[calc(100%-50px)] w-full flex-col">
|
||||
<Kitty className="w-full flex-1 pb-1 pl-2 pr-2 pt-1">
|
||||
<Nvim />
|
||||
</Kitty>
|
||||
|
||||
<Music />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<button
|
||||
@@ -29,6 +45,14 @@ export default function App() {
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<AppProvider>
|
||||
<AppRoot />
|
||||
</AppProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { WaybarWidgetGroup } from "./WaybarWidgetGroup";
|
||||
import { WaybarCPUWidget } from "./Widgets/WaybarCPUWidget";
|
||||
import { WaybarDiskWidget } from "./Widgets/WaybarDiskWidget";
|
||||
import { WaybarRAMWidget } from "./Widgets/WaybarRAMWidget";
|
||||
import { WaybarTitleWidget } from "./Widgets/WaybarTitleWidget";
|
||||
|
||||
export const Waybar = () => {
|
||||
return (
|
||||
<div className="flex w-full flex-row">
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarCPUWidget variation={10} frequency={1000} cores={10} />
|
||||
<WaybarRAMWidget
|
||||
variation={0.5}
|
||||
frequency={1000}
|
||||
min={18}
|
||||
max={25}
|
||||
start={21}
|
||||
capacity={16}
|
||||
/>
|
||||
<WaybarDiskWidget current={35.9} variation={4.1} capacity={160.3} />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTitleWidget />
|
||||
</WaybarWidgetGroup>
|
||||
{/*
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarLockWidget />
|
||||
<WaybarTimeWidget />
|
||||
<WaybarShutdownWidget />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTemperatureWidget />
|
||||
<WaybarBatteryWidget />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarBrightnessWidget />
|
||||
<WaybarVolumeWidget />
|
||||
<WaybarMicrophoneWidget />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTrayWidget />
|
||||
<WaybarWeatherWidget />
|
||||
</WaybarWidgetGroup>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -4,9 +4,16 @@ import { cn } from "~/utils/react";
|
||||
export const WaybarWidget = (props: {
|
||||
className?: string;
|
||||
tooltip?: string;
|
||||
interactable?: boolean;
|
||||
children: ReactNode | Array<ReactNode>;
|
||||
}) => (
|
||||
<div className={cn("font-bold text-[#2b2b2c] ", props.className)}>
|
||||
<div
|
||||
className={cn(
|
||||
"py-[6.5px] font-bold text-[#2b2b2c]",
|
||||
props.className,
|
||||
props.interactable && "cursor-pointer",
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { type ReactNode } from "react";
|
||||
import { cn } from "~/utils/react";
|
||||
|
||||
export const WaybarWidgetGroup = (props: {
|
||||
className?: string;
|
||||
children: ReactNode | Array<ReactNode>;
|
||||
}) => (
|
||||
<div className="flex flex-row justify-between gap-5 rounded-lg bg-[#e6e7ec] px-4 py-[5px] opacity-80">
|
||||
<div
|
||||
className={cn(
|
||||
`flex flex-row justify-between rounded-[10px] bg-[#e6e7ec] opacity-80`,
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
// gap-5 px-3 py-[6.5px]
|
||||
|
||||
27
src/components/Waybar/Widgets/WaybarBatteryWidget.tsx
Normal file
27
src/components/Waybar/Widgets/WaybarBatteryWidget.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
import { lerpIcon } from "~/utils/icons";
|
||||
|
||||
const ICONS = ["", "", "", "", "", "", "", "", "", "", ""];
|
||||
|
||||
export const WaybarBatteryWidget = (props: { frequency: number }) => {
|
||||
const [battery, setBattery] = useState(100);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setBattery((x) => x - 1);
|
||||
|
||||
if (battery - 1 === 0) {
|
||||
// TODO: do something
|
||||
}
|
||||
}, props.frequency);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
return (
|
||||
<WaybarWidget className="pl-[0.625rem] pr-3 text-[#1d7715]">
|
||||
{lerpIcon(ICONS, battery, 100)} {battery}%
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
26
src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx
Normal file
26
src/components/Waybar/Widgets/WaybarBrightnessWidget.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { type WheelEvent } from "react";
|
||||
import { clamp } from "~/utils/math";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
import { useApp } from "~/hooks/useApp";
|
||||
import { lerpIcon } from "~/utils/icons";
|
||||
|
||||
const ICONS = ["", "", ""];
|
||||
|
||||
export const WaybarBrightnessWidget = () => {
|
||||
const { brightness, setBrightness } = useApp();
|
||||
|
||||
const handleScroll = (e: WheelEvent) => {
|
||||
let newBrightness = brightness - Math.sign(e.deltaY);
|
||||
newBrightness = clamp(newBrightness, 0, 100);
|
||||
|
||||
setBrightness(newBrightness);
|
||||
};
|
||||
|
||||
return (
|
||||
<WaybarWidget className="pl-3 pr-[0.625rem]">
|
||||
<span onWheel={handleScroll}>
|
||||
{lerpIcon(ICONS, brightness, 100)} {brightness}%
|
||||
</span>
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
// 1 is really active, but often changes, 19-30%
|
||||
@@ -11,10 +11,6 @@ export const WaybarCPUWidget = (props: {
|
||||
frequency: number;
|
||||
}) => {
|
||||
const [usage, setUsage] = useState(new Array<number>(props.cores).fill(0));
|
||||
const totalUsage = useMemo(
|
||||
() => Math.round(usage.reduce((acc, v) => acc + v, 0) / usage.length),
|
||||
[usage],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
@@ -23,7 +19,13 @@ export const WaybarCPUWidget = (props: {
|
||||
}, props.frequency);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [usage]);
|
||||
}, [usage, props.frequency]);
|
||||
|
||||
return <WaybarWidget> {totalUsage}%</WaybarWidget>;
|
||||
const totalUsage = Math.round(
|
||||
usage.reduce((acc, v) => acc + v, 0) / usage.length,
|
||||
);
|
||||
|
||||
return (
|
||||
<WaybarWidget className="pl-3 pr-[0.625rem]"> {totalUsage}%</WaybarWidget>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,5 +12,5 @@ export const WaybarDiskWidget = (props: {
|
||||
props.current + randomMinMax(-props.variation, props.variation + 1);
|
||||
const usage = Math.round((value / props.capacity) * 100);
|
||||
|
||||
return <WaybarWidget> {usage}%</WaybarWidget>;
|
||||
return <WaybarWidget className="pl-[0.625rem] pr-3"> {usage}%</WaybarWidget>;
|
||||
};
|
||||
|
||||
5
src/components/Waybar/Widgets/WaybarHomeWidget.tsx
Normal file
5
src/components/Waybar/Widgets/WaybarHomeWidget.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarHomeWidget = () => (
|
||||
<WaybarWidget className="text-[#407cdd]"></WaybarWidget>
|
||||
);
|
||||
9
src/components/Waybar/Widgets/WaybarLockWidget.tsx
Normal file
9
src/components/Waybar/Widgets/WaybarLockWidget.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarLockWidget = () => {
|
||||
return (
|
||||
<WaybarWidget className="pl-3 pr-[0.625rem]" interactable>
|
||||
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
33
src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx
Normal file
33
src/components/Waybar/Widgets/WaybarMicrophoneWidget.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { type WheelEvent, useState } from "react";
|
||||
import { clamp } from "~/utils/math";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarMicrophoneWidget = () => {
|
||||
const [muted, setMuted] = useState(false);
|
||||
const [volume, setVolume] = useState(100);
|
||||
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
let newVolume = volume - Math.sign(e.deltaY) * 5;
|
||||
newVolume = clamp(newVolume, 0, 100);
|
||||
|
||||
setVolume(newVolume);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
setMuted((muted) => !muted);
|
||||
};
|
||||
|
||||
const icon = muted ? "" : "";
|
||||
|
||||
return (
|
||||
<WaybarWidget className="pl-[0.625rem] pr-3" interactable>
|
||||
<span
|
||||
className="text-[#ad6bfd]"
|
||||
onWheel={handleWheel}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{icon} {!muted && `${volume}%`}
|
||||
</span>
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
9
src/components/Waybar/Widgets/WaybarPowerWidget.tsx
Normal file
9
src/components/Waybar/Widgets/WaybarPowerWidget.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarPowerWidget = () => {
|
||||
return (
|
||||
<WaybarWidget className="pl-[0.625rem] pr-3" interactable>
|
||||
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
@@ -26,5 +26,5 @@ export const WaybarRAMWidget = (props: {
|
||||
|
||||
// TODO: tooltip
|
||||
// Memory - (capacity * usage).1f GB used
|
||||
return <WaybarWidget> {usage}%</WaybarWidget>;
|
||||
return <WaybarWidget className="px-[0.625rem]"> {usage}%</WaybarWidget>;
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { WaybarWidget } from "./WaybarWidget";
|
||||
|
||||
export const WaybarSessionWidget = () => (
|
||||
<WaybarWidget>
|
||||
<span></span>
|
||||
<span>03:13 PM</span>
|
||||
<span></span>
|
||||
</WaybarWidget>
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
import { clamp, randomMinMax } from "~/utils/math";
|
||||
|
||||
export const WaybarTemperatureWidget = (props: {
|
||||
min: number;
|
||||
max: number;
|
||||
variation: number;
|
||||
frequency: number;
|
||||
}) => {
|
||||
const [temperature, setTemperature] = useState(
|
||||
randomMinMax(props.min, props.max),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
const offset = randomMinMax(-props.variation, props.variation + 1);
|
||||
setTemperature((x) => clamp(x + offset, props.min, props.max));
|
||||
}, props.frequency);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
return (
|
||||
<WaybarWidget className="pl-3 pr-[0.625rem]">
|
||||
{temperature}°C
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
|
||||
24
src/components/Waybar/Widgets/WaybarTimeWidget.tsx
Normal file
24
src/components/Waybar/Widgets/WaybarTimeWidget.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarTimeWidget = () => {
|
||||
const [date, setDate] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setDate(new Date());
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
});
|
||||
|
||||
const time = date.toLocaleString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
});
|
||||
|
||||
return <WaybarWidget className="px-[0.625rem]">{time}</WaybarWidget>;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarTitleWidget = () => <WaybarWidget>nvim</WaybarWidget>;
|
||||
export const WaybarTitleWidget = () => (
|
||||
<WaybarWidget className="px-3">nvim</WaybarWidget>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarToggleThemeWidget = () => (
|
||||
<WaybarWidget className="pl-[0.625rem] pr-3" interactable>
|
||||
|
||||
</WaybarWidget>
|
||||
);
|
||||
5
src/components/Waybar/Widgets/WaybarTrayWidget.tsx
Normal file
5
src/components/Waybar/Widgets/WaybarTrayWidget.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarTrayWidget = () => (
|
||||
<WaybarWidget className="pl-3 pr-[0.625rem]">X</WaybarWidget>
|
||||
);
|
||||
37
src/components/Waybar/Widgets/WaybarVolumeWidget.tsx
Normal file
37
src/components/Waybar/Widgets/WaybarVolumeWidget.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { type WheelEvent, useState } from "react";
|
||||
import { clamp } from "~/utils/math";
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
import { useApp } from "~/hooks/useApp";
|
||||
import { lerpIcon } from "~/utils/icons";
|
||||
|
||||
const ICONS = ["", "", ""];
|
||||
|
||||
export const WaybarVolumeWidget = () => {
|
||||
const [muted, setMuted] = useState(false);
|
||||
const { volume, setVolume } = useApp();
|
||||
|
||||
const handleWheel = (e: WheelEvent) => {
|
||||
let newVolume = volume - Math.sign(e.deltaY) * 5;
|
||||
newVolume = clamp(newVolume, 0, 100);
|
||||
|
||||
setVolume(newVolume);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
setMuted(!muted);
|
||||
};
|
||||
|
||||
const icon = muted ? "" : lerpIcon(ICONS, volume, 100);
|
||||
|
||||
return (
|
||||
<WaybarWidget className="px-[0.625rem]" interactable>
|
||||
<span
|
||||
className="text-[#407cdd]"
|
||||
onWheel={handleWheel}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{icon} {!muted && `${volume}%`}
|
||||
</span>
|
||||
</WaybarWidget>
|
||||
);
|
||||
};
|
||||
5
src/components/Waybar/Widgets/WaybarWeatherWidget.tsx
Normal file
5
src/components/Waybar/Widgets/WaybarWeatherWidget.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { WaybarWidget } from "../WaybarWidget";
|
||||
|
||||
export const WaybarWeatherWidget = () => (
|
||||
<WaybarWidget className="px-[0.625rem]">🌧️ 27°</WaybarWidget>
|
||||
);
|
||||
78
src/components/Waybar/index.tsx
Normal file
78
src/components/Waybar/index.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { WaybarWidgetGroup } from "./WaybarWidgetGroup";
|
||||
import { WaybarCPUWidget } from "./Widgets/WaybarCPUWidget";
|
||||
import { WaybarDiskWidget } from "./Widgets/WaybarDiskWidget";
|
||||
import { WaybarRAMWidget } from "./Widgets/WaybarRAMWidget";
|
||||
import { WaybarTitleWidget } from "./Widgets/WaybarTitleWidget";
|
||||
import { WaybarHomeWidget } from "./Widgets/WaybarHomeWidget";
|
||||
import { randomMinMax } from "~/utils/math";
|
||||
import { WaybarTemperatureWidget } from "./Widgets/WaybarTemperatureWidget";
|
||||
import { WaybarBatteryWidget } from "./Widgets/WaybarBatteryWidget";
|
||||
import { WaybarBrightnessWidget } from "./Widgets/WaybarBrightnessWidget";
|
||||
import { WaybarVolumeWidget } from "./Widgets/WaybarVolumeWidget";
|
||||
import { WaybarMicrophoneWidget } from "./Widgets/WaybarMicrophoneWidget";
|
||||
import { WaybarLockWidget } from "./Widgets/WaybarLockWidget";
|
||||
import { WaybarTimeWidget } from "./Widgets/WaybarTimeWidget";
|
||||
import { WaybarPowerWidget } from "./Widgets/WaybarPowerWidget";
|
||||
import { WaybarTrayWidget } from "./Widgets/WaybarTrayWidget";
|
||||
import { WaybarToggleThemeWidget } from "./Widgets/WaybarToggleThemeWidget";
|
||||
import { WaybarWeatherWidget } from "./Widgets/WaybarWeatherWidget";
|
||||
|
||||
export const Waybar = () => {
|
||||
return (
|
||||
<div className="grid h-[37px] w-full select-none grid-cols-[1fr_max-content_1fr] grid-rows-1 gap-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<WaybarWidgetGroup className="rounded-r-[5px] pl-[10px] pr-[14px]">
|
||||
<WaybarHomeWidget />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarCPUWidget variation={2} frequency={1000} cores={10} />
|
||||
<WaybarRAMWidget
|
||||
variation={1}
|
||||
frequency={3000}
|
||||
min={18}
|
||||
max={40}
|
||||
start={randomMinMax(20, 30)}
|
||||
capacity={16}
|
||||
/>
|
||||
<WaybarDiskWidget current={35.9} variation={4.1} capacity={160.3} />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTitleWidget />
|
||||
</WaybarWidgetGroup>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarLockWidget />
|
||||
<WaybarTimeWidget />
|
||||
<WaybarPowerWidget />
|
||||
</WaybarWidgetGroup>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTemperatureWidget
|
||||
min={50}
|
||||
max={70}
|
||||
variation={1}
|
||||
frequency={7000}
|
||||
/>
|
||||
|
||||
<WaybarBatteryWidget frequency={7000} />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarBrightnessWidget />
|
||||
<WaybarVolumeWidget />
|
||||
<WaybarMicrophoneWidget />
|
||||
</WaybarWidgetGroup>
|
||||
|
||||
<WaybarWidgetGroup>
|
||||
<WaybarTrayWidget />
|
||||
<WaybarWeatherWidget />
|
||||
<WaybarToggleThemeWidget />
|
||||
</WaybarWidgetGroup>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createContext } from "react";
|
||||
import { type Prettify, type State } from "~/utils/types";
|
||||
|
||||
export const AppContext = createContext<
|
||||
| {
|
||||
activeKitty: string;
|
||||
setActiveKitty: (value: string) => void;
|
||||
}
|
||||
| Prettify<
|
||||
State<"activeKitty", string> &
|
||||
State<"brightness", number> &
|
||||
State<"volume", number>
|
||||
>
|
||||
| undefined
|
||||
>(undefined);
|
||||
|
||||
@@ -3,8 +3,20 @@ import { AppContext } from "~/context/AppContext";
|
||||
|
||||
export const AppProvider = (props: { children?: ReactNode }) => {
|
||||
const [activeKitty, setActiveKitty] = useState(":r0:");
|
||||
const [brightness, setBrightness] = useState(100);
|
||||
const [volume, setVolume] = useState(100);
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ activeKitty, setActiveKitty }}>
|
||||
<AppContext.Provider
|
||||
value={{
|
||||
activeKitty,
|
||||
setActiveKitty,
|
||||
brightness,
|
||||
setBrightness,
|
||||
volume,
|
||||
setVolume,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { type Child, type Icon } from "./tree";
|
||||
|
||||
export const lerpIcon = (icons: Array<string>, value: number, max: number) =>
|
||||
icons[Math.floor((value / max) * icons.length)] ?? icons[icons.length - 1];
|
||||
|
||||
export const getIcon = (file: Child | string | undefined): Icon => {
|
||||
if (!file) return DEFAULT_ICON;
|
||||
|
||||
|
||||
5
src/utils/math.ts
Normal file
5
src/utils/math.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const clamp = (v: number, min: number, max: number) =>
|
||||
Math.min(Math.max(v, min), max);
|
||||
|
||||
export const randomMinMax = (min: number, max: number) =>
|
||||
Math.floor(Math.random() * (max - min) + min);
|
||||
4
src/utils/react.ts
Normal file
4
src/utils/react.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { type ClassNameValue, twMerge } from "tailwind-merge";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
export const cn = (...classes: Array<ClassNameValue>) => twMerge(clsx(classes));
|
||||
@@ -6,3 +6,12 @@ export type Prettify<T> = NonNullable<{ [K in keyof T]: T[K] }>;
|
||||
export type InnerKittyProps<T extends (...args: any[]) => any> = Prettify<
|
||||
Parameters<T>[0] & KittyContextProps
|
||||
>;
|
||||
|
||||
export type State<
|
||||
Name extends string,
|
||||
T,
|
||||
> = Name extends `${infer First}${infer Rest}`
|
||||
? {
|
||||
[K in Name]: T;
|
||||
} & { [K in `set${Capitalize<First>}${Rest}`]: (value: T) => void }
|
||||
: never;
|
||||
|
||||
Reference in New Issue
Block a user