Skip to content

Commit 8527b78

Browse files
committed
feat: migrate to Svelte 5 and Tailwind 4, update shadcn-svelte component, fix a few bugs
1 parent 15584e9 commit 8527b78

68 files changed

Lines changed: 1630 additions & 899 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 714 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bun.lockb

-174 KB
Binary file not shown.

components.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
"$schema": "https://shadcn-svelte.com/schema.json",
33
"style": "default",
44
"tailwind": {
5-
"config": "tailwind.config.js",
65
"css": "src/app.css",
76
"baseColor": "slate"
87
},
98
"aliases": {
109
"components": "$lib/components",
11-
"utils": "$lib/utils"
10+
"utils": "$lib/utils",
11+
"ui": "$lib/components/ui",
12+
"hooks": "$lib/hooks",
13+
"lib": "$lib"
1214
},
13-
"typescript": true
15+
"typescript": true,
16+
"registry": "https://shadcn-svelte.com/registry"
1417
}

package.json

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,45 @@
1313
"server": "vite build && bun server.ts"
1414
},
1515
"devDependencies": {
16+
"@internationalized/date": "^3.8.1",
17+
"@lucide/svelte": "^0.515.0",
1618
"@sveltejs/adapter-node": "^5.3.1",
17-
"@sveltejs/kit": "^2.36.1",
18-
"@sveltejs/vite-plugin-svelte": "^3.1.2",
19+
"@sveltejs/kit": "^2.36.2",
20+
"@sveltejs/vite-plugin-svelte": "^6.1.3",
1921
"@tailwindcss/typography": "^0.5.16",
2022
"@tailwindcss/vite": "^4.1.12",
2123
"@types/dompurify": "^3.2.0",
22-
"@types/express": "^4.17.23",
24+
"@types/express": "^5.0.3",
2325
"@types/katex": "^0.16.7",
2426
"autoprefixer": "^10.4.21",
27+
"mode-watcher": "^1.0.8",
2528
"postcss": "^8.5.6",
2629
"prettier": "^3.6.2",
2730
"prettier-plugin-svelte": "^3.4.0",
28-
"svelte": "^4.2.20",
29-
"svelte-check": "^3.8.6",
30-
"svelte-confetti": "^1.4.0",
31+
"svelte": "^5.38.3",
32+
"svelte-check": "^4.3.1",
33+
"svelte-confetti": "^2.3.2",
3134
"tailwindcss": "^4.1.12",
3235
"tslib": "^2.8.1",
36+
"tw-animate-css": "^1.3.7",
3337
"typescript": "^5.9.2",
34-
"vite": "^5.4.19"
38+
"vite": "^7.1.3"
3539
},
3640
"type": "module",
3741
"dependencies": {
38-
"bits-ui": "^0.21.16",
42+
"bits-ui": "^2.9.4",
3943
"clsx": "^2.1.1",
4044
"dompurify": "^3.2.6",
41-
"express": "^4.21.2",
45+
"express": "^5.1.0",
4246
"katex": "^0.16.22",
43-
"lucide-svelte": "^0.408.0",
44-
"mathjs": "^13.2.3",
47+
"mathjs": "^14.6.0",
4548
"minidenticons": "^4.2.1",
4649
"quill": "^2.0.3",
4750
"socket.io": "^4.8.1",
4851
"socket.io-client": "^4.8.1",
49-
"svelte-sonner": "^0.3.28",
50-
"tailwind-merge": "^2.6.0",
51-
"tailwind-variants": "^0.2.1",
52-
"zod": "^3.25.76"
52+
"svelte-sonner": "^1.0.5",
53+
"tailwind-merge": "^3.3.1",
54+
"tailwind-variants": "^1.0.0",
55+
"zod": "^4.1.3"
5356
}
5457
}

src/app.css

Lines changed: 100 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,112 @@
11
@import "tailwindcss";
2+
@import "tw-animate-css";
3+
4+
@custom-variant dark (&:is(.dark *));
5+
:root {
6+
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
7+
--foreground: hsl(240 10% 3.9%);
8+
--muted: hsl(240 4.8% 95.9%);
9+
--muted-foreground: hsl(240 3.8% 46.1%);
10+
--popover: hsl(0 0% 100%);
11+
--popover-foreground: hsl(240 10% 3.9%);
12+
--card: hsl(0 0% 100%);
13+
--card-foreground: hsl(240 10% 3.9%);
14+
--border: hsl(240 5.9% 90%);
15+
--input: hsl(240 5.9% 90%);
16+
--primary: hsl(240 5.9% 10%);
17+
--primary-foreground: hsl(0 0% 98%);
18+
--secondary: hsl(240 4.8% 95.9%);
19+
--secondary-foreground: hsl(240 5.9% 10%);
20+
--accent: hsl(240 4.8% 95.9%);
21+
--accent-foreground: hsl(240 5.9% 10%);
22+
--destructive: hsl(0 72.2% 50.6%);
23+
--destructive-foreground: hsl(0 0% 98%);
24+
--ring: hsl(240 10% 3.9%);
25+
--sidebar: hsl(0 0% 98%);
26+
--sidebar-foreground: hsl(240 5.3% 26.1%);
27+
--sidebar-primary: hsl(240 5.9% 10%);
28+
--sidebar-primary-foreground: hsl(0 0% 98%);
29+
--sidebar-accent: hsl(240 4.8% 95.9%);
30+
--sidebar-accent-foreground: hsl(240 5.9% 10%);
31+
--sidebar-border: hsl(220 13% 91%);
32+
--sidebar-ring: hsl(217.2 91.2% 59.8%);
33+
34+
--radius: 0.5rem;
35+
}
236

3-
@config '../tailwind.config.js';
4-
5-
@layer base {
6-
:root {
7-
--background: 0 0% 100%;
8-
--foreground: 222.2 84% 4.9%;
9-
10-
--muted: 210 40% 96.1%;
11-
--muted-foreground: 215.4 16.3% 46.9%;
12-
13-
--popover: 0 0% 100%;
14-
--popover-foreground: 222.2 84% 4.9%;
15-
16-
--card: 0 0% 100%;
17-
--card-foreground: 222.2 84% 4.9%;
18-
19-
--border: 214.3 31.8% 91.4%;
20-
--input: 214.3 31.8% 91.4%;
21-
22-
--primary: 222.2 47.4% 11.2%;
23-
--primary-foreground: 210 40% 98%;
24-
25-
--secondary: 210 40% 96.1%;
26-
--secondary-foreground: 222.2 47.4% 11.2%;
27-
28-
--accent: 210 40% 96.1%;
29-
--accent-foreground: 222.2 47.4% 11.2%;
30-
31-
--destructive: 0 72.2% 50.6%;
32-
--destructive-foreground: 210 40% 98%;
33-
34-
--ring: 222.2 84% 4.9%;
35-
36-
--radius: 0.5rem;
37-
}
38-
39-
.dark {
40-
--background: 222.2 84% 4.9%;
41-
--foreground: 210 40% 98%;
42-
43-
--muted: 217.2 32.6% 17.5%;
44-
--muted-foreground: 215 20.2% 65.1%;
45-
46-
--popover: 222.2 84% 4.9%;
47-
--popover-foreground: 210 40% 98%;
48-
49-
--card: 222.2 84% 4.9%;
50-
--card-foreground: 210 40% 98%;
51-
52-
--border: 217.2 32.6% 17.5%;
53-
--input: 217.2 32.6% 17.5%;
54-
55-
--primary: 210 40% 98%;
56-
--primary-foreground: 222.2 47.4% 11.2%;
57-
58-
--secondary: 217.2 32.6% 17.5%;
59-
--secondary-foreground: 210 40% 98%;
60-
61-
--accent: 217.2 32.6% 17.5%;
62-
--accent-foreground: 210 40% 98%;
63-
64-
--destructive: 0 62.8% 30.6%;
65-
--destructive-foreground: 210 40% 98%;
37+
.dark {
38+
--background: hsl(240 10% 3.9%);
39+
--foreground: hsl(0 0% 98%);
40+
--muted: hsl(240 3.7% 15.9%);
41+
--muted-foreground: hsl(240 5% 64.9%);
42+
--popover: hsl(240 10% 3.9%);
43+
--popover-foreground: hsl(0 0% 98%);
44+
--card: hsl(240 10% 3.9%);
45+
--card-foreground: hsl(0 0% 98%);
46+
--border: hsl(240 3.7% 15.9%);
47+
--input: hsl(240 3.7% 15.9%);
48+
--primary: hsl(0 0% 98%);
49+
--primary-foreground: hsl(240 5.9% 10%);
50+
--secondary: hsl(240 3.7% 15.9%);
51+
--secondary-foreground: hsl(0 0% 98%);
52+
--accent: hsl(240 3.7% 15.9%);
53+
--accent-foreground: hsl(0 0% 98%);
54+
--destructive: hsl(0 62.8% 30.6%);
55+
--destructive-foreground: hsl(0 0% 98%);
56+
--ring: hsl(240 4.9% 83.9%);
57+
--sidebar: hsl(240 5.9% 10%);
58+
--sidebar-foreground: hsl(240 4.8% 95.9%);
59+
--sidebar-primary: hsl(224.3 76.3% 48%);
60+
--sidebar-primary-foreground: hsl(0 0% 100%);
61+
--sidebar-accent: hsl(240 3.7% 15.9%);
62+
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
63+
--sidebar-border: hsl(240 3.7% 15.9%);
64+
--sidebar-ring: hsl(217.2 91.2% 59.8%);
65+
}
6666

67-
--ring: hsl(212.7, 26.8%, 83.9);
68-
}
67+
@theme inline {
68+
/* Radius (for rounded-*) */
69+
--radius-sm: calc(var(--radius) - 4px);
70+
--radius-md: calc(var(--radius) - 2px);
71+
--radius-lg: var(--radius);
72+
--radius-xl: calc(var(--radius) + 4px);
73+
74+
/* Colors */
75+
--color-background: var(--background);
76+
--color-foreground: var(--foreground);
77+
--color-muted: var(--muted);
78+
--color-muted-foreground: var(--muted-foreground);
79+
--color-popover: var(--popover);
80+
--color-popover-foreground: var(--popover-foreground);
81+
--color-card: var(--card);
82+
--color-card-foreground: var(--card-foreground);
83+
--color-border: var(--border);
84+
--color-input: var(--input);
85+
--color-primary: var(--primary);
86+
--color-primary-foreground: var(--primary-foreground);
87+
--color-secondary: var(--secondary);
88+
--color-secondary-foreground: var(--secondary-foreground);
89+
--color-accent: var(--accent);
90+
--color-accent-foreground: var(--accent-foreground);
91+
--color-destructive: var(--destructive);
92+
--color-destructive-foreground: var(--destructive-foreground);
93+
--color-ring: var(--ring);
94+
--color-radius: var(--radius);
95+
--color-sidebar: var(--sidebar);
96+
--color-sidebar-foreground: var(--sidebar-foreground);
97+
--color-sidebar-primary: var(--sidebar-primary);
98+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
99+
--color-sidebar-accent: var(--sidebar-accent);
100+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
101+
--color-sidebar-border: var(--sidebar-border);
102+
--color-sidebar-ring: var(--sidebar-ring);
69103
}
70104

71105
@layer base {
72106
* {
73107
@apply border-border;
74108
}
109+
75110
body {
76111
@apply bg-background text-foreground;
77112
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<script lang="ts">
22
import { minidenticon } from "minidenticons";
33
4-
export let className: string;
5-
export let seed: string;
4+
interface Props {
5+
className: string;
6+
seed: string;
7+
}
68
7-
$: svg = minidenticon(seed);
9+
let { className, seed }: Props = $props();
10+
11+
let svg = $derived(minidenticon(seed));
812
</script>
913

1014
<div class={className}>{@html svg}</div>

src/lib/components/Quill.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<script lang="ts">
22
import { Textarea } from "$lib/components/ui/textarea";
3-
export let html = "";
3+
interface Props {
4+
html?: string;
5+
}
6+
7+
let { html = $bindable("") }: Props = $props();
48
</script>
59

610
<Textarea bind:value={html} contenteditable="true" placeholder="Enter text here. This supports HTML input." />

src/lib/components/ui/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# ui
2+
3+
This is from [shadcn-svelte](https://shadcn-svelte.com)
Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,80 @@
1-
<script lang="ts">
2-
import { Button as ButtonPrimitive } from "bits-ui";
3-
import { type Events, type Props, buttonVariants } from "./index.js";
4-
import { cn } from "$lib/utils.js";
1+
<script lang="ts" module>
2+
import { cn, type WithElementRef } from "$lib/utils.js";
3+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
4+
import { type VariantProps, tv } from "tailwind-variants";
5+
6+
export const buttonVariants = tv({
7+
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
8+
variants: {
9+
variant: {
10+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
11+
destructive:
12+
"bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white",
13+
outline:
14+
"bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border",
15+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
16+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
17+
link: "text-primary underline-offset-4 hover:underline"
18+
},
19+
size: {
20+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
21+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
22+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
23+
icon: "size-9"
24+
}
25+
},
26+
defaultVariants: {
27+
variant: "default",
28+
size: "default"
29+
}
30+
});
531
6-
type $$Props = Props;
7-
type $$Events = Events;
32+
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
33+
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
834
9-
let className: $$Props["class"] = undefined;
10-
export let variant: $$Props["variant"] = "default";
11-
export let size: $$Props["size"] = "default";
12-
export let builders: $$Props["builders"] = [];
13-
export { className as class };
35+
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
36+
WithElementRef<HTMLAnchorAttributes> & {
37+
variant?: ButtonVariant;
38+
size?: ButtonSize;
39+
};
40+
</script>
41+
42+
<script lang="ts">
43+
let {
44+
class: className,
45+
variant = "default",
46+
size = "default",
47+
ref = $bindable(null),
48+
href = undefined,
49+
type = "button",
50+
disabled,
51+
children,
52+
...restProps
53+
}: ButtonProps = $props();
1454
</script>
1555

16-
<ButtonPrimitive.Root
17-
{builders}
18-
class={cn(buttonVariants({ variant, size, className }))}
19-
type="button"
20-
{...$$restProps}
21-
on:click
22-
on:keydown
23-
>
24-
<slot />
25-
</ButtonPrimitive.Root>
56+
{#if href}
57+
<a
58+
bind:this={ref}
59+
data-slot="button"
60+
class={cn(buttonVariants({ variant, size }), className)}
61+
href={disabled ? undefined : href}
62+
aria-disabled={disabled}
63+
role={disabled ? "link" : undefined}
64+
tabindex={disabled ? -1 : undefined}
65+
{...restProps}
66+
>
67+
{@render children?.()}
68+
</a>
69+
{:else}
70+
<button
71+
bind:this={ref}
72+
data-slot="button"
73+
class={cn(buttonVariants({ variant, size }), className)}
74+
{type}
75+
{disabled}
76+
{...restProps}
77+
>
78+
{@render children?.()}
79+
</button>
80+
{/if}

0 commit comments

Comments
 (0)