style: format with prettier

This commit is contained in:
2025-04-18 11:57:50 +02:00
parent 11b9aa9edb
commit 332465b85c
35 changed files with 733 additions and 728 deletions

View File

@@ -4,5 +4,7 @@
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5",
"semi": true
}
"semi": true,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

10
.vscode/settings.json vendored
View File

@@ -1,6 +1,6 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.insertSpaces": true,
"editor.rulers": [100]
}
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.insertSpaces": true,
"editor.rulers": [100]
}

View File

@@ -1,17 +1,17 @@
{
"$schema": "https://next.shadcn-svelte.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "neutral"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
},
"typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
"$schema": "https://next.shadcn-svelte.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "neutral"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
},
"typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
}

View File

@@ -1,37 +1,38 @@
{
"name": "chillhop",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"ui:add": "npx shadcn-svelte@next add",
"name": "chillhop",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"ui:add": "npx shadcn-svelte@next add",
"format": "prettier --write ."
},
"devDependencies": {
"@lucide/svelte": "^0.488.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-cloudflare": "^5.0.1",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"autoprefixer": "^10.4.20",
"bits-ui": "^1.3.19",
"clsx": "^2.1.1",
"prettier": "^3.5.3",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwind-merge": "^3.2.0",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.0.0",
"vite": "^6.0.0",
"wrangler": "^4.12.0"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
},
"devDependencies": {
"@lucide/svelte": "^0.488.0",
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/adapter-cloudflare": "^5.0.1",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"autoprefixer": "^10.4.20",
"bits-ui": "^1.3.19",
"clsx": "^2.1.1",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.3",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwind-merge": "^3.2.0",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.0.0",
"vite": "^6.0.0",
"wrangler": "^4.12.0"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
autoprefixer: {},
},
};

View File

@@ -24,7 +24,7 @@
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 3.9%;
--radius: 1rem;
--sidebar-background: 0 0% 98%;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
@@ -54,7 +54,7 @@
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 83.1%;
--sidebar-background: 240 5.9% 10%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
@@ -72,4 +72,4 @@
body {
@apply bg-background text-foreground m-0 p-0 overflow-hidden;
}
}
}

14
src/app.d.ts vendored
View File

@@ -1,13 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View File

@@ -1,12 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@@ -1,7 +1,8 @@
<video src="https://ch-cdn.srizan.dev/flower-shop-beachside-moewalls-com.mp4"
autoplay
loop
muted
playsinline
class="absolute top-0 left-0 w-full h-full object-cover -z-10"
></video>
<video
src="https://ch-cdn.srizan.dev/flower-shop-beachside-moewalls-com.mp4"
autoplay
loop
muted
playsinline
class="absolute top-0 left-0 w-full h-full object-cover -z-10"
></video>

View File

@@ -1,7 +1,9 @@
<script>
import MusicPlayer from "@/components/app/now-playing.svelte";
import MusicPlayer from '@/components/app/now-playing.svelte';
</script>
<div class="fixed bottom-5 left-2 right-2 z-50 flex items-center p-4 bg-white/10 backdrop-blur-lg rounded-xl shadow-lg">
<div
class="fixed bottom-5 left-2 right-2 z-50 flex items-center p-4 bg-white/10 backdrop-blur-lg rounded-xl shadow-lg"
>
<MusicPlayer />
</div>
</div>

View File

@@ -1,138 +1,143 @@
<script lang="ts">
import { state } from "@/state.svelte";
import { getGeneralData, getStationSongs } from "@/utils";
import { onMount } from "svelte";
// svelte-ignore non_reactive_update
let audioElement: HTMLAudioElement;
let isInteracting = false; // Flag to prevent multiple interactions
import { state } from '@/state.svelte';
import { getGeneralData, getStationSongs } from '@/utils';
import { onMount } from 'svelte';
function togglePlayback(play: boolean) {
if (!audioElement) return;
if (play && state.hasInteracted) {
audioElement.play().catch(() => {
state.error = "Audio playback failed. Please interact with the page first.";
state.isPlaying = false;
});
audioElement.currentTime = state.currentTime;
} else {
audioElement.pause();
}
// svelte-ignore non_reactive_update
let audioElement: HTMLAudioElement;
let isInteracting = false; // Flag to prevent multiple interactions
function togglePlayback(play: boolean) {
if (!audioElement) return;
if (play && state.hasInteracted) {
audioElement.play().catch(() => {
state.error = 'Audio playback failed. Please interact with the page first.';
state.isPlaying = false;
});
audioElement.currentTime = state.currentTime;
} else {
audioElement.pause();
}
}
function playAudio() {
togglePlayback(true);
}
state.togglePlay = () => {
state.isPlaying = !state.isPlaying;
togglePlayback(state.isPlaying);
};
function handleInteraction() {
if (isInteracting) return;
isInteracting = true;
state.hasInteracted = true;
if (state.isPlaying) {
setTimeout(() => {
playAudio();
isInteracting = false;
}, 100);
} else {
isInteracting = false;
}
}
onMount(async () => {
const data = await getGeneralData();
state.presets = data.presets;
state.stations = data.stations;
state.backgrounds = data.backgrounds;
state.atmospheres = data.atmospheres;
if (data.stations.length > 0 && state.currentStation === null) {
state.currentStation = data.stations[0].id;
}
if (data.backgrounds.length > 0 && state.currentBackgroundId === null) {
const firstActiveBg = data.backgrounds.find((bg) => bg.isActive && !bg.parentId);
state.currentBackgroundId = firstActiveBg
? firstActiveBg.id
: data.backgrounds[0]?.id || null;
} else {
state.error = 'Failed to load initial data (empty response).';
}
function playAudio() {
togglePlayback(true);
const stationSongs = await getStationSongs(state.currentStation!);
if (stationSongs) {
state.songQueue = stationSongs;
} else {
state.error = 'Failed to load songs.';
}
state.togglePlay = () => {
state.isPlaying = !state.isPlaying;
togglePlayback(state.isPlaying);
};
function handleInteraction() {
if (isInteracting) return;
isInteracting = true;
state.hasInteracted = true;
if (state.isPlaying) {
setTimeout(() => {
playAudio();
isInteracting = false;
}, 100);
} else {
isInteracting = false;
}
if (state.songQueue.length > 0) {
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
state.error = 'No songs available.';
}
onMount(async () => {
const data = await getGeneralData();
state.presets = data.presets;
state.stations = data.stations;
state.backgrounds = data.backgrounds;
state.atmospheres = data.atmospheres;
const currentTime = new Date().getTime() / 1000;
const startTime = new Date(state.currentSong!.startTime).getTime() / 1000;
const endTime = new Date(state.currentSong!.endTime).getTime() / 1000;
const duration = endTime - startTime;
const elapsed = currentTime - startTime;
if (elapsed > 0 && elapsed < duration) {
state.currentTime = elapsed;
} else {
state.currentTime = 0;
}
if (data.stations.length > 0 && state.currentStation === null) {
state.currentStation = data.stations[0].id;
}
if (data.backgrounds.length > 0 && state.currentBackgroundId === null) {
const firstActiveBg = data.backgrounds.find(bg => bg.isActive && !bg.parentId);
state.currentBackgroundId = firstActiveBg ? firstActiveBg.id : (data.backgrounds[0]?.id || null);
} else {
state.error = "Failed to load initial data (empty response).";
}
state.isLoading = false;
});
const stationSongs = await getStationSongs(state.currentStation!);
if (stationSongs) {
state.songQueue = stationSongs;
} else {
state.error = "Failed to load songs.";
}
if (state.songQueue.length > 0) {
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
state.error = "No songs available.";
}
const currentTime = new Date().getTime() / 1000;
const startTime = new Date(state.currentSong!.startTime).getTime() / 1000;
const endTime = new Date(state.currentSong!.endTime).getTime() / 1000;
const duration = endTime - startTime;
const elapsed = currentTime - startTime;
if (elapsed > 0 && elapsed < duration) {
state.currentTime = elapsed;
} else {
state.currentTime = 0;
}
state.isLoading = false;
});
$effect(() => {
if (!audioElement) return;
togglePlayback(state.isPlaying);
});
$effect(() => {
if (!audioElement) return;
togglePlayback(state.isPlaying);
});
</script>
{#if !state.hasInteracted}
<button class="flex flex-col h-screen w-full items-center justify-center space-y-2 cursor-pointer" onclick={handleInteraction}>
<button
class="flex flex-col h-screen w-full items-center justify-center space-y-2 cursor-pointer"
onclick={handleInteraction}
>
<p>Click anywhere on the screen</p>
</button>
{/if}
{#if !state.isLoading}
<audio
bind:this={audioElement}
src={`https://stream.chillhop.com/mp3/${state.currentSong!.fileId}`}
autoplay
volume={state.volume}
onended={async () => {
state.currentSong = null;
state.currentTime = 0;
<audio
bind:this={audioElement}
src={`https://stream.chillhop.com/mp3/${state.currentSong!.fileId}`}
autoplay
volume={state.volume}
onended={async () => {
state.currentSong = null;
state.currentTime = 0;
state.songQueue.shift();
if (state.songQueue.length > 0) {
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
const stationSongs = await getStationSongs(state.currentStation!);
if (stationSongs) {
state.songQueue = stationSongs;
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
state.error = "Failed to load songs.";
}
}
}}
ontimeupdate={() => {
if (audioElement) {
state.currentTime = audioElement.currentTime;
}
}}
class="hidden"
></audio>
{/if}
state.songQueue.shift();
if (state.songQueue.length > 0) {
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
const stationSongs = await getStationSongs(state.currentStation!);
if (stationSongs) {
state.songQueue = stationSongs;
state.currentSong = state.songQueue[0];
state.duration = state.currentSong.duration;
} else {
state.error = 'Failed to load songs.';
}
}
}}
ontimeupdate={() => {
if (audioElement) {
state.currentTime = audioElement.currentTime;
}
}}
class="hidden"
></audio>
{/if}

View File

@@ -1,19 +1,15 @@
<script lang="ts">
import { Button } from "@/components/ui/button";
import { state } from "@/state.svelte";
import Pause from "@lucide/svelte/icons/pause";
import Play from "@lucide/svelte/icons/play";
import { Button } from '@/components/ui/button';
import { state } from '@/state.svelte';
import Pause from '@lucide/svelte/icons/pause';
import Play from '@lucide/svelte/icons/play';
function togglePlay() {
state.togglePlay();
}
</script>
<img
src={state.currentSong?.image}
alt="Cover Art"
class="size-16 rounded-lg shadow-lg"
/>
<img src={state.currentSong?.image} alt="Cover Art" class="size-16 rounded-lg shadow-lg" />
<div class="flex flex-col ml-4">
<h2 class="text-lg font-semibold">{state.currentSong?.title}</h2>
@@ -26,4 +22,4 @@
{:else}
<Play />
{/if}
</Button>
</Button>

View File

@@ -1,76 +1,71 @@
<script lang="ts" module>
import type { WithElementRef } from "bits-ui";
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
import { type VariantProps, tv } from "tailwind-variants";
import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({
base: "focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
variants: {
variant: {
default: "bg-white/10 backdrop-blur-md hover:bg-white/20 text-foreground border border-white/20 shadow-lg",
olddefault: "bg-primary text-primary-foreground hover:bg-primary/90 shadow",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm",
outline:
"border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
export const buttonVariants = tv({
base: 'focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: {
variant: {
default:
'bg-white/10 backdrop-blur-md hover:bg-white/20 text-foreground border border-white/20 shadow-lg',
olddefault: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm',
outline:
'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
});
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
};
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
};
</script>
<script lang="ts">
import { cn } from "$lib/utils.js";
import { cn } from '$lib/utils.js';
let {
class: className,
variant = "default",
size = "default",
ref = $bindable(null),
href = undefined,
type = "button",
children,
...restProps
}: ButtonProps = $props();
let {
class: className,
variant = 'default',
size = 'default',
ref = $bindable(null),
href = undefined,
type = 'button',
children,
...restProps
}: ButtonProps = $props();
</script>
{#if href}
<a
bind:this={ref}
class={cn(buttonVariants({ variant, size }), className)}
{href}
{...restProps}
>
{@render children?.()}
</a>
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
{@render children?.()}
</a>
{:else}
<button
bind:this={ref}
class={cn(buttonVariants({ variant, size }), className)}
{type}
{...restProps}
>
{@render children?.()}
</button>
<button
bind:this={ref}
class={cn(buttonVariants({ variant, size }), className)}
{type}
{...restProps}
>
{@render children?.()}
</button>
{/if}

View File

@@ -1,17 +1,17 @@
import Root, {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
buttonVariants,
} from "./button.svelte";
type ButtonProps,
type ButtonSize,
type ButtonVariant,
buttonVariants,
} from './button.svelte';
export {
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
};

View File

@@ -1,40 +1,40 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from "bits-ui";
import Check from "@lucide/svelte/icons/check";
import Minus from "@lucide/svelte/icons/minus";
import { cn } from "$lib/utils.js";
import type { Snippet } from "svelte";
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import Check from '@lucide/svelte/icons/check';
import Minus from '@lucide/svelte/icons/minus';
import { cn } from '$lib/utils.js';
import type { Snippet } from 'svelte';
let {
ref = $bindable(null),
class: className,
children: childrenProp,
checked = $bindable(false),
indeterminate = $bindable(false),
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
let {
ref = $bindable(null),
class: className,
children: childrenProp,
checked = $bindable(false),
indeterminate = $bindable(false),
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<DropdownMenuPrimitive.CheckboxItem
bind:ref
bind:checked
bind:indeterminate
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...restProps}
bind:ref
bind:checked
bind:indeterminate
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...restProps}
>
{#snippet children({ checked, indeterminate })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if indeterminate}
<Minus class="size-4" />
{:else}
<Check class={cn("size-4", !checked && "text-transparent")} />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
{#snippet children({ checked, indeterminate })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if indeterminate}
<Minus class="size-4" />
{:else}
<Check class={cn('size-4', !checked && 'text-transparent')} />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</DropdownMenuPrimitive.CheckboxItem>

View File

@@ -1,27 +1,27 @@
<script lang="ts">
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
portalProps,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props();
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
portalProps,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props();
</script>
<DropdownMenuPrimitive.Portal {...portalProps}>
<DropdownMenuPrimitive.Content
bind:ref
{sideOffset}
class={cn(
"bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md",
"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 outline-none",
className
)}
{...restProps}
/>
<DropdownMenuPrimitive.Content
bind:ref
{sideOffset}
class={cn(
'bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
'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 outline-none',
className
)}
{...restProps}
/>
</DropdownMenuPrimitive.Portal>

View File

@@ -1,19 +1,19 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.GroupHeading
bind:ref
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...restProps}
bind:ref
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps}
/>

View File

@@ -1,23 +1,23 @@
<script lang="ts">
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.Item
bind:ref
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8",
className
)}
{...restProps}
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
inset && 'pl-8',
className
)}
{...restProps}
/>

View File

@@ -1,23 +1,23 @@
<script lang="ts">
import { cn } from "$lib/utils.js";
import { type WithElementRef } from "bits-ui";
import type { HTMLAttributes } from "svelte/elements";
import { cn } from '$lib/utils.js';
import { type WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
</script>
<div
bind:this={ref}
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
{...restProps}
bind:this={ref}
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...restProps}
>
{@render children?.()}
{@render children?.()}
</div>

View File

@@ -1,30 +1,30 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from "bits-ui";
import Circle from "@lucide/svelte/icons/circle";
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
import Circle from '@lucide/svelte/icons/circle';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
</script>
<DropdownMenuPrimitive.RadioItem
bind:ref
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...restProps}
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...restProps}
>
{#snippet children({ checked })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if checked}
<Circle class="size-2 fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
{/snippet}
{#snippet children({ checked })}
<span class="absolute left-2 flex size-3.5 items-center justify-center">
{#if checked}
<Circle class="size-2 fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
{/snippet}
</DropdownMenuPrimitive.RadioItem>

View File

@@ -1,16 +1,16 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props();
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props();
</script>
<DropdownMenuPrimitive.Separator
bind:ref
class={cn("bg-muted -mx-1 my-1 h-px", className)}
{...restProps}
bind:ref
class={cn('bg-muted -mx-1 my-1 h-px', className)}
{...restProps}
/>

View File

@@ -1,20 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { type WithElementRef } from "bits-ui";
import { cn } from "$lib/utils.js";
import type { HTMLAttributes } from 'svelte/elements';
import { type WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
class={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...restProps}
bind:this={ref}
class={cn('ml-auto text-xs tracking-widest opacity-60', className)}
{...restProps}
>
{@render children?.()}
{@render children?.()}
</span>

View File

@@ -1,19 +1,19 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SubContentProps = $props();
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SubContentProps = $props();
</script>
<DropdownMenuPrimitive.SubContent
bind:ref
class={cn(
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none",
className
)}
{...restProps}
bind:ref
class={cn(
'bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none',
className
)}
{...restProps}
/>

View File

@@ -1,28 +1,28 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from "bits-ui";
import ChevronRight from "@lucide/svelte/icons/chevron-right";
import { cn } from "$lib/utils.js";
import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
import ChevronRight from '@lucide/svelte/icons/chevron-right';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithoutChild<DropdownMenuPrimitive.SubTriggerProps> & {
inset?: boolean;
} = $props();
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithoutChild<DropdownMenuPrimitive.SubTriggerProps> & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.SubTrigger
bind:ref
class={cn(
"data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
inset && "pl-8",
className
)}
{...restProps}
bind:ref
class={cn(
'data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8',
className
)}
{...restProps}
>
{@render children?.()}
<ChevronRight class="ml-auto" />
{@render children?.()}
<ChevronRight class="ml-auto" />
</DropdownMenuPrimitive.SubTrigger>

View File

@@ -1,14 +1,14 @@
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
import Content from "./dropdown-menu-content.svelte";
import GroupHeading from "./dropdown-menu-group-heading.svelte";
import Item from "./dropdown-menu-item.svelte";
import Label from "./dropdown-menu-label.svelte";
import RadioItem from "./dropdown-menu-radio-item.svelte";
import Separator from "./dropdown-menu-separator.svelte";
import Shortcut from "./dropdown-menu-shortcut.svelte";
import SubContent from "./dropdown-menu-sub-content.svelte";
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
import Content from './dropdown-menu-content.svelte';
import GroupHeading from './dropdown-menu-group-heading.svelte';
import Item from './dropdown-menu-item.svelte';
import Label from './dropdown-menu-label.svelte';
import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from './dropdown-menu-separator.svelte';
import Shortcut from './dropdown-menu-shortcut.svelte';
import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from './dropdown-menu-sub-trigger.svelte';
const Sub = DropdownMenuPrimitive.Sub;
const Root = DropdownMenuPrimitive.Root;
@@ -17,34 +17,34 @@ const Group = DropdownMenuPrimitive.Group;
const RadioGroup = DropdownMenuPrimitive.RadioGroup;
export {
CheckboxItem,
Content,
Root as DropdownMenu,
CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Group as DropdownMenuGroup,
GroupHeading as DropdownMenuGroupHeading,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
RadioGroup as DropdownMenuRadioGroup,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
Trigger as DropdownMenuTrigger,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger,
CheckboxItem,
Content,
Root as DropdownMenu,
CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Group as DropdownMenuGroup,
GroupHeading as DropdownMenuGroupHeading,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
RadioGroup as DropdownMenuRadioGroup,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
Trigger as DropdownMenuTrigger,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger,
};

View File

@@ -1,25 +1,25 @@
import type { Song, Atmosphere, Preset, Station, Background } from "./types"; // Added Preset, Station, Background
import type { Song, Atmosphere, Preset, Station, Background } from './types'; // Added Preset, Station, Background
export const state = $state({
hasInteracted: false,
currentStation: null as number | null,
currentSong: null as Song | null,
songQueue: [] as Song[],
isPlaying: true,
volume: 0.5,
isMuted: false,
currentBackgroundId: null as string | null,
activeAtmospheres: {} as Record<string, number>, // { atmosphereId: volume (0-100) }
isLoading: true,
error: null as string | null,
currentTime: 0,
duration: 0,
hasInteracted: false,
currentStation: null as number | null,
currentSong: null as Song | null,
songQueue: [] as Song[],
isPlaying: true,
volume: 0.5,
isMuted: false,
currentBackgroundId: null as string | null,
activeAtmospheres: {} as Record<string, number>, // { atmosphereId: volume (0-100) }
isLoading: true,
error: null as string | null,
currentTime: 0,
duration: 0,
presets: [] as Preset[],
stations: [] as Station[],
backgrounds: [] as Background[],
atmospheres: [] as Atmosphere[],
// in daemon.svelte
togglePlay: (() => {}) as () => void
presets: [] as Preset[],
stations: [] as Station[],
backgrounds: [] as Background[],
atmospheres: [] as Atmosphere[],
// in daemon.svelte
togglePlay: (() => {}) as () => void,
});

View File

@@ -1,77 +1,76 @@
export interface Song {
id: number
fileId: number
artists: string
title: string
image: string
likes: number
featured?: string
releaseDate: string
releaseDateText: string
duration: number
isrc: string
label: string
spotifyId: string
startTime: string
endTime: string
id: number;
fileId: number;
artists: string;
title: string;
image: string;
likes: number;
featured?: string;
releaseDate: string;
releaseDateText: string;
duration: number;
isrc: string;
label: string;
spotifyId: string;
startTime: string;
endTime: string;
}
export interface Preset {
id: number;
userId: number;
name: string;
backgroundId: string;
stationId: string; // Represented as string in JSON
atmospheres: string; // JSON stringified object
sortOrder: number;
key: string;
id: number;
userId: number;
name: string;
backgroundId: string;
stationId: string; // Represented as string in JSON
atmospheres: string; // JSON stringified object
sortOrder: number;
key: string;
}
export interface StationMetaSocials {
spotify: string | null;
apple: string | null;
spotify: string | null;
apple: string | null;
}
export interface StationMetaIcon {
static: string;
static: string;
}
// Structure within the base64 decoded 'meta' string for stations
export interface DecodedStationMeta {
shortDescription: string;
icon: StationMetaIcon;
socials: StationMetaSocials;
shortDescription: string;
icon: StationMetaIcon;
socials: StationMetaSocials;
}
export interface Station {
name: string;
id: number;
meta: string; // Base64 encoded JSON string (DecodedStationMeta)
name: string;
id: number;
meta: string; // Base64 encoded JSON string (DecodedStationMeta)
}
export interface Background {
id: string;
name: string;
parentId: string | null;
landscapeUrl: string;
portraitUrl: string;
thumbnailUrl: string;
sortOrder: number;
isActive: number; // 0 or 1
id: string;
name: string;
parentId: string | null;
landscapeUrl: string;
portraitUrl: string;
thumbnailUrl: string;
sortOrder: number;
isActive: number; // 0 or 1
}
export interface Atmosphere {
id: string;
name: string;
url: string;
sortOrder: number;
urlMobile: string;
id: string;
name: string;
url: string;
sortOrder: number;
urlMobile: string;
}
export interface ChillhopData {
presets: Preset[];
stations: Station[];
backgrounds: Background[];
atmospheres: Atmosphere[];
}
presets: Preset[];
stations: Station[];
backgrounds: Background[];
atmospheres: Atmosphere[];
}

View File

@@ -1,19 +1,19 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import type { ChillhopData, Song } from "./types";
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import type { ChillhopData, Song } from './types';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
return twMerge(clsx(inputs));
}
export async function getGeneralData() {
const res = await fetch('https://stream.chillhop.com/presets')
const data = await res.json() as ChillhopData;
return data;
const res = await fetch('https://stream.chillhop.com/presets');
const data = (await res.json()) as ChillhopData;
return data;
}
export async function getStationSongs(stationId: number) {
const res = await fetch(`https://stream.chillhop.com/live/${stationId}`)
const data = await res.json() as Song[];
return data;
const res = await fetch(`https://stream.chillhop.com/live/${stationId}`);
const data = (await res.json()) as Song[];
return data;
}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import '../app.css';
let { children } = $props();
import '../app.css';
let { children } = $props();
</script>
{@render children()}

View File

@@ -1,10 +1,9 @@
<script lang="ts">
import BgImage from "@/components/app/bg-image.svelte";
import BottomBar from "@/components/app/bottom-bar.svelte";
import Daemon from "@/components/app/daemon.svelte";
import Spinner from '@lucide/svelte/icons/loader'
import { state } from "@/state.svelte";
import BgImage from '@/components/app/bg-image.svelte';
import BottomBar from '@/components/app/bottom-bar.svelte';
import Daemon from '@/components/app/daemon.svelte';
import Spinner from '@lucide/svelte/icons/loader';
import { state } from '@/state.svelte';
</script>
<BgImage />
@@ -21,9 +20,9 @@
<p>Loading...</p>
</div>
{:else if state.error}
<div class="flex h-screen w-full items-center justify-center text-red-500">
<div class="flex h-screen w-full items-center justify-center text-red-500">
<p>Error: {state.error}</p>
</div>
</div>
{:else if state.hasInteracted}
<BottomBar />
{/if}
{/if}

View File

@@ -1,21 +1,21 @@
import adapter from "@sveltejs/adapter-auto";
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
alias: {
"@/*": "./src/lib/*",
},
}
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
alias: {
'@/*': './src/lib/*',
},
},
};
export default config;

View File

@@ -1,96 +1,96 @@
import { fontFamily } from "tailwindcss/defaultTheme";
import type { Config } from "tailwindcss";
import tailwindcssAnimate from "tailwindcss-animate";
import { fontFamily } from 'tailwindcss/defaultTheme';
import type { Config } from 'tailwindcss';
import tailwindcssAnimate from 'tailwindcss-animate';
const config: Config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
},
secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
},
destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
},
muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
},
accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
},
popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
},
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)"
},
sidebar: {
DEFAULT: "hsl(var(--sidebar-background))",
foreground: "hsl(var(--sidebar-foreground))",
primary: "hsl(var(--sidebar-primary))",
"primary-foreground": "hsl(var(--sidebar-primary-foreground))",
accent: "hsl(var(--sidebar-accent))",
"accent-foreground": "hsl(var(--sidebar-accent-foreground))",
border: "hsl(var(--sidebar-border))",
ring: "hsl(var(--sidebar-ring))",
},
},
borderRadius: {
xl: "calc(var(--radius) + 4px)",
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
fontFamily: {
sans: [...fontFamily.sans]
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--bits-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--bits-accordion-content-height)" },
to: { height: "0" },
},
"caret-blink": {
"0%,70%,100%": { opacity: "1" },
"20%,50%": { opacity: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"caret-blink": "caret-blink 1.25s ease-out infinite",
},
},
},
plugins: [tailwindcssAnimate],
darkMode: ['class'],
content: ['./src/**/*.{html,js,svelte,ts}'],
safelist: ['dark'],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
border: 'hsl(var(--border) / <alpha-value>)',
input: 'hsl(var(--input) / <alpha-value>)',
ring: 'hsl(var(--ring) / <alpha-value>)',
background: 'hsl(var(--background) / <alpha-value>)',
foreground: 'hsl(var(--foreground) / <alpha-value>)',
primary: {
DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
},
secondary: {
DEFAULT: 'hsl(var(--secondary) / <alpha-value>)',
foreground: 'hsl(var(--secondary-foreground) / <alpha-value>)',
},
destructive: {
DEFAULT: 'hsl(var(--destructive) / <alpha-value>)',
foreground: 'hsl(var(--destructive-foreground) / <alpha-value>)',
},
muted: {
DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
},
accent: {
DEFAULT: 'hsl(var(--accent) / <alpha-value>)',
foreground: 'hsl(var(--accent-foreground) / <alpha-value>)',
},
popover: {
DEFAULT: 'hsl(var(--popover) / <alpha-value>)',
foreground: 'hsl(var(--popover-foreground) / <alpha-value>)',
},
card: {
DEFAULT: 'hsl(var(--card) / <alpha-value>)',
foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
},
},
borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
fontFamily: {
sans: [...fontFamily.sans],
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--bits-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--bits-accordion-content-height)' },
to: { height: '0' },
},
'caret-blink': {
'0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'caret-blink': 'caret-blink 1.25s ease-out infinite',
},
},
},
plugins: [tailwindcssAnimate],
};
export default config;

View File

@@ -1,19 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@@ -2,5 +2,5 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
plugins: [sveltekit()],
});

View File

@@ -1539,6 +1539,11 @@ postcss@^8.4.47, postcss@^8.5.3:
picocolors "^1.1.1"
source-map-js "^1.2.1"
prettier-plugin-svelte@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.3.3.tgz#49d5c025a1516063ac7ef026806f880caa310424"
integrity sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==
prettier@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5"