chore: update dependencies and refactor UI components

This commit is contained in:
ZhymabekRoman 2025-01-26 20:21:10 +05:00
parent 6b36cd8f2a
commit 8671046383
9 changed files with 196 additions and 172 deletions

Binary file not shown.

View file

@ -12,47 +12,47 @@
"format": "prettier --write ."
},
"devDependencies": {
"@iconify-json/heroicons": "^1.2.1",
"@iconify-json/heroicons-outline": "^1.2.0",
"@iconify-json/mage": "^1.2.1",
"@iconify-json/mdi": "^1.2.1",
"@iconify-json/simple-icons": "^1.2.12",
"@iconify-json/teenyicons": "^1.2.1",
"@shikijs/rehype": "^1.24.2",
"@iconify-json/heroicons": "^1.2.2",
"@iconify-json/heroicons-outline": "^1.2.1",
"@iconify-json/mage": "^1.2.2",
"@iconify-json/mdi": "^1.2.3",
"@iconify-json/simple-icons": "^1.2.21",
"@iconify-json/teenyicons": "^1.2.2",
"@shikijs/rehype": "^1.29.1",
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/kit": "^2.7.4",
"@sveltejs/kit": "^2.16.1",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"autoprefixer": "^10.4.20",
"clsx": "^2.1.1",
"cmdk-sv": "^0.0.18",
"lucide-svelte": "^0.453.0",
"mode-watcher": "^0.4.1",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
"postcss": "^8.5.1",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"svelte": "^4.2.19",
"svelte-check": "^4.0.5",
"svelte-check": "^4.1.4",
"svelte-sonner": "^0.3.28",
"tailwind-merge": "^2.5.4",
"tailwind-merge": "^2.6.0",
"tailwind-variants": "^0.2.1",
"tailwindcss": "^3.4.14",
"typescript": "^5.6.3",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.3",
"vaul-svelte": "^0.3.2",
"vite": "^5.4.10"
"vite": "^5.4.14"
},
"type": "module",
"dependencies": {
"@iconify/svelte": "^4.0.2",
"@iconify/tailwind": "^1.1.3",
"@iconify/svelte": "^4.2.0",
"@iconify/tailwind": "^1.2.0",
"bits-ui": "^0.21.16",
"copy-to-clipboard": "^3.3.3",
"es-toolkit": "^1.27.0",
"hast-util-to-html": "^9.0.3",
"es-toolkit": "^1.31.0",
"hast-util-to-html": "^9.0.4",
"mdsvex": "^0.12.3",
"medium-zoom": "^1.1.0",
"ofetch": "^1.4.1",
"rehype-external-links": "^3.0.0",
"shiki": "^1.23.1",
"shiki": "^1.29.1",
"shiki-transformer-copy-button": "^0.0.3",
"svelte-bricks": "^0.2.1",
"svelte-lazy": "1.2.9",

View file

@ -6,6 +6,7 @@
import Skeleton from '$lib/components/ui/skeleton/skeleton.svelte';
import Footer from '$lib/elements/Footer.svelte';
import './ArticlePage.css';
import Button from '$lib/components/ui/button/button.svelte';
export let data;
@ -37,76 +38,66 @@
<Header />
<div class="flex flex-col min-h-screen">
<main class="flex-1 w-full h-full max-w-5xl px-4 py-8">
<nav class="flex items-center justify-between gap-2 mb-4 text-center">
<a
href="/"
class="flex items-center justify-center transition bg-white rounded-full shadow-md text-primary hover:text-primary/90 group size-8 shadow-zinc-800/5 ring-1 ring-zinc-900/5 dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20"
>
<span class="icon-[heroicons--arrow-left-20-solid] size-6" />
</a>
{#if article?.url}
<a href={article.url} class="font-bold text-primary hover:text-primary/90">
Original article
</a>
{/if}
</nav>
{#if error}
<div class="p-6 text-center bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<h1
class="mb-4 text-2xl font-bold {error.status === 404 ? 'text-amber-600' : 'text-red-600'}"
>
{error.status === 404 ? 'Article Not Found' : 'Error Loading Article'}
</h1>
<p class="text-gray-600 dark:text-gray-400">
{getErrorMessage(error)}
</p>
{#if error.details && process.env.NODE_ENV === 'development'}
<pre class="p-4 mt-4 overflow-auto text-sm bg-gray-100 dark:bg-zinc-800">
<main class="flex-1 w-full h-full max-w-6xl px-4 py-8 mx-auto">
<div class="lg:flex lg:space-x-6 lg:justify-center">
{#if error}
<div class="p-6 text-center bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<h1
class="mb-4 text-2xl font-bold {error.status === 404
? 'text-amber-600'
: 'text-red-600'}"
>
{error.status === 404 ? 'Article Not Found' : 'Error Loading Article'}
</h1>
<p class="text-gray-600 dark:text-gray-400">
{getErrorMessage(error)}
</p>
{#if error.details && process.env.NODE_ENV === 'development'}
<pre class="p-4 mt-4 overflow-auto text-sm bg-gray-100 dark:bg-zinc-800">
{error.details}
</pre>
{/if}
<div class="mt-6 space-x-4">
<a
href="/"
class="inline-block px-4 py-2 text-white rounded-md bg-primary hover:bg-primary/90"
>
Return Home
</a>
<button
class="inline-block px-4 py-2 border rounded-md border-primary text-primary hover:bg-primary/10"
on:click={() => window.location.reload()}
>
Try Again
</button>
{/if}
<div class="mt-6 space-x-4">
<a
href="/"
class="inline-block px-4 py-2 text-white rounded-md bg-primary hover:bg-primary/90"
>
Return Home
</a>
<button
class="inline-block px-4 py-2 border rounded-md border-primary text-primary hover:bg-primary/10"
on:click={() => window.location.reload()}
>
Try Again
</button>
</div>
</div>
</div>
{:else if showSkeleton}
<div class="lg:flex lg:space-x-8">
<article class="flex-grow overflow-hidden bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<Skeleton class="w-full h-96" />
<div class="p-6 bg-gray-50 dark:bg-zinc-800">
<Skeleton class="w-32 h-4 mb-2" />
<Skeleton class="w-full h-10 mb-4" />
<div class="flex items-center">
<Skeleton class="w-12 h-12 mr-4 rounded-full" />
<div class="space-y-2">
<Skeleton class="w-40 h-4" />
<Skeleton class="w-32 h-4" />
{:else if showSkeleton}
<div class="flex-grow max-w-[calc(100%-10rem)]">
<article class="w-full overflow-hidden bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<Skeleton class="w-full h-96" />
<div class="p-6 bg-gray-50 dark:bg-zinc-800">
<Skeleton class="w-32 h-4 mb-2" />
<Skeleton class="w-full h-10 mb-4" />
<div class="flex items-center">
<Skeleton class="w-12 h-12 mr-4 rounded-full" />
<div class="space-y-2">
<Skeleton class="w-40 h-4" />
<Skeleton class="w-32 h-4" />
</div>
</div>
</div>
</div>
<div class="p-6">
<div class="space-y-4">
<Skeleton class="w-full h-4" />
<Skeleton class="w-full h-4" />
<Skeleton class="w-3/4 h-4" />
<div class="p-6">
<div class="space-y-4">
<Skeleton class="w-full h-4" />
<Skeleton class="w-full h-4" />
<Skeleton class="w-3/4 h-4" />
</div>
</div>
</div>
</article>
</article>
</div>
<aside class="order-first mt-7 lg:mt-0 lg:min-w-80 lg:order-none">
<aside class="mt-7 lg:mt-0 max-w-72 lg:flex-shrink-0">
<div class="w-full p-4 bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<Skeleton class="w-32 h-6 mb-4" />
<div class="space-y-2">
@ -116,69 +107,84 @@
</div>
</div>
</aside>
</div>
{:else}
<div class="lg:flex lg:space-x-8">
<article class="flex-grow overflow-hidden bg-white rounded-lg shadow-lg dark:bg-zinc-900">
{#if article.postImage}
<ImageZoom
src={article.postImage}
alt="Post cover image"
class="object-cover w-full h-auto min-h-96"
/>
{/if}
<header class="p-6 bg-gray-50 dark:bg-zinc-800">
<p class="mb-2 text-gray-600 dark:text-gray-400">{formatDate(article.date)}</p>
<h1 class="mb-4 text-4xl font-bold text-gray-900 dark:text-white">{article.title}</h1>
<div class="flex items-center">
<img src={article.author.avatar} alt="" class="w-12 h-12 mr-4 rounded-full" />
<div>
<p class="font-semibold text-gray-900 dark:text-white">{article.author.name}</p>
<p class="text-gray-600 dark:text-gray-400">{article.author.role}</p>
{:else}
<div class="w-full">
<article class="overflow-hidden bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<nav class="flex items-center justify-between gap-2 p-4 text-center">
<button
class="flex items-center justify-center transition bg-white rounded-full shadow-md text-primary hover:text-primary/90 group size-8 shadow-zinc-800/5 ring-1 ring-zinc-900/5 dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0 dark:ring-white/10 dark:hover:border-zinc-700 dark:hover:ring-white/20"
on:click={() => window.history.back()}
>
<span class="icon-[heroicons--arrow-left-20-solid] size-6" />
</button>
{#if article?.url}
<a href={article.url} class="font-bold text-primary hover:text-primary/90">
Original article
</a>
{/if}
</nav>
{#if article.postImage}
<ImageZoom
src={article.postImage}
alt="Post cover image"
class="object-cover w-full h-auto min-h-96"
/>
{/if}
<header class="p-6 bg-gray-50 dark:bg-zinc-800">
<p class="mb-2 text-gray-600 dark:text-gray-400">{formatDate(article.date)}</p>
<h1 class="mb-4 text-4xl font-bold text-gray-900 dark:text-white">{article.title}</h1>
<div class="flex items-center">
<img src={article.author.avatar} alt="" class="w-12 h-12 mr-4 rounded-full" />
<div>
<p class="font-semibold text-gray-900 dark:text-white">{article.author.name}</p>
<p class="text-gray-600 dark:text-gray-400">{article.author.role}</p>
</div>
</div>
</header>
<div class="p-6 {article.postImage ? '' : 'pt-0'} dark:text-gray-300">
<div class="prose max-w-none">
{#if content}
{@html content}
{:else}
<p>Error loading content</p>
{/if}
</div>
</div>
</header>
</article>
</div>
<div class="p-6 {article.postImage ? '' : 'pt-0'} dark:text-gray-300">
<div class="prose max-w-none">
{#if content}
{@html content}
<aside class="w-full mt-7 lg:mt-0 max-w-64" aria-labelledby="toc-heading">
<div class="sticky top-12">
<Button class="w-full mb-4">
<span class="icon-[heroicons--arrow-down-20-solid] size-4 mr-1" />
Download as PDF/Markdown
</Button>
<nav class="w-full p-4 bg-white rounded-lg shadow-lg dark:bg-zinc-900">
<h2 id="toc-heading" class="mb-4 text-xl font-semibold text-primary">Contents</h2>
{#if article.tableOfContents && article.tableOfContents.length > 0}
<ul class="space-y-1">
{#each article.tableOfContents as item}
<li>
<Button
variant="ghost"
class="justify-start w-full text-zinc-600 hover:text-zinc-600 dark:text-gray-100 dark:hover:text-white"
>
<a class="" href={`#${item.id}`}>{item.title}</a>
</Button>
</li>
{/each}
</ul>
{:else}
<p>Error loading content</p>
<p class="italic text-zinc-600 dark:text-gray-400">
No table of contents available
</p>
{/if}
</div>
</nav>
</div>
</article>
<aside class="order-first mt-7 lg:mt-0 lg:min-w-80 lg:order-none">
<nav
aria-labelledby="toc-heading"
class="w-full p-4 bg-white rounded-lg shadow-lg dark:bg-zinc-900 lg:sticky lg:top-36"
>
<h2 id="toc-heading" class="mb-4 text-xl font-semibold text-gray-900 dark:text-white">
Contents
</h2>
{#if article.tableOfContents && article.tableOfContents.length > 0}
<ul class="space-y-2">
{#each article.tableOfContents as item}
<li>
<a
href={`#${item.id}`}
class="transition-colors text-zinc-800 hover:text-zinc-900 dark:text-gray-300 dark:hover:text-white"
>
{item.title}
</a>
</li>
{/each}
</ul>
{:else}
<p class="dark:text-gray-300">No table of contents available</p>
{/if}
</nav>
</aside>
</div>
{/if}
{/if}
</div>
</main>
<Footer />
</div>

View file

@ -64,6 +64,7 @@
<div class="flex items-center">
<img
src="https://miro.medium.com/v2/resize:fill:48:48/{collection.avatarId}"
referrerpolicy="no-referrer"
alt={collection.name.charAt(0)}
loading="eager"
class="w-4 h-4 mr-1 rounded-full no-lightense"

View file

@ -62,7 +62,7 @@
>Freedium βeta</a
>
<div class="flex-grow max-w-md mx-2">
<!-- <div class="flex-grow max-w-md mx-2">
<Button
class="items-center hidden w-full h-8 gap-2 pl-2 pr-3 text-sm transition bg-white rounded-full ui-not-focus-visible:outline-none text-zinc-500 ring-1 ring-zinc-900/10 hover:ring-zinc-900/20 hover:bg-zinc-50 focus-visible:ring-2 focus-visible:ring-primary lg:flex dark:bg-white/5 dark:text-zinc-400 dark:ring-inset dark:ring-white/10 dark:hover:ring-white/20 dark:hover:bg-white/10"
on:click={toggleSearch}
@ -71,11 +71,11 @@
<span class="icon-[heroicons-outline--magnifying-glass]" />
<span>Search articles...</span>
</Button>
</div>
</div> -->
<div class="items-center hidden space-x-2 md:flex">
<Button class="lg:hidden" variant="ghost" size="icon" on:click={toggleSearch}>
<span class="icon-[heroicons-outline--magnifying-glass] size-5" />
<Button variant="ghost" size="icon" on:click={toggleSearch}>
<span class="icon-[heroicons--magnifying-glass-16-solid] size-5" />
</Button>
<ExtensionsButton />
<div class="w-px h-6 bg-zinc-300 dark:bg-zinc-700"></div>
@ -104,6 +104,7 @@
<span class="icon-[heroicons-outline--magnifying-glass] size-5" />
</Button>
<ThemeToggle />
<ReportProblem />
<Button
variant="ghost"
size="icon"
@ -140,17 +141,16 @@
icon="icon-[simple-icons--discord]"
/>
<!-- <ExtensionsButton /> -->
<ReportProblem />
</div>
</div>
{/if}
<div
<!-- <div
class="transition-transform duration-300"
style="transform: translateY({isHeaderVisible ? '0' : '-100%'})"
>
<Advertise />
</div>
</div> -->
</nav>
<SearchDialog bind:open={isSearchOpen} />

View file

@ -6,7 +6,7 @@
<div class="container w-full py-20 pt-20 mx-auto break-words">
<div class="flex flex-col items-center justify-center h-60">
<h1 class="mt-8 text-4xl font-bold text-center text-primary md:max-w-3xl">
Freedium: Your paywall breakthrough for Medium!
Freedium: Breaking Down the Walls of Knowledge
</h1>
<UrlBox />
</div>

View file

@ -24,8 +24,8 @@
{#if $isDesktop}
<Dialog.Root bind:open>
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}>
<div class="flex items-center space-x-2 text-white">
<Dialog.Trigger class={buttonVariants({ variant: 'ghost' })}>
<div class="flex items-center space-x-2 text-primary">
<span class="icon-[heroicons--exclamation-triangle-solid] size-5" />
<span class="hidden text-sm font-medium lg:block">Report a problem</span>
</div>

View file

@ -1,35 +1,52 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button/index.js';
import { createSwitch } from '@melt-ui/svelte';
import { browser } from '$app/environment';
import { Moon, Sun } from 'lucide-svelte';
let darkMode = true;
const initialChecked = browser
? localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
: false;
function handleSwitchDarkMode() {
darkMode = !darkMode;
localStorage.setItem('theme', darkMode ? 'dark' : 'light');
darkMode
const {
elements: { root, input },
states: { checked }
} = createSwitch({
defaultChecked: initialChecked
});
$: if (browser) {
const isDark = $checked;
localStorage.setItem('theme', isDark ? 'dark' : 'light');
isDark
? document.documentElement.classList.add('dark')
: document.documentElement.classList.remove('dark');
}
if (browser) {
if (
const isDark =
localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
darkMode = true;
} else {
document.documentElement.classList.remove('dark');
darkMode = false;
}
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
isDark
? document.documentElement.classList.add('dark')
: document.documentElement.classList.remove('dark');
}
</script>
<Button
class="px-3 text-gray-600 py-7 dark:text-white hover:text-primary dark:hover:text-primary"
on:click={handleSwitchDarkMode}
variant="ghost"
<button
{...$root}
use:root
class="relative inline-flex h-[36px] w-[60px] cursor-pointer items-center rounded-full bg-gray-200 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 data-[state=checked]:bg-primary dark:bg-gray-700"
>
<span class={`${darkMode ? 'icon-[heroicons--moon-solid]' : 'icon-[heroicons--sun-solid]'} size-5`} />
</Button>
<span
class="pointer-events-none flex h-[30px] w-[30px] items-center justify-center rounded-full bg-white transition-transform duration-200"
style:transform={$checked ? 'translateX(24px)' : 'translateX(3px)'}
>
{#if !$checked}
<Sun class="text-gray-600 size-4" />
{:else}
<Moon class="text-gray-600 size-4" />
{/if}
</span>
<input {...$input} use:input type="checkbox" class="sr-only" />
</button>

View file

@ -30,7 +30,7 @@
<!-- svelte-ignore a11y-autofocus -->
<input
type="text"
placeholder="Enter Medium post link"
placeholder="Enterx post link"
class="w-full bg-transparent border-zinc-300 focus:outline-none text-primary"
autofocus
bind:value={url}