mirror of
https://github.com/SrIzan10/lofi.git
synced 2026-06-06 00:56:53 +00:00
feat: fine tuned bg analyzer and non glassmorphic volume slider
This commit is contained in:
@@ -11,10 +11,10 @@
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
|
||||
const sourceY = Math.floor(video.videoHeight * 0.67); // take the top two-thirds
|
||||
const sourceHeight = Math.floor(video.videoHeight * 0.33); // take the bottom third
|
||||
const sourceY = Math.floor(video.videoHeight * 0.8);
|
||||
// Sample only 20% of the video height
|
||||
const sourceHeight = Math.floor(video.videoHeight * 0.2);
|
||||
|
||||
// draw the bottom third of the video to the canvas
|
||||
// prettier-ignore
|
||||
ctx.drawImage(
|
||||
video,
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
|
||||
const avgBrightness = totalBrightness / pixelCount;
|
||||
const isDark = avgBrightness < 0.45;
|
||||
const isDark = avgBrightness < 0.3;
|
||||
|
||||
setMode(isDark ? 'dark' : 'light');
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { state } from '@/state.svelte';
|
||||
import Pause from '@lucide/svelte/icons/pause';
|
||||
import Play from '@lucide/svelte/icons/play';
|
||||
import Volume from './volume.svelte';
|
||||
|
||||
function togglePlay() {
|
||||
state.togglePlay();
|
||||
@@ -16,11 +17,14 @@
|
||||
<p class="text-sm">{state.currentSong?.artists}</p>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<Button size="icon" onclick={togglePlay} class="w-10 h-10 md:ml-4">
|
||||
{#if state.isPlaying}
|
||||
<Pause />
|
||||
{:else}
|
||||
<Play />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="gap-4">
|
||||
<Button size="icon" onclick={togglePlay} class="size-10 md:ml-4">
|
||||
{#if state.isPlaying}
|
||||
<Pause />
|
||||
{:else}
|
||||
<Play />
|
||||
{/if}
|
||||
</Button>
|
||||
<Volume />
|
||||
</div>
|
||||
</div>
|
||||
34
src/lib/components/app/volume.svelte
Normal file
34
src/lib/components/app/volume.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { Slider } from "$lib/components/ui/slider";
|
||||
import * as Popover from "$lib/components/ui/popover";
|
||||
import { state as appState } from "@/state.svelte";
|
||||
import VolumeZero from "@lucide/svelte/icons/volume";
|
||||
import VolumeOne from "@lucide/svelte/icons/volume-1";
|
||||
import VolumeTwo from "@lucide/svelte/icons/volume-2";
|
||||
import VolumeX from "@lucide/svelte/icons/volume-x";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
let value = $state(appState.volume);
|
||||
$effect(() => {
|
||||
appState.volume = value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Popover.Root>
|
||||
<Popover.Trigger>
|
||||
<Button size="icon" class="size-10">
|
||||
{#if value === 0}
|
||||
<VolumeX />
|
||||
{:else if value > 0 && value <= 0.4}
|
||||
<VolumeZero />
|
||||
{:else if value > 0.4 && value <= 0.8}
|
||||
<VolumeOne />
|
||||
{:else}
|
||||
<VolumeTwo />
|
||||
{/if}
|
||||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="w-2 h-32" side="top">
|
||||
<Slider type="single" orientation="vertical" bind:value max={1} step={0.01} />
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
17
src/lib/components/ui/popover/index.ts
Normal file
17
src/lib/components/ui/popover/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Popover as PopoverPrimitive } from "bits-ui";
|
||||
import Content from "./popover-content.svelte";
|
||||
const Root = PopoverPrimitive.Root;
|
||||
const Trigger = PopoverPrimitive.Trigger;
|
||||
const Close = PopoverPrimitive.Close;
|
||||
|
||||
export {
|
||||
Root,
|
||||
Content,
|
||||
Trigger,
|
||||
Close,
|
||||
//
|
||||
Root as Popover,
|
||||
Content as PopoverContent,
|
||||
Trigger as PopoverTrigger,
|
||||
Close as PopoverClose,
|
||||
};
|
||||
28
src/lib/components/ui/popover/popover-content.svelte
Normal file
28
src/lib/components/ui/popover/popover-content.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { Popover as PopoverPrimitive } from "bits-ui";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
align = "center",
|
||||
sideOffset = 4,
|
||||
portalProps,
|
||||
...restProps
|
||||
}: PopoverPrimitive.ContentProps & {
|
||||
portalProps?: PopoverPrimitive.PortalProps;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<PopoverPrimitive.Portal {...portalProps}>
|
||||
<PopoverPrimitive.Content
|
||||
bind:ref
|
||||
{align}
|
||||
{sideOffset}
|
||||
class={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
7
src/lib/components/ui/slider/index.ts
Normal file
7
src/lib/components/ui/slider/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import Root from "./slider.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Slider,
|
||||
};
|
||||
44
src/lib/components/ui/slider/slider.svelte
Normal file
44
src/lib/components/ui/slider/slider.svelte
Normal file
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
import { Slider as SliderPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(),
|
||||
orientation = "horizontal",
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SliderPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<!--
|
||||
Discriminated Unions + Destructing (required for bindable) do not
|
||||
get along, so we shut typescript up by casting `value` to `never`.
|
||||
-->
|
||||
<SliderPrimitive.Root
|
||||
bind:ref
|
||||
bind:value={value as never}
|
||||
{orientation}
|
||||
class={cn(
|
||||
"relative flex touch-none select-none items-center data-[orientation='vertical']:h-full data-[orientation='horizontal']:w-full data-[orientation='vertical']:w-auto data-[orientation='vertical']:flex-col",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ thumbs })}
|
||||
<span
|
||||
data-orientation={orientation}
|
||||
class="bg-primary/20 relative grow overflow-hidden rounded-full data-[orientation='horizontal']:h-1.5 data-[orientation='vertical']:h-full data-[orientation='horizontal']:w-full data-[orientation='vertical']:w-1.5"
|
||||
>
|
||||
<SliderPrimitive.Range
|
||||
class="bg-primary absolute data-[orientation='horizontal']:h-full data-[orientation='vertical']:w-full"
|
||||
/>
|
||||
</span>
|
||||
{#each thumbs as thumb (thumb)}
|
||||
<SliderPrimitive.Thumb
|
||||
index={thumb}
|
||||
class="border-primary/50 bg-background focus-visible:ring-ring block size-4 rounded-full border shadow transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50"
|
||||
/>
|
||||
{/each}
|
||||
{/snippet}
|
||||
</SliderPrimitive.Root>
|
||||
Reference in New Issue
Block a user