feat: blog section
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,133 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getPostBySlug, getFeaturedImageUrl } from "@/lib/wp-api";
|
||||
import { formatDateShamsi } from "@/lib/format-date";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { parseContentHeadings, getReadingTimeMinutes } from "@/lib/blog-utils";
|
||||
import { BlogToc } from "@/components/blog-toc";
|
||||
import { BlogPostMeta } from "@/components/blog-post-meta";
|
||||
import { BlogCollapseSection } from "@/components/blog-collapse-section";
|
||||
import { ScrollToTop } from "@/components/scroll-to-top";
|
||||
|
||||
interface BlogPostPageProps {
|
||||
params: Promise<{ slug: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: BlogPostPageProps): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const post = await getPostBySlug(slug);
|
||||
if (!post) return { title: "پست یافت نشد" };
|
||||
const title = post.title?.rendered ?? "پست";
|
||||
const excerpt = post.excerpt?.rendered?.replace(/<[^>]*>/g, "").trim().slice(0, 160) ?? "";
|
||||
return {
|
||||
title,
|
||||
description: excerpt,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({ params }: BlogPostPageProps) {
|
||||
const { slug } = await params;
|
||||
let post;
|
||||
try {
|
||||
post = await getPostBySlug(slug);
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
if (!post) notFound();
|
||||
|
||||
const date = post.date ? formatDateShamsi(post.date) : null;
|
||||
const modifiedDate = post.modified
|
||||
? formatDateShamsi(post.modified)
|
||||
: null;
|
||||
const authorName = post._embedded?.author?.[0]?.name ?? null;
|
||||
const thumbnailUrl = getFeaturedImageUrl(post, "full");
|
||||
const media = post._embedded?.["wp:featuredmedia"]?.[0];
|
||||
const altText = media?.alt_text || post.title?.rendered?.replace(/<[^>]*>/g, "") || "";
|
||||
|
||||
const { toc, segments } = parseContentHeadings(post.content.rendered);
|
||||
const readingTimeMinutes = getReadingTimeMinutes(post.content.rendered);
|
||||
const postTitle = post.title?.rendered?.replace(/<[^>]*>/g, "") ?? "";
|
||||
const postPath = `/blog/${slug}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* تصویر شاخص تمامعرض بالای صفحه */}
|
||||
{thumbnailUrl && (
|
||||
<div className="pt-28 md:pt-36">
|
||||
<div className="relative w-full aspect-[21/5] min-h-[10rem] md:min-h-[10rem] bg-muted">
|
||||
<Image
|
||||
src={thumbnailUrl}
|
||||
alt={altText}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="100vw"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<section className="bg-background pt-8 pb-20 md:pt-12 md:pb-28">
|
||||
<div className="mx-auto max-w-6xl px-6">
|
||||
{/* Single grid so sticky TOC stays for whole page scroll */}
|
||||
<div className="lg:grid lg:grid-cols-[16rem_1fr] lg:gap-10">
|
||||
<BlogToc items={toc} className="mb-6 lg:mb-0" />
|
||||
|
||||
<div className="min-w-0 max-w-3xl space-y-0">
|
||||
<div className="pb-8 md:pb-12">
|
||||
<nav className="mb-6" aria-label="مسیر">
|
||||
<Link
|
||||
href="/blog"
|
||||
className="text-muted-foreground hover:text-foreground text-sm inline-flex items-center gap-1 transition-colors"
|
||||
>
|
||||
<ChevronRight className="size-4" aria-hidden />
|
||||
بلاگ
|
||||
</Link>
|
||||
</nav>
|
||||
<h1
|
||||
className="text-2xl font-bold md:text-3xl leading-tight tracking-normal"
|
||||
dangerouslySetInnerHTML={{ __html: post.title.rendered }}
|
||||
/>
|
||||
<BlogPostMeta
|
||||
url={postPath}
|
||||
title={postTitle}
|
||||
readingTimeMinutes={readingTimeMinutes}
|
||||
date={date}
|
||||
dateTime={post.date}
|
||||
modifiedDate={modifiedDate}
|
||||
authorName={authorName}
|
||||
className="mt-4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<article className="blog-content text-justify space-y-6 text-foreground [&_h2]:text-2xl [&_h2]:font-bold [&_h2]:mt-8 [&_h2]:mb-4 [&_h3]:text-xl [&_h3]:font-bold [&_h3]:mt-6 [&_h3]:mb-3 [&_p]:leading-relaxed [&_p]:text-muted-foreground [&_a]:text-primary [&_a]:no-underline hover:[&_a]:underline [&_ul]:list-disc [&_ul]:pr-6 [&_ol]:list-decimal [&_ol]:pr-6 [&_li]:my-1 [&_h2]:scroll-mt-24 [&_h3]:scroll-mt-24 [&_h4]:scroll-mt-24">
|
||||
{segments.map((seg, i) =>
|
||||
seg.type === "html" ? (
|
||||
<div
|
||||
key={i}
|
||||
dangerouslySetInnerHTML={{ __html: seg.content }}
|
||||
/>
|
||||
) : seg.blockType === "collapse" ? (
|
||||
<BlogCollapseSection
|
||||
key={i}
|
||||
section={seg.payload}
|
||||
idPrefix={`blog-${slug}-${i}`}
|
||||
/>
|
||||
) : (
|
||||
/* سایر بلاکها (blockquote, code, ...) بعداً با کامپوننت خودشان رندر میشوند */
|
||||
null
|
||||
)
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ScrollToTop />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { getPosts, getCategories } from "@/lib/wp-api";
|
||||
import { BlogPostCard } from "@/components/blog-post-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { ChevronRight, ChevronLeft } from "lucide-react";
|
||||
|
||||
const PER_PAGE = 9;
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "بلاگ",
|
||||
description: "مقالات و مطالب فیپ در زمینه یکپارچهسازی، اتوماسیون و بهرهوری سازمانی.",
|
||||
};
|
||||
|
||||
interface BlogPageProps {
|
||||
searchParams: Promise<{ search?: string; category?: string; page?: string }>;
|
||||
}
|
||||
|
||||
export default async function BlogPage({ searchParams }: BlogPageProps) {
|
||||
const params = await searchParams;
|
||||
const search = params.search ?? "";
|
||||
const categoryId = params.category ? parseInt(params.category, 10) : undefined;
|
||||
const page = Math.max(1, parseInt(params.page ?? "1", 10) || 1);
|
||||
|
||||
let postsResult;
|
||||
let categories: Awaited<ReturnType<typeof getCategories>> = [];
|
||||
|
||||
try {
|
||||
[postsResult, categories] = await Promise.all([
|
||||
getPosts({
|
||||
page,
|
||||
perPage: PER_PAGE,
|
||||
search: search || undefined,
|
||||
categoryId: Number.isFinite(categoryId) ? categoryId : undefined,
|
||||
}),
|
||||
getCategories(),
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return (
|
||||
<section className="bg-background pt-32 pb-20 md:pt-40 md:pb-28">
|
||||
<div className="mx-auto max-w-4xl px-6 text-center">
|
||||
<h1 className="text-4xl font-bold md:text-5xl">بلاگ</h1>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
در حال حاضر امکان بارگذاری مطالب وجود ندارد. لطفاً بعداً تلاش کنید.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const { posts, total, totalPages } = postsResult;
|
||||
const hasFilters = search || categoryId;
|
||||
|
||||
function buildQuery(updates: { search?: string; category?: string; page?: number }) {
|
||||
const q = new URLSearchParams();
|
||||
if (updates.search !== undefined && updates.search !== "") q.set("search", updates.search);
|
||||
if (updates.category !== undefined && updates.category !== "") q.set("category", updates.category);
|
||||
if (updates.page !== undefined && updates.page > 1) q.set("page", String(updates.page));
|
||||
const s = q.toString();
|
||||
return s ? `?${s}` : "";
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Hero + search & filters in one section */}
|
||||
<section className="bg-background pt-32 pb-10 md:pt-40 md:pb-14">
|
||||
<div className="mx-auto max-w-6xl px-6">
|
||||
<div className="text-center space-y-8">
|
||||
<div className="space-y-3">
|
||||
<h1 className="text-4xl font-bold md:text-5xl lg:text-6xl leading-tight tracking-normal">
|
||||
بلاگ
|
||||
</h1>
|
||||
<p className="text-lg md:text-xl leading-relaxed text-muted-foreground max-w-2xl mx-auto">
|
||||
مقالات و مطالب فیپ در زمینه یکپارچهسازی، اتوماسیون و بهرهوری سازمانی.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-5 max-w-2xl mx-auto">
|
||||
<form
|
||||
method="get"
|
||||
action="/blog"
|
||||
className="flex flex-col sm:flex-row gap-2 sm:gap-2 w-full"
|
||||
>
|
||||
<input type="hidden" name="category" value={categoryId ?? ""} />
|
||||
<Input
|
||||
type="search"
|
||||
name="search"
|
||||
placeholder="جستجو در مطالب..."
|
||||
defaultValue={search}
|
||||
className="flex-1 w-full sm:min-w-[14rem] text-base"
|
||||
aria-label="جستجو در مطالب"
|
||||
/>
|
||||
<div className="flex gap-2 flex-shrink-0">
|
||||
<Button type="submit" size="default" className="flex-1 sm:flex-none">
|
||||
جستجو
|
||||
</Button>
|
||||
{hasFilters && (
|
||||
<Link href="/blog" className="flex-1 sm:flex-none">
|
||||
<Button type="button" variant="outline" size="default" className="w-full">
|
||||
پاک کردن فیلتر
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{categories.length > 0 && (
|
||||
<div className="flex flex-wrap items-center justify-center gap-2">
|
||||
<Link
|
||||
href="/blog"
|
||||
className={`text-sm rounded-full px-4 py-2 transition-colors ${!categoryId
|
||||
? "bg-primary text-primary-foreground shadow-sm"
|
||||
: "bg-muted/80 hover:bg-muted text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
همه
|
||||
</Link>
|
||||
{categories.map((cat) => (
|
||||
<Link
|
||||
key={cat.id}
|
||||
href={`/blog${buildQuery({ search, category: String(cat.id), page: 1 })}`}
|
||||
className={`text-sm rounded-full px-4 py-2 transition-colors ${categoryId === cat.id
|
||||
? "bg-primary text-primary-foreground shadow-sm"
|
||||
: "bg-muted/80 hover:bg-muted text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{cat.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="bg-background pb-20 md:pb-28">
|
||||
<div className="mx-auto max-w-6xl px-6 pt-2">
|
||||
{posts.length === 0 ? (
|
||||
<div className="text-center py-16 text-muted-foreground">
|
||||
<p>مطلبی یافت نشد.</p>
|
||||
<Link href="/blog" className="mt-4 inline-block">
|
||||
<Button variant="outline" size="sm">
|
||||
نمایش همه مطالب
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<ul className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{posts.map((post) => (
|
||||
<li key={post.id}>
|
||||
<BlogPostCard post={post} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<nav
|
||||
className="mt-12 flex flex-wrap items-center justify-center gap-2"
|
||||
aria-label="صفحهبندی"
|
||||
>
|
||||
{page > 1 && (
|
||||
<Link
|
||||
href={`/blog${buildQuery({
|
||||
search,
|
||||
category: categoryId ? String(categoryId) : undefined,
|
||||
page: page - 1,
|
||||
})}`}
|
||||
>
|
||||
<Button variant="outline" size="icon" aria-label="صفحه قبل">
|
||||
<ChevronRight className="size-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
<span className="text-muted-foreground text-sm px-2">
|
||||
صفحه {page} از {totalPages} ({total} مطلب)
|
||||
</span>
|
||||
{page < totalPages && (
|
||||
<Link
|
||||
href={`/blog${buildQuery({
|
||||
search,
|
||||
category: categoryId ? String(categoryId) : undefined,
|
||||
page: page + 1,
|
||||
})}`}
|
||||
>
|
||||
<Button variant="outline" size="icon" aria-label="صفحه بعد">
|
||||
<ChevronLeft className="size-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -190,6 +190,9 @@
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import type { CollapseBlockPayload } from "@/lib/blog-blocks";
|
||||
|
||||
interface BlogCollapseSectionProps {
|
||||
section: CollapseBlockPayload;
|
||||
/** Optional prefix for accordion item ids (e.g. for multiple sections on one page). */
|
||||
idPrefix?: string;
|
||||
}
|
||||
|
||||
export function BlogCollapseSection({
|
||||
section,
|
||||
idPrefix = "collapse",
|
||||
}: BlogCollapseSectionProps) {
|
||||
if (!section.items.length) return null;
|
||||
|
||||
return (
|
||||
<div className="my-8 space-y-4 border rounded-md p-6">
|
||||
{section.title ? (
|
||||
<h2 className="text-2xl font-bold !mt-0 mb-4 scroll-mt-24">
|
||||
{section.title}
|
||||
</h2>
|
||||
) : null}
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
{section.items.map((item, i) => (
|
||||
<AccordionItem
|
||||
key={i}
|
||||
value={`${idPrefix}-${i}`}
|
||||
className="border-b last:border-b-0 !m-0"
|
||||
>
|
||||
<AccordionTrigger className="text-right hover:no-underline py-4 [&>svg]:shrink-0">
|
||||
{item.question}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="text-muted-foreground leading-relaxed text-justify">
|
||||
{item.answer}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { ImageIcon, User } from "lucide-react";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { getFeaturedImageUrl, type WpPost } from "@/lib/wp-api";
|
||||
import { formatDateShamsi } from "@/lib/format-date";
|
||||
|
||||
function stripHtml(html: string): string {
|
||||
return html.replace(/<[^>]*>/g, "").trim();
|
||||
}
|
||||
|
||||
export function BlogPostCard({ post }: { post: WpPost }) {
|
||||
const excerpt = stripHtml(post.excerpt.rendered) || stripHtml(post.content.rendered).slice(0, 160);
|
||||
const modifiedDate = post.modified ? formatDateShamsi(post.modified) : null;
|
||||
const authorName = post._embedded?.author?.[0]?.name ?? null;
|
||||
const thumbnailUrl = getFeaturedImageUrl(post, "medium");
|
||||
|
||||
return (
|
||||
<Link href={`/blog/${encodeURIComponent(post.slug)}`} className="block h-full">
|
||||
<Card className="h-full overflow-hidden transition-all duration-300 hover:-translate-y-1 hover:shadow-lg pt-0 gap-1">
|
||||
{thumbnailUrl ? (
|
||||
<div className="relative aspect-video w-full overflow-hidden bg-muted">
|
||||
<Image
|
||||
src={thumbnailUrl}
|
||||
alt=""
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="relative flex aspect-video w-full items-center justify-center overflow-hidden bg-muted"
|
||||
aria-hidden
|
||||
>
|
||||
<ImageIcon className="size-12 text-muted-foreground/50" />
|
||||
</div>
|
||||
)}
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-xl font-bold leading-tight line-clamp-2 mt-2">
|
||||
{post.title.rendered}
|
||||
</CardTitle>
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground bg-muted inline-flex p-1 rounded-sm">
|
||||
{authorName && (
|
||||
<span className="inline-flex flex-1 items-center gap-1.5">
|
||||
<User className="size-3.5 shrink-0" aria-hidden />
|
||||
{authorName}
|
||||
</span>
|
||||
)}
|
||||
{modifiedDate && (
|
||||
<time dateTime={post.modified}>
|
||||
{modifiedDate}
|
||||
</time>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0">
|
||||
<p className="text-muted-foreground text-sm leading-relaxed line-clamp-3">
|
||||
{excerpt}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
import {
|
||||
Link2,
|
||||
Clock,
|
||||
Calendar,
|
||||
User,
|
||||
Share2,
|
||||
Check,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface BlogPostMetaProps {
|
||||
/** Path (e.g. /blog/slug). Full URL is built on client for share/copy. */
|
||||
url: string;
|
||||
title: string;
|
||||
readingTimeMinutes: number;
|
||||
/** Shamsi publish date (e.g. "۱۴۰۳/۰۸/۱۵") */
|
||||
date?: string | null;
|
||||
/** ISO date for <time dateTime> */
|
||||
dateTime?: string | null;
|
||||
modifiedDate?: string | null;
|
||||
authorName?: string | null;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function BlogPostMeta({
|
||||
url,
|
||||
title,
|
||||
readingTimeMinutes,
|
||||
date,
|
||||
dateTime,
|
||||
modifiedDate,
|
||||
authorName,
|
||||
className,
|
||||
}: BlogPostMetaProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [shared, setShared] = useState(false);
|
||||
const [fullUrl, setFullUrl] = useState(url);
|
||||
const [canNativeShare, setCanNativeShare] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (url.startsWith("/")) {
|
||||
setFullUrl(window.location.origin + url);
|
||||
} else {
|
||||
setFullUrl(url);
|
||||
}
|
||||
setCanNativeShare(Boolean(typeof navigator !== "undefined" && navigator.share));
|
||||
}, [url]);
|
||||
|
||||
const copyLink = useCallback(async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(fullUrl);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
setCopied(false);
|
||||
}
|
||||
}, [fullUrl]);
|
||||
|
||||
const handleShare = useCallback(async () => {
|
||||
if (typeof navigator !== "undefined" && navigator.share) {
|
||||
try {
|
||||
await navigator.share({
|
||||
title,
|
||||
url: fullUrl,
|
||||
text: title,
|
||||
});
|
||||
setShared(true);
|
||||
setTimeout(() => setShared(false), 2000);
|
||||
} catch (err) {
|
||||
if ((err as Error).name !== "AbortError") copyLink();
|
||||
}
|
||||
} else {
|
||||
copyLink();
|
||||
}
|
||||
}, [title, fullUrl, copyLink]);
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-wrap items-center gap-x-5 gap-y-2 text-sm text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Reading time */}
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Clock className="size-4 shrink-0" aria-hidden />
|
||||
<span>
|
||||
{readingTimeMinutes === 1
|
||||
? "۱ دقیقه مطالعه"
|
||||
: `${readingTimeMinutes.toLocaleString("fa-IR")} دقیقه مطالعه`}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{/* Publish date */}
|
||||
{date && (
|
||||
<time
|
||||
className="inline-flex items-center gap-1.5"
|
||||
dateTime={dateTime ?? undefined}
|
||||
>
|
||||
<Calendar className="size-4 shrink-0" aria-hidden />
|
||||
<span>انتشار: {date}</span>
|
||||
</time>
|
||||
)}
|
||||
|
||||
{/* Last updated */}
|
||||
{modifiedDate && (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Calendar className="size-4 shrink-0" aria-hidden />
|
||||
<span>آخرین بروزرسانی: {modifiedDate}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Author */}
|
||||
{authorName && (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<User className="size-4 shrink-0" aria-hidden />
|
||||
<span>{authorName}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Divider + actions */}
|
||||
<span className="mr-auto inline-flex items-center gap-1">
|
||||
<span
|
||||
className="hidden sm:inline h-4 w-px bg-border ml-2"
|
||||
aria-hidden
|
||||
/>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="size-8 text-muted-foreground hover:text-foreground"
|
||||
onClick={copyLink}
|
||||
aria-label="کپی لینک"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="size-4 text-green-600" />
|
||||
) : (
|
||||
<Link2 className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{copied ? "کپی شد" : "کپی لینک"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="size-8 text-muted-foreground hover:text-foreground"
|
||||
onClick={handleShare}
|
||||
aria-label="اشتراکگذاری"
|
||||
>
|
||||
{shared ? (
|
||||
<Check className="size-4 text-green-600" />
|
||||
) : (
|
||||
<Share2 className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{shared ? "اشتراک گذاشته شد" : canNativeShare ? "اشتراکگذاری" : "کپی لینک"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronDown, List } from "lucide-react";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { TocItem } from "@/lib/blog-utils";
|
||||
|
||||
interface TocGroup {
|
||||
heading: TocItem;
|
||||
children: TocItem[];
|
||||
}
|
||||
|
||||
function groupByLevel2(items: TocItem[]): TocGroup[] {
|
||||
const groups: TocGroup[] = [];
|
||||
let current: TocGroup | null = null;
|
||||
for (const item of items) {
|
||||
if (item.level === 2) {
|
||||
current = { heading: item, children: [] };
|
||||
groups.push(current);
|
||||
} else if (current) {
|
||||
current.children.push(item);
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
interface BlogTocProps {
|
||||
items: TocItem[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const tocLinkClass = (
|
||||
level: number,
|
||||
isActive: boolean
|
||||
) =>
|
||||
cn(
|
||||
"block rounded-md border-r-2 rounded-s-none border-transparent py-1.5 text-sm transition-colors hover:text-foreground hover:bg-muted/60",
|
||||
level === 2 && "pr-3",
|
||||
level === 3 && "pr-5",
|
||||
level === 4 && "pr-7",
|
||||
isActive
|
||||
? "font-medium text-foreground border-primary bg-muted/50"
|
||||
: "text-muted-foreground"
|
||||
);
|
||||
|
||||
export function BlogToc({ items, className }: BlogTocProps) {
|
||||
const [activeId, setActiveId] = useState<string | null>(items[0]?.id ?? null);
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [openSections, setOpenSections] = useState<Set<string>>(() => new Set());
|
||||
|
||||
const groups = useMemo(() => groupByLevel2(items), [items]);
|
||||
|
||||
useEffect(() => {
|
||||
setOpenSections(new Set(groups.map((g) => g.heading.id)));
|
||||
}, [groups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (items.length === 0) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
const id = entry.target.getAttribute("id");
|
||||
if (id) setActiveId(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin: "-80px 0% -70% 0%", threshold: 0 }
|
||||
);
|
||||
|
||||
items.forEach(({ id }) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeId && groups.length > 0) {
|
||||
const group = groups.find(
|
||||
(g) => g.heading.id === activeId || g.children.some((c) => c.id === activeId)
|
||||
);
|
||||
if (group) {
|
||||
setOpenSections((prev) => new Set(prev).add(group.heading.id));
|
||||
}
|
||||
}
|
||||
}, [activeId, groups]);
|
||||
|
||||
if (items.length === 0) return null;
|
||||
|
||||
const renderTocLink = (item: TocItem, onNav?: () => void) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={`#${item.id}`}
|
||||
onClick={() => {
|
||||
setMobileOpen(false);
|
||||
onNav?.();
|
||||
}}
|
||||
className={tocLinkClass(item.level, activeId === item.id)}
|
||||
>
|
||||
<span className="line-clamp-2">{item.text}</span>
|
||||
</Link>
|
||||
);
|
||||
|
||||
const tocList = (
|
||||
<nav aria-label="فهرست مطالب" className="space-y-1">
|
||||
{groups.map(({ heading, children }) => {
|
||||
const hasChildren = children.length > 0;
|
||||
const isOpen = openSections.has(heading.id);
|
||||
|
||||
if (!hasChildren) {
|
||||
return renderTocLink(heading);
|
||||
}
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
key={heading.id}
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
setOpenSections((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (open) next.add(heading.id);
|
||||
else next.delete(heading.id);
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-0.5 rounded-md border-r-2 rounded-s-none py-1.5 pr-3",
|
||||
activeId === heading.id ? "border-primary bg-muted/50" : "border-transparent"
|
||||
)}
|
||||
>
|
||||
<Link
|
||||
href={`#${heading.id}`}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className={cn(
|
||||
"min-w-0 flex-1 text-sm transition-colors hover:text-foreground hover:bg-muted/60 rounded-md py-0.5 -my-0.5",
|
||||
activeId === heading.id ? "font-medium text-foreground" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<span className="line-clamp-2">{heading.text}</span>
|
||||
</Link>
|
||||
<CollapsibleTrigger
|
||||
className="shrink-0 rounded p-1 text-muted-foreground hover:bg-muted/60 hover:text-foreground transition-colors"
|
||||
aria-label={isOpen ? "بستن" : "باز کردن"}
|
||||
>
|
||||
<ChevronDown
|
||||
className={cn("size-4 transition-transform", isOpen && "rotate-180")}
|
||||
/>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<CollapsibleContent>
|
||||
<div className="space-y-1 pt-0.5 pr-2 border-r border-border/50">
|
||||
{children.map((item) => renderTocLink(item))}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Desktop: sticky sidebar left */}
|
||||
<aside
|
||||
className={cn(
|
||||
"hidden lg:block shrink-0",
|
||||
"sticky top-24 self-start max-h-[calc(100vh-6rem)]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<p className="mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
فهرست مطالب
|
||||
</p>
|
||||
<ScrollArea className="h-full pb-8" dir="rtl">
|
||||
{tocList}
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
|
||||
{/* Mobile: collapsible, sticky when scrolling */}
|
||||
<Collapsible
|
||||
open={mobileOpen}
|
||||
onOpenChange={setMobileOpen}
|
||||
className={cn(
|
||||
"lg:hidden w-full sticky top-20 z-10 bg-background",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<CollapsibleTrigger
|
||||
className={cn(
|
||||
"flex w-full items-center justify-between gap-2 rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm font-medium transition-colors hover:bg-muted/50",
|
||||
mobileOpen && "rounded-b-none border-b-0"
|
||||
)}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<List className="size-4 text-muted-foreground" />
|
||||
فهرست مطالب
|
||||
</span>
|
||||
<ChevronDown
|
||||
className={cn("size-4 text-muted-foreground transition-transform", mobileOpen && "rotate-180")}
|
||||
/>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="max-h-64 overflow-y-auto rounded-b-lg border border-t-0 border-border bg-muted/20 p-3">
|
||||
{tocList}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -16,9 +16,7 @@ export default function FooterSection() {
|
||||
return (
|
||||
<footer className="py-16 md:py-32">
|
||||
<div className="mx-auto max-w-5xl px-6">
|
||||
<Link href="/" aria-label="go home" className="mx-auto block size-fit">
|
||||
<Logo />
|
||||
</Link>
|
||||
<Logo />
|
||||
|
||||
<div className="my-8 flex flex-wrap justify-center gap-6 text-sm">
|
||||
{links.map((link, index) => (
|
||||
|
||||
+47
-27
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { Logo } from '@/components/logo'
|
||||
import { Menu, X } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -11,10 +12,17 @@ const menuItems = [
|
||||
{ name: 'صفحه اصلی', href: '/' },
|
||||
{ name: 'درباره ما', href: '/about' },
|
||||
{ name: 'راه حلها', href: '/solutions' },
|
||||
{ name: 'بلاگ', href: '/blog' },
|
||||
{ name: 'قیمتگذاری', href: '/pricing' },
|
||||
]
|
||||
|
||||
function isLinkActive(pathname: string, href: string): boolean {
|
||||
if (href === '/') return pathname === '/'
|
||||
return pathname === href || pathname.startsWith(href + '/')
|
||||
}
|
||||
|
||||
export const HeroHeader = () => {
|
||||
const pathname = usePathname()
|
||||
const [menuState, setMenuState] = React.useState(false)
|
||||
const [isScrolled, setIsScrolled] = React.useState(false)
|
||||
|
||||
@@ -33,13 +41,7 @@ export const HeroHeader = () => {
|
||||
<div className={cn('mx-auto mt-2 max-w-6xl px-6 transition-all duration-300 lg:px-12 bg-background/80 backdrop-blur-md rounded-2xl border border-border/50', isScrolled && 'bg-background/95 max-w-4xl shadow-lg lg:px-5')}>
|
||||
<div className="relative flex flex-wrap items-center justify-between gap-6 py-2 lg:gap-0 lg:py-3">
|
||||
<div className="flex w-full justify-between lg:w-auto">
|
||||
<Link
|
||||
href="/"
|
||||
aria-label="home"
|
||||
className="flex items-center space-x-2">
|
||||
<Logo />
|
||||
</Link>
|
||||
|
||||
<Logo />
|
||||
<button
|
||||
onClick={() => setMenuState(!menuState)}
|
||||
aria-label={menuState == true ? 'بستن منو' : 'باز کردن منو'}
|
||||
@@ -51,16 +53,25 @@ export const HeroHeader = () => {
|
||||
|
||||
<div className="absolute inset-0 m-auto hidden size-fit lg:flex items-center gap-8">
|
||||
<ul className="flex gap-8 text-sm">
|
||||
{menuItems.map((item, index) => (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-muted-foreground hover:text-foreground transition-all duration-300 relative group">
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
<span className="absolute bottom-0 right-0 w-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{menuItems.map((item, index) => {
|
||||
const active = isLinkActive(pathname ?? '', item.href)
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'text-muted-foreground hover:text-foreground transition-all duration-300 relative group',
|
||||
active && 'text-foreground'
|
||||
)}>
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
<span className={cn(
|
||||
'absolute bottom-0 right-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full',
|
||||
active ? 'w-full' : 'w-0'
|
||||
)} />
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
@@ -68,16 +79,25 @@ export const HeroHeader = () => {
|
||||
<div className="bg-background in-data-[state=active]:block lg:in-data-[state=active]:flex mb-6 hidden w-full flex-wrap items-center justify-end space-y-8 rounded-3xl border p-6 shadow-2xl shadow-zinc-300/20 md:flex-nowrap lg:m-0 lg:flex lg:w-fit lg:gap-6 lg:space-y-0 lg:border-transparent lg:bg-transparent lg:p-0 lg:shadow-none dark:shadow-none dark:lg:bg-transparent">
|
||||
<div className="lg:hidden w-full">
|
||||
<ul className="space-y-6 text-base">
|
||||
{menuItems.map((item, index) => (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-muted-foreground hover:text-foreground transition-all duration-300 relative group block">
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
<span className="absolute bottom-0 right-0 w-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
{menuItems.map((item, index) => {
|
||||
const active = isLinkActive(pathname ?? '', item.href)
|
||||
return (
|
||||
<li key={index}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'text-muted-foreground hover:text-foreground transition-all duration-300 relative group block',
|
||||
active && 'text-foreground'
|
||||
)}>
|
||||
<span className="relative z-10">{item.name}</span>
|
||||
<span className={cn(
|
||||
'absolute bottom-0 right-0 h-0.5 bg-accent transition-all duration-300 group-hover:w-full',
|
||||
active ? 'w-full' : 'w-0'
|
||||
)} />
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
<div className="mt-6 flex justify-end">
|
||||
<ThemeToggle />
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { ChevronUp } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const SCROLL_THRESHOLD = 0.4; // show after 40% scroll
|
||||
|
||||
export function ScrollToTop({ className }: { className?: string }) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function onScroll() {
|
||||
const maxScroll =
|
||||
document.documentElement.scrollHeight - window.innerHeight;
|
||||
if (maxScroll <= 0) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
const ratio = window.scrollY / maxScroll;
|
||||
setVisible(ratio >= SCROLL_THRESHOLD);
|
||||
}
|
||||
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed bottom-6 start-6 z-50 transition-opacity duration-300",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={scrollToTop}
|
||||
aria-label="برگشت به بالا"
|
||||
className="size-10 rounded-md shadow-md"
|
||||
>
|
||||
<ChevronUp className="size-5" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* زیرساخت کشف بلاکهای محتوای بلاگ و تبدیل به JSON.
|
||||
* مرحله ۱: کشف بلاکهای شناختهشده در HTML (collapse, و بعداً blockquote, code, ...)
|
||||
* مرحله ۲: استخراج payload به صورت JSON برای هر بلاک
|
||||
* مرحله ۳: لایه بیرونی بر اساس blockType و payload کامپوننت مناسب را رندر میکند.
|
||||
*
|
||||
* برای افزودن بلاک جدید: یک تعریف در BLOCK_DEFINITIONS اضافه کنید و در رندر blockType را هندل کنید.
|
||||
*/
|
||||
import * as cheerio from "cheerio";
|
||||
import type { Element } from "domhandler";
|
||||
|
||||
// ─── انواع بلاک (برای گسترش بعدی) ───────────────────────────────────────────
|
||||
|
||||
/** نام نوع بلاک؛ با افزودن بلاک جدید این یونیون را گسترش دهید. */
|
||||
export type BlockType = "collapse" | "blockquote" | "code";
|
||||
|
||||
/** Payload بلاک collapse (سوالات متداول / accordion). */
|
||||
export interface CollapseBlockPayload {
|
||||
title: string;
|
||||
items: { question: string; answer: string }[];
|
||||
}
|
||||
|
||||
/** Payload بلاک blockquote — بعداً پیادهسازی میشود. */
|
||||
// export interface BlockquoteBlockPayload { ... }
|
||||
|
||||
/** Payload بلاک code — بعداً پیادهسازی میشود. */
|
||||
// export interface CodeBlockPayload { ... }
|
||||
|
||||
/** یونیون همهٔ payloadها برای تایپامن رندر. */
|
||||
export type BlockPayload = CollapseBlockPayload; // | BlockquoteBlockPayload | CodeBlockPayload
|
||||
|
||||
// ─── سگمنت خروجی برای لایه رندر ─────────────────────────────────────────────
|
||||
|
||||
/** یک سگمنت HTML خام. */
|
||||
export interface HtmlSegment {
|
||||
type: "html";
|
||||
content: string;
|
||||
}
|
||||
|
||||
/** یک بلاک با نوع و payload (JSON) برای رندر با کامپوننت مناسب. */
|
||||
export interface BlockSegment {
|
||||
type: "block";
|
||||
blockType: BlockType;
|
||||
payload: BlockPayload;
|
||||
}
|
||||
|
||||
/** هر سگمنت محتوا: یا HTML یا یک بلاک با JSON. */
|
||||
export type ContentSegment = HtmlSegment | BlockSegment;
|
||||
|
||||
/** خروجی کشف بلاک: HTML با placeholderها و آرایهٔ سگمنتها (با payload توکار). */
|
||||
export interface DiscoveredContent {
|
||||
/** HTML بعد از جایگزینی بلاکها با placeholder (برای ادامهٔ پردازش مثلاً تزریق id سرنویس). */
|
||||
htmlWithPlaceholders: string;
|
||||
/** سگمنتها به ترتیب برای رندر؛ هر بلاک شامل blockType و payload است. */
|
||||
segments: ContentSegment[];
|
||||
}
|
||||
|
||||
// ─── تعریف بلاکها (ثبت برای کشف و استخراج) ────────────────────────────────
|
||||
|
||||
export interface BlockDefinition<T extends BlockPayload = BlockPayload> {
|
||||
blockType: BlockType;
|
||||
/** سلکتور Cheerio برای پیدا کردن المنت ریشهٔ بلاک. */
|
||||
selector: string;
|
||||
/** استخراج payload (JSON) از المنت. */
|
||||
extract: ($: cheerio.CheerioAPI, el: Element) => T;
|
||||
}
|
||||
|
||||
function extractCollapsePayload(
|
||||
$: cheerio.CheerioAPI,
|
||||
el: Element
|
||||
): CollapseBlockPayload {
|
||||
const $el = $(el);
|
||||
const innerHtml = $el.html() ?? "";
|
||||
const $inner = cheerio.load(innerHtml, null, false);
|
||||
|
||||
const title = $inner("h2").first().text().trim();
|
||||
const items: { question: string; answer: string }[] = [];
|
||||
$inner("ul").each((_: number, ul: Element) => {
|
||||
const question = $inner(ul).find("li strong").first().text().trim();
|
||||
const answer = $inner(ul).nextAll("p").first().text().trim();
|
||||
if (question || answer) items.push({ question, answer });
|
||||
});
|
||||
|
||||
return { title, items };
|
||||
}
|
||||
|
||||
/**
|
||||
* ثبت همهٔ بلاکهای شناختهشده.
|
||||
* برای بلاکهای جدید (مثلاً blockquote, code) فقط یک مورد جدید به این آرایه اضافه کنید.
|
||||
*/
|
||||
export const BLOCK_DEFINITIONS: BlockDefinition[] = [
|
||||
{
|
||||
blockType: "collapse",
|
||||
selector: ".collpase-section",
|
||||
extract: extractCollapsePayload as BlockDefinition["extract"],
|
||||
},
|
||||
// بعداً:
|
||||
// { blockType: "blockquote", selector: "blockquote.wp-block-quote", extract: extractBlockquotePayload },
|
||||
// { blockType: "code", selector: "pre.wp-block-code", extract: extractCodePayload },
|
||||
];
|
||||
|
||||
// ─── کشف بلاک و ساخت سگمنتها ─────────────────────────────────────────────
|
||||
|
||||
const PLACEHOLDER_REGEX =
|
||||
/<div data-blog-block-type="([^"]+)" data-blog-block-id="(\d+)"[^>]*><\/div>/g;
|
||||
|
||||
function isElement(node: unknown): node is Element {
|
||||
return typeof node === "object" && node !== null && (node as Element).type === "tag";
|
||||
}
|
||||
|
||||
/**
|
||||
* ترتیب قرارگیری المنت در سند را با یک مپ نگه میدارد.
|
||||
*/
|
||||
function buildDocumentOrderMap($: cheerio.CheerioAPI): Map<Element, number> {
|
||||
const map = new Map<Element, number>();
|
||||
let index = 0;
|
||||
$("*")
|
||||
.toArray()
|
||||
.filter(isElement)
|
||||
.forEach((el) => {
|
||||
map.set(el, index++);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* مرحله ۱ و ۲: از روی HTML محتوا بلاکهای شناختهشده را کشف میکند،
|
||||
* به JSON (payload) تبدیل میکند، و خروجی را به صورت HTML با placeholder و
|
||||
* آرایهٔ segments (با payload توکار) به لایه بیرونی میدهد.
|
||||
*/
|
||||
export function discoverBlocks(html: string): DiscoveredContent {
|
||||
const $ = cheerio.load(html, null, false);
|
||||
const orderMap = buildDocumentOrderMap($);
|
||||
|
||||
type BlockEntry = {
|
||||
el: Element;
|
||||
blockType: BlockType;
|
||||
blockId: number;
|
||||
payload: BlockPayload;
|
||||
};
|
||||
const entries: BlockEntry[] = [];
|
||||
const blocksByType: Record<string, BlockPayload[]> = {
|
||||
collapse: [],
|
||||
blockquote: [],
|
||||
code: [],
|
||||
};
|
||||
|
||||
for (const def of BLOCK_DEFINITIONS) {
|
||||
const elements = $('.collpase-section').toArray();
|
||||
for (const el of elements) {
|
||||
const payload = def.extract($, el) as BlockPayload;
|
||||
const blockId = blocksByType[def.blockType].length;
|
||||
blocksByType[def.blockType].push(payload);
|
||||
entries.push({
|
||||
el,
|
||||
blockType: def.blockType,
|
||||
blockId,
|
||||
payload,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// مرتبسازی بر اساس ترتیب در سند (از آخر به اول جایگزین میکنیم تا ایندکسها جابجا نشوند)
|
||||
entries.sort((a, b) => orderMap.get(a.el)! - orderMap.get(b.el)!);
|
||||
|
||||
// جایگزینی با placeholder از آخر به اول
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
const { el, blockType, blockId } = entries[i];
|
||||
$(el).replaceWith(
|
||||
`<div data-blog-block-type="${blockType}" data-blog-block-id="${blockId}"></div>`
|
||||
);
|
||||
}
|
||||
|
||||
const htmlWithPlaceholders = $.html() ?? html;
|
||||
|
||||
// ساخت segments: تقسیم با placeholder و قرار دادن payload در هر بلاک
|
||||
const segments: ContentSegment[] = [];
|
||||
let lastEnd = 0;
|
||||
const regex = new RegExp(PLACEHOLDER_REGEX.source, "g");
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = regex.exec(htmlWithPlaceholders)) !== null) {
|
||||
if (m.index > lastEnd) {
|
||||
segments.push({
|
||||
type: "html",
|
||||
content: htmlWithPlaceholders.slice(lastEnd, m.index),
|
||||
});
|
||||
}
|
||||
const blockType = m[1] as BlockType;
|
||||
const blockId = parseInt(m[2], 10);
|
||||
const list = blocksByType[blockType];
|
||||
if (list && blockId >= 0 && blockId < list.length) {
|
||||
segments.push({
|
||||
type: "block",
|
||||
blockType,
|
||||
payload: list[blockId],
|
||||
});
|
||||
}
|
||||
lastEnd = regex.lastIndex;
|
||||
}
|
||||
if (lastEnd < htmlWithPlaceholders.length) {
|
||||
segments.push({
|
||||
type: "html",
|
||||
content: htmlWithPlaceholders.slice(lastEnd),
|
||||
});
|
||||
}
|
||||
|
||||
return { htmlWithPlaceholders, segments };
|
||||
}
|
||||
|
||||
/**
|
||||
* بعد از تزریق id سرنویسها در HTML، محتوای هر سگمنت html را با نسخهٔ بهروز (شامل idها) جایگزین میکند.
|
||||
* لایهٔ blog-utils بعد از inject کردن idها این تابع را صدا میزند.
|
||||
*/
|
||||
export function mergeHeadingIdsIntoContentSegments(
|
||||
contentWithIds: string,
|
||||
segments: ContentSegment[]
|
||||
): ContentSegment[] {
|
||||
const parts: string[] = [];
|
||||
let last = 0;
|
||||
let m: RegExpExecArray | null;
|
||||
const regex = new RegExp(PLACEHOLDER_REGEX.source, "g");
|
||||
while ((m = regex.exec(contentWithIds)) !== null) {
|
||||
parts.push(contentWithIds.slice(last, m.index));
|
||||
last = regex.lastIndex;
|
||||
}
|
||||
parts.push(contentWithIds.slice(last));
|
||||
|
||||
let j = 0;
|
||||
return segments.map((seg) => {
|
||||
if (seg.type === "html") {
|
||||
return { ...seg, content: parts[j++] ?? seg.content };
|
||||
}
|
||||
return seg;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
discoverBlocks,
|
||||
mergeHeadingIdsIntoContentSegments,
|
||||
type ContentSegment,
|
||||
} from "@/lib/blog-blocks";
|
||||
|
||||
export type { ContentSegment } from "@/lib/blog-blocks";
|
||||
|
||||
/**
|
||||
* Creates a URL-safe slug from heading text (strip HTML, normalize, Persian-friendly).
|
||||
*/
|
||||
function slugify(text: string): string {
|
||||
const stripped = text.replace(/<[^>]*>/g, "").trim();
|
||||
return stripped
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^\p{L}\p{N}-]/gu, "")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-|-$/g, "")
|
||||
.toLowerCase() || "section";
|
||||
}
|
||||
|
||||
export interface TocItem {
|
||||
id: string;
|
||||
text: string;
|
||||
level: number; // 2 = h2, 3 = h3, 4 = h4
|
||||
}
|
||||
|
||||
const HEADING_REGEX = /<(h[234])[^>]*>([\s\S]*?)<\/\1>/gi;
|
||||
const usedIds = new Map<string, number>();
|
||||
|
||||
function ensureUniqueId(baseId: string): string {
|
||||
const count = usedIds.get(baseId) ?? 0;
|
||||
usedIds.set(baseId, count + 1);
|
||||
return count === 0 ? baseId : `${baseId}-${count}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* پارس محتوا: ابتدا بلاکها کشف و به JSON تبدیل میشوند (blog-blocks)،
|
||||
* سپس id سرنویسها تزریق میشود و سگمنتهای نهایی برای رندر برمیگردند.
|
||||
*/
|
||||
export function parseContentHeadings(html: string): {
|
||||
contentWithIds: string;
|
||||
toc: TocItem[];
|
||||
segments: ContentSegment[];
|
||||
} {
|
||||
const { htmlWithPlaceholders, segments: rawSegments } = discoverBlocks(html);
|
||||
|
||||
usedIds.clear();
|
||||
const toc: TocItem[] = [];
|
||||
let lastIndex = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
const parts: string[] = [];
|
||||
const regex = new RegExp(HEADING_REGEX.source, "gi");
|
||||
|
||||
while ((match = regex.exec(htmlWithPlaceholders)) !== null) {
|
||||
const [fullMatch, tag, inner] = match;
|
||||
const level = parseInt(tag.charAt(1), 10) as 2 | 3 | 4;
|
||||
const text = inner.replace(/<[^>]*>/g, "").trim();
|
||||
const baseId = slugify(text);
|
||||
const id = ensureUniqueId(baseId);
|
||||
|
||||
toc.push({ id, text, level });
|
||||
|
||||
const openTag = fullMatch.slice(0, fullMatch.indexOf(">") + 1);
|
||||
const hasId = /^\s*<h[234][^>]*\bid\s*=/i.test(openTag);
|
||||
const newOpenTag = hasId
|
||||
? openTag
|
||||
: openTag.replace(/^(<\s*h[234])(\s|>)/i, `$1 id="${id}"$2`);
|
||||
|
||||
parts.push(htmlWithPlaceholders.slice(lastIndex, match.index));
|
||||
parts.push(newOpenTag + inner + `</${tag}>`);
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
|
||||
parts.push(htmlWithPlaceholders.slice(lastIndex));
|
||||
const contentWithIds = parts.join("");
|
||||
const segments = mergeHeadingIdsIntoContentSegments(
|
||||
contentWithIds,
|
||||
rawSegments
|
||||
);
|
||||
|
||||
return { contentWithIds, toc, segments };
|
||||
}
|
||||
|
||||
/** Average reading speed in words per minute (Persian/English mix). */
|
||||
const WPM = 200;
|
||||
|
||||
/**
|
||||
* Estimates reading time in minutes from HTML content.
|
||||
*/
|
||||
export function getReadingTimeMinutes(html: string): number {
|
||||
const text = html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
||||
const wordCount = text.split(/\s+/).filter(Boolean).length;
|
||||
return Math.max(1, Math.ceil(wordCount / WPM));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { format, newDate } from "date-fns-jalali";
|
||||
import { faIR } from "date-fns-jalali/locale";
|
||||
|
||||
/**
|
||||
* Format a Gregorian date (Date or ISO string) to Shamsi (Jalali) Persian string.
|
||||
* Example: "۱۴ بهمن ۱۴۰۴"
|
||||
*/
|
||||
export function formatDateShamsi(
|
||||
date: Date | string,
|
||||
formatStr: string = "d MMMM yyyy"
|
||||
): string {
|
||||
const d = typeof date === "string" ? new Date(date) : date;
|
||||
if (Number.isNaN(d.getTime())) return "";
|
||||
return format(d, formatStr, { locale: faIR });
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
const WP_API_BASE = "https://wpa.feep.ir/wp-json/wp/v2";
|
||||
const FETCH_TIMEOUT_MS = 15_000;
|
||||
|
||||
export interface WpMediaSize {
|
||||
source_url: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface WpFeaturedMedia {
|
||||
source_url: string;
|
||||
alt_text?: string;
|
||||
media_details?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: Record<string, WpMediaSize>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface WpAuthor {
|
||||
id: number;
|
||||
name: string;
|
||||
slug?: string;
|
||||
}
|
||||
|
||||
export interface WpPost {
|
||||
id: number;
|
||||
date: string;
|
||||
modified?: string;
|
||||
slug: string;
|
||||
title: { rendered: string };
|
||||
content: { rendered: string };
|
||||
excerpt: { rendered: string };
|
||||
featured_media: number;
|
||||
categories: number[];
|
||||
author?: number;
|
||||
_embedded?: {
|
||||
"wp:featuredmedia"?: WpFeaturedMedia[];
|
||||
author?: WpAuthor[];
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns thumbnail URL for list/card (medium) or full for single post. */
|
||||
export function getFeaturedImageUrl(
|
||||
post: WpPost,
|
||||
size: "medium" | "medium_large" | "full" = "medium"
|
||||
): string | null {
|
||||
const media = post._embedded?.["wp:featuredmedia"]?.[0];
|
||||
if (!media?.source_url) return null;
|
||||
const sizes = media.media_details?.sizes;
|
||||
if (sizes?.[size]?.source_url) return sizes[size].source_url;
|
||||
if (size !== "full" && sizes?.medium?.source_url) return sizes.medium.source_url;
|
||||
return media.source_url;
|
||||
}
|
||||
|
||||
export interface WpCategory {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface GetPostsResult {
|
||||
posts: WpPost[];
|
||||
total: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface GetPostsOptions {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
search?: string;
|
||||
categoryId?: number;
|
||||
}
|
||||
|
||||
async function fetchWithTimeout(
|
||||
url: string,
|
||||
options?: RequestInit
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
next: { revalidate: 0 },
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
return res;
|
||||
} catch (e) {
|
||||
clearTimeout(timeout);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPosts(
|
||||
options: GetPostsOptions = {}
|
||||
): Promise<GetPostsResult> {
|
||||
const { page = 1, perPage = 9, search, categoryId } = options;
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
params.set("per_page", String(perPage));
|
||||
if (search?.trim()) params.set("search", search.trim());
|
||||
if (categoryId) params.set("categories", String(categoryId));
|
||||
params.set("_embed", "1");
|
||||
|
||||
const url = `${WP_API_BASE}/posts?${params.toString()}`;
|
||||
const res = await fetchWithTimeout(url);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
const total = parseInt(res.headers.get("x-wp-total") ?? "0", 10);
|
||||
const totalPages = parseInt(res.headers.get("x-wp-totalpages") ?? "1", 10);
|
||||
const posts: WpPost[] = await res.json();
|
||||
|
||||
return { posts, total, totalPages };
|
||||
}
|
||||
|
||||
export async function getPostBySlug(slug: string): Promise<WpPost | null> {
|
||||
const decodedSlug = decodeURIComponent(slug);
|
||||
const params = new URLSearchParams({ slug: decodedSlug, _embed: "1" });
|
||||
const url = `${WP_API_BASE}/posts?${params.toString()}`;
|
||||
const res = await fetchWithTimeout(url);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
const posts: WpPost[] = await res.json();
|
||||
return posts.length > 0 ? posts[0] : null;
|
||||
}
|
||||
|
||||
export async function getCategories(): Promise<WpCategory[]> {
|
||||
const url = `${WP_API_BASE}/categories?per_page=100`;
|
||||
const res = await fetchWithTimeout(url);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
const data: WpCategory[] = await res.json();
|
||||
return data.filter((c) => c.count > 0);
|
||||
}
|
||||
@@ -12,6 +12,10 @@ const nextConfig: NextConfig = {
|
||||
protocol: "https",
|
||||
hostname: "images.unsplash.com",
|
||||
},
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "wpa.feep.ir",
|
||||
},
|
||||
],
|
||||
},
|
||||
typescript: {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
@@ -41,6 +42,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-jalali": "^4.1.0-0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.555.0",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz"
|
||||
integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==
|
||||
|
||||
"@babel/core@^7.0.0", "@babel/core@^7.24.4":
|
||||
"@babel/core@^7.24.4":
|
||||
version "7.28.5"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz"
|
||||
integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==
|
||||
@@ -164,6 +164,14 @@
|
||||
"@emnapi/wasi-threads" "1.1.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/core@^1.6.0", "@emnapi/core@^1.7.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.8.1.tgz#fd9efe721a616288345ffee17a1f26ac5dd01349"
|
||||
integrity sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==
|
||||
dependencies:
|
||||
"@emnapi/wasi-threads" "1.1.0"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/runtime@^1.4.3":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz"
|
||||
@@ -171,9 +179,16 @@
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/wasi-threads@1.1.0":
|
||||
"@emnapi/runtime@^1.6.0", "@emnapi/runtime@^1.7.0", "@emnapi/runtime@^1.7.1":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.8.1.tgz#550fa7e3c0d49c5fb175a116e8cd70614f9a22a5"
|
||||
integrity sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/wasi-threads@1.1.0", "@emnapi/wasi-threads@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf"
|
||||
integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
@@ -320,6 +335,143 @@
|
||||
resolved "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz"
|
||||
integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==
|
||||
|
||||
"@img/sharp-darwin-arm64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86"
|
||||
integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-darwin-arm64" "1.2.4"
|
||||
|
||||
"@img/sharp-darwin-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b"
|
||||
integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-darwin-x64" "1.2.4"
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43"
|
||||
integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==
|
||||
|
||||
"@img/sharp-libvips-darwin-x64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc"
|
||||
integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==
|
||||
|
||||
"@img/sharp-libvips-linux-arm64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318"
|
||||
integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==
|
||||
|
||||
"@img/sharp-libvips-linux-arm@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d"
|
||||
integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==
|
||||
|
||||
"@img/sharp-libvips-linux-ppc64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7"
|
||||
integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==
|
||||
|
||||
"@img/sharp-libvips-linux-riscv64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de"
|
||||
integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==
|
||||
|
||||
"@img/sharp-libvips-linux-s390x@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec"
|
||||
integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==
|
||||
|
||||
"@img/sharp-libvips-linux-x64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce"
|
||||
integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06"
|
||||
integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75"
|
||||
integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==
|
||||
|
||||
"@img/sharp-linux-arm64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc"
|
||||
integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-arm64" "1.2.4"
|
||||
|
||||
"@img/sharp-linux-arm@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d"
|
||||
integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-arm" "1.2.4"
|
||||
|
||||
"@img/sharp-linux-ppc64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813"
|
||||
integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-ppc64" "1.2.4"
|
||||
|
||||
"@img/sharp-linux-riscv64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60"
|
||||
integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-riscv64" "1.2.4"
|
||||
|
||||
"@img/sharp-linux-s390x@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7"
|
||||
integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-s390x" "1.2.4"
|
||||
|
||||
"@img/sharp-linux-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8"
|
||||
integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-x64" "1.2.4"
|
||||
|
||||
"@img/sharp-linuxmusl-arm64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086"
|
||||
integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
|
||||
|
||||
"@img/sharp-linuxmusl-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f"
|
||||
integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linuxmusl-x64" "1.2.4"
|
||||
|
||||
"@img/sharp-wasm32@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0"
|
||||
integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==
|
||||
dependencies:
|
||||
"@emnapi/runtime" "^1.7.0"
|
||||
|
||||
"@img/sharp-win32-arm64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a"
|
||||
integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==
|
||||
|
||||
"@img/sharp-win32-ia32@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de"
|
||||
integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==
|
||||
|
||||
"@img/sharp-win32-x64@0.34.5":
|
||||
version "0.34.5"
|
||||
resolved "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz"
|
||||
@@ -359,6 +511,24 @@
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@napi-rs/wasm-runtime@^0.2.11":
|
||||
version "0.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2"
|
||||
integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.4.3"
|
||||
"@emnapi/runtime" "^1.4.3"
|
||||
"@tybys/wasm-util" "^0.10.0"
|
||||
|
||||
"@napi-rs/wasm-runtime@^1.0.7":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz#c3705ab549d176b8dc5172723d6156c3dc426af2"
|
||||
integrity sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.7.1"
|
||||
"@emnapi/runtime" "^1.7.1"
|
||||
"@tybys/wasm-util" "^0.10.1"
|
||||
|
||||
"@next/env@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.npmjs.org/@next/env/-/env-16.0.6.tgz"
|
||||
@@ -371,6 +541,41 @@
|
||||
dependencies:
|
||||
fast-glob "3.3.1"
|
||||
|
||||
"@next/swc-darwin-arm64@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.6.tgz#e43c59c193a27a10f299c15c7305560226680493"
|
||||
integrity sha512-AGzKiPlDiui+9JcPRHLI4V9WFTTcKukhJTfK9qu3e0tz+Y/88B7vo5yZoO7UaikplJEHORzG3QaBFQfkjhnL0Q==
|
||||
|
||||
"@next/swc-darwin-x64@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.6.tgz#083292f12c7fbc31658b5c7a581be1ec1c0bb04e"
|
||||
integrity sha512-LlLLNrK9WCIUkq2GciWDcquXYIf7vLxX8XE49gz7EncssZGL1vlHwgmURiJsUZAvk0HM1a8qb1ABDezsjAE/jw==
|
||||
|
||||
"@next/swc-linux-arm64-gnu@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.6.tgz#2495a4dc0d6ff6fba7bd5edfbec72c79b3221c29"
|
||||
integrity sha512-r04NzmLSGGfG8EPXKVK72N5zDNnq9pa9el78LhdtqIC3zqKh74QfKHnk24DoK4PEs6eY7sIK/CnNpt30oc59kg==
|
||||
|
||||
"@next/swc-linux-arm64-musl@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.6.tgz#06b6a22512a7f721f4075166177c965902d9a735"
|
||||
integrity sha512-hfB/QV0hA7lbD1OJxp52wVDlpffUMfyxUB5ysZbb/pBC5iuhyLcEKSVQo56PFUUmUQzbMsAtUu6k2Gh9bBtWXA==
|
||||
|
||||
"@next/swc-linux-x64-gnu@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.6.tgz#8ffa6574b3c64edae3c01255231c708f2abe21d9"
|
||||
integrity sha512-PZJushBgfvKhJBy01yXMdgL+l5XKr7uSn5jhOQXQXiH3iPT2M9iG64yHpPNGIKitKrHJInwmhPVGogZBAJOCPw==
|
||||
|
||||
"@next/swc-linux-x64-musl@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.6.tgz#4d2d912563b8423b304b5ebac091f52aa5b4ba49"
|
||||
integrity sha512-LqY76IojrH9yS5fyATjLzlOIOgwyzBuNRqXwVxcGfZ58DWNQSyfnLGlfF6shAEqjwlDNLh4Z+P0rnOI87Y9jEw==
|
||||
|
||||
"@next/swc-win32-arm64-msvc@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.6.tgz#d8f70405b5f0a3fb5037719deb0ae05085c335cd"
|
||||
integrity sha512-eIfSNNqAkj0tqKRf0u7BVjqylJCuabSrxnpSENY3YKApqwDMeAqYPmnOwmVe6DDl3Lvkbe7cJAyP6i9hQ5PmmQ==
|
||||
|
||||
"@next/swc-win32-x64-msvc@16.0.6":
|
||||
version "16.0.6"
|
||||
resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.6.tgz"
|
||||
@@ -384,7 +589,7 @@
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
|
||||
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
@@ -478,7 +683,7 @@
|
||||
"@radix-ui/react-use-previous" "1.1.1"
|
||||
"@radix-ui/react-use-size" "1.1.1"
|
||||
|
||||
"@radix-ui/react-collapsible@^1.1.12", "@radix-ui/react-collapsible@1.1.12":
|
||||
"@radix-ui/react-collapsible@1.1.12", "@radix-ui/react-collapsible@^1.1.12":
|
||||
version "1.1.12"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz"
|
||||
integrity sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==
|
||||
@@ -502,7 +707,7 @@
|
||||
"@radix-ui/react-primitive" "2.1.3"
|
||||
"@radix-ui/react-slot" "1.2.3"
|
||||
|
||||
"@radix-ui/react-compose-refs@^1.1.1", "@radix-ui/react-compose-refs@1.1.2":
|
||||
"@radix-ui/react-compose-refs@1.1.2", "@radix-ui/react-compose-refs@^1.1.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz"
|
||||
integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==
|
||||
@@ -529,7 +734,7 @@
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz"
|
||||
integrity sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==
|
||||
|
||||
"@radix-ui/react-dialog@^1.1.1", "@radix-ui/react-dialog@^1.1.15", "@radix-ui/react-dialog@^1.1.6", "@radix-ui/react-dialog@1.1.15":
|
||||
"@radix-ui/react-dialog@1.1.15", "@radix-ui/react-dialog@^1.1.1", "@radix-ui/react-dialog@^1.1.15", "@radix-ui/react-dialog@^1.1.6":
|
||||
version "1.1.15"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz"
|
||||
integrity sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==
|
||||
@@ -607,7 +812,7 @@
|
||||
"@radix-ui/react-primitive" "2.1.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.2.2"
|
||||
|
||||
"@radix-ui/react-id@^1.1.0", "@radix-ui/react-id@1.1.1":
|
||||
"@radix-ui/react-id@1.1.1", "@radix-ui/react-id@^1.1.0":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz"
|
||||
integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==
|
||||
@@ -734,7 +939,7 @@
|
||||
"@radix-ui/react-compose-refs" "1.1.2"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||
|
||||
"@radix-ui/react-primitive@^2.0.2", "@radix-ui/react-primitive@2.1.3":
|
||||
"@radix-ui/react-primitive@2.1.3", "@radix-ui/react-primitive@^2.0.2":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz"
|
||||
integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==
|
||||
@@ -853,13 +1058,6 @@
|
||||
"@radix-ui/react-use-previous" "1.1.1"
|
||||
"@radix-ui/react-use-size" "1.1.1"
|
||||
|
||||
"@radix-ui/react-slot@^1.2.4", "@radix-ui/react-slot@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz"
|
||||
integrity sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.2"
|
||||
|
||||
"@radix-ui/react-slot@1.2.3":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz"
|
||||
@@ -867,6 +1065,13 @@
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.2"
|
||||
|
||||
"@radix-ui/react-slot@1.2.4", "@radix-ui/react-slot@^1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz"
|
||||
integrity sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==
|
||||
dependencies:
|
||||
"@radix-ui/react-compose-refs" "1.1.2"
|
||||
|
||||
"@radix-ui/react-switch@^1.2.6":
|
||||
version "1.2.6"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz"
|
||||
@@ -907,7 +1112,7 @@
|
||||
"@radix-ui/react-toggle" "1.1.10"
|
||||
"@radix-ui/react-use-controllable-state" "1.2.2"
|
||||
|
||||
"@radix-ui/react-toggle@^1.1.10", "@radix-ui/react-toggle@1.1.10":
|
||||
"@radix-ui/react-toggle@1.1.10", "@radix-ui/react-toggle@^1.1.10":
|
||||
version "1.1.10"
|
||||
resolved "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz"
|
||||
integrity sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==
|
||||
@@ -1034,6 +1239,68 @@
|
||||
source-map-js "^1.2.1"
|
||||
tailwindcss "4.1.17"
|
||||
|
||||
"@tailwindcss/oxide-android-arm64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz#17f0dc901f88a979c5bff618181bce596dff596d"
|
||||
integrity sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz#63e12e62b83f6949dbb10b5a7f6e441606835efc"
|
||||
integrity sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz#6dad270d2777508f55e2b73eca0eaef625bc45a7"
|
||||
integrity sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz#e7628b4602ac7d73c11a9922ecb83c24337eff55"
|
||||
integrity sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz#4d96a6fe4c7ed20e7a013101ee46f46caca2233e"
|
||||
integrity sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz#adc3c01cd73610870bfc21db5713571e08fb2210"
|
||||
integrity sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz#39ceda30407af56a1ee125b2c5ce856c6d29250f"
|
||||
integrity sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz#a3d4bd876c04d09856af0c394f5095fbc8a2b14c"
|
||||
integrity sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz#bdc20aa4fb2d28cc928f2cfffff7a9cd03a51d5b"
|
||||
integrity sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz#7c0804748935928751838f86ff4f879c38f8a6d7"
|
||||
integrity sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==
|
||||
dependencies:
|
||||
"@emnapi/core" "^1.6.0"
|
||||
"@emnapi/runtime" "^1.6.0"
|
||||
"@emnapi/wasi-threads" "^1.1.0"
|
||||
"@napi-rs/wasm-runtime" "^1.0.7"
|
||||
"@tybys/wasm-util" "^0.10.1"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz#7222fc2ceee9d45ebe5aebf38707ee9833a20475"
|
||||
integrity sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc@4.1.17":
|
||||
version "4.1.17"
|
||||
resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz"
|
||||
@@ -1068,9 +1335,9 @@
|
||||
postcss "^8.4.41"
|
||||
tailwindcss "4.1.17"
|
||||
|
||||
"@tybys/wasm-util@^0.10.0":
|
||||
"@tybys/wasm-util@^0.10.0", "@tybys/wasm-util@^0.10.1":
|
||||
version "0.10.1"
|
||||
resolved "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414"
|
||||
integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
@@ -1148,12 +1415,12 @@
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/react-dom@*", "@types/react-dom@^19":
|
||||
"@types/react-dom@^19":
|
||||
version "19.2.3"
|
||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz"
|
||||
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
||||
|
||||
"@types/react@*", "@types/react@^19", "@types/react@^19.2.0":
|
||||
"@types/react@^19":
|
||||
version "19.2.7"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz"
|
||||
integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==
|
||||
@@ -1175,7 +1442,7 @@
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/parser@^8.48.0", "@typescript-eslint/parser@8.48.0":
|
||||
"@typescript-eslint/parser@8.48.0":
|
||||
version "8.48.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz"
|
||||
integrity sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==
|
||||
@@ -1203,7 +1470,7 @@
|
||||
"@typescript-eslint/types" "8.48.0"
|
||||
"@typescript-eslint/visitor-keys" "8.48.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@^8.48.0", "@typescript-eslint/tsconfig-utils@8.48.0":
|
||||
"@typescript-eslint/tsconfig-utils@8.48.0", "@typescript-eslint/tsconfig-utils@^8.48.0":
|
||||
version "8.48.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz"
|
||||
integrity sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==
|
||||
@@ -1219,7 +1486,7 @@
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/types@^8.48.0", "@typescript-eslint/types@8.48.0":
|
||||
"@typescript-eslint/types@8.48.0", "@typescript-eslint/types@^8.48.0":
|
||||
version "8.48.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz"
|
||||
integrity sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==
|
||||
@@ -1257,6 +1524,98 @@
|
||||
"@typescript-eslint/types" "8.48.0"
|
||||
eslint-visitor-keys "^4.2.1"
|
||||
|
||||
"@unrs/resolver-binding-android-arm-eabi@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz#9f5b04503088e6a354295e8ea8fe3cb99e43af81"
|
||||
integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==
|
||||
|
||||
"@unrs/resolver-binding-android-arm64@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz#7414885431bd7178b989aedc4d25cccb3865bc9f"
|
||||
integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==
|
||||
|
||||
"@unrs/resolver-binding-darwin-arm64@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz#b4a8556f42171fb9c9f7bac8235045e82aa0cbdf"
|
||||
integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==
|
||||
|
||||
"@unrs/resolver-binding-darwin-x64@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz#fd4d81257b13f4d1a083890a6a17c00de571f0dc"
|
||||
integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==
|
||||
|
||||
"@unrs/resolver-binding-freebsd-x64@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz#d2513084d0f37c407757e22f32bd924a78cfd99b"
|
||||
integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==
|
||||
|
||||
"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz#844d2605d057488d77fab09705f2866b86164e0a"
|
||||
integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==
|
||||
|
||||
"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz#204892995cefb6bd1d017d52d097193bc61ddad3"
|
||||
integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==
|
||||
|
||||
"@unrs/resolver-binding-linux-arm64-gnu@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz#023eb0c3aac46066a10be7a3f362e7b34f3bdf9d"
|
||||
integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==
|
||||
|
||||
"@unrs/resolver-binding-linux-arm64-musl@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz#9e6f9abb06424e3140a60ac996139786f5d99be0"
|
||||
integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==
|
||||
|
||||
"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz#b111417f17c9d1b02efbec8e08398f0c5527bb44"
|
||||
integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==
|
||||
|
||||
"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz#92ffbf02748af3e99873945c9a8a5ead01d508a9"
|
||||
integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==
|
||||
|
||||
"@unrs/resolver-binding-linux-riscv64-musl@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz#0bec6f1258fc390e6b305e9ff44256cb207de165"
|
||||
integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==
|
||||
|
||||
"@unrs/resolver-binding-linux-s390x-gnu@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz#577843a084c5952f5906770633ccfb89dac9bc94"
|
||||
integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==
|
||||
|
||||
"@unrs/resolver-binding-linux-x64-gnu@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935"
|
||||
integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==
|
||||
|
||||
"@unrs/resolver-binding-linux-x64-musl@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz#bfb9af75f783f98f6a22c4244214efe4df1853d6"
|
||||
integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==
|
||||
|
||||
"@unrs/resolver-binding-wasm32-wasi@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz#752c359dd875684b27429500d88226d7cc72f71d"
|
||||
integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==
|
||||
dependencies:
|
||||
"@napi-rs/wasm-runtime" "^0.2.11"
|
||||
|
||||
"@unrs/resolver-binding-win32-arm64-msvc@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz#ce5735e600e4c2fbb409cd051b3b7da4a399af35"
|
||||
integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==
|
||||
|
||||
"@unrs/resolver-binding-win32-ia32-msvc@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz#72fc57bc7c64ec5c3de0d64ee0d1810317bc60a6"
|
||||
integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==
|
||||
|
||||
"@unrs/resolver-binding-win32-x64-msvc@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz"
|
||||
@@ -1267,7 +1626,7 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.15.0:
|
||||
acorn@^8.15.0:
|
||||
version "8.15.0"
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz"
|
||||
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
|
||||
@@ -1456,7 +1815,7 @@ braces@^3.0.3:
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.24.0, "browserslist@>= 4.21.0":
|
||||
browserslist@^4.24.0:
|
||||
version "4.28.0"
|
||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz"
|
||||
integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==
|
||||
@@ -1574,7 +1933,7 @@ csstype@^3.0.2, csstype@^3.2.2:
|
||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz"
|
||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||
|
||||
d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3":
|
||||
"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz"
|
||||
integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
|
||||
@@ -1596,7 +1955,7 @@ d3-ease@^3.0.1:
|
||||
resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz"
|
||||
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
|
||||
|
||||
d3-interpolate@^3.0.1, "d3-interpolate@1.2.0 - 3":
|
||||
"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz"
|
||||
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
|
||||
@@ -1633,7 +1992,7 @@ d3-shape@^3.1.0:
|
||||
dependencies:
|
||||
d3-time "1 - 3"
|
||||
|
||||
d3-time@^3.0.0, "d3-time@1 - 3", "d3-time@2.1.1 - 3":
|
||||
"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz"
|
||||
integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
|
||||
@@ -1978,7 +2337,7 @@ eslint-module-utils@^2.12.1:
|
||||
dependencies:
|
||||
debug "^3.2.7"
|
||||
|
||||
eslint-plugin-import@*, eslint-plugin-import@^2.32.0:
|
||||
eslint-plugin-import@^2.32.0:
|
||||
version "2.32.0"
|
||||
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz"
|
||||
integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==
|
||||
@@ -2077,7 +2436,7 @@ eslint-visitor-keys@^4.2.1:
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz"
|
||||
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
|
||||
|
||||
eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9, eslint@>=9.0.0:
|
||||
eslint@^9:
|
||||
version "9.39.1"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz"
|
||||
integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==
|
||||
@@ -2340,16 +2699,16 @@ glob-parent@^6.0.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
globals@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||
|
||||
globals@16.4.0:
|
||||
version "16.4.0"
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz"
|
||||
integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==
|
||||
|
||||
globals@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz"
|
||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||
|
||||
globalthis@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz"
|
||||
@@ -2682,7 +3041,7 @@ iterator.prototype@^1.1.4:
|
||||
has-symbols "^1.1.0"
|
||||
set-function-name "^2.0.2"
|
||||
|
||||
jiti@*, jiti@^2.6.1:
|
||||
jiti@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz"
|
||||
integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==
|
||||
@@ -2768,6 +3127,56 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lightningcss-android-arm64@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307"
|
||||
integrity sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==
|
||||
|
||||
lightningcss-darwin-arm64@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz#a5fa946d27c029e48c7ff929e6e724a7de46eb2c"
|
||||
integrity sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==
|
||||
|
||||
lightningcss-darwin-x64@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz#5ce87e9cd7c4f2dcc1b713f5e8ee185c88d9b7cd"
|
||||
integrity sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==
|
||||
|
||||
lightningcss-freebsd-x64@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz#6ae1d5e773c97961df5cff57b851807ef33692a5"
|
||||
integrity sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz#62c489610c0424151a6121fa99d77731536cdaeb"
|
||||
integrity sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz#2a3661b56fe95a0cafae90be026fe0590d089298"
|
||||
integrity sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz#d7ddd6b26959245e026bc1ad9eb6aa983aa90e6b"
|
||||
integrity sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz#5a89814c8e63213a5965c3d166dff83c36152b1a"
|
||||
integrity sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz#808c2e91ce0bf5d0af0e867c6152e5378c049728"
|
||||
integrity sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz#ab4a8a8a2e6a82a4531e8bbb6bf0ff161ee6625a"
|
||||
integrity sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.2:
|
||||
version "1.30.2"
|
||||
resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz"
|
||||
@@ -3076,7 +3485,7 @@ picomatch@^2.3.1:
|
||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
"picomatch@^3 || ^4", picomatch@^4.0.3:
|
||||
picomatch@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz"
|
||||
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||
@@ -3086,15 +3495,6 @@ possible-typed-array-names@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz"
|
||||
integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==
|
||||
|
||||
postcss@^8.4.41:
|
||||
version "8.5.6"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz"
|
||||
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
|
||||
dependencies:
|
||||
nanoid "^3.3.11"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
postcss@8.4.31:
|
||||
version "8.4.31"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz"
|
||||
@@ -3104,6 +3504,15 @@ postcss@8.4.31:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
postcss@^8.4.41:
|
||||
version "8.5.6"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz"
|
||||
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
|
||||
dependencies:
|
||||
nanoid "^3.3.11"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
|
||||
@@ -3137,14 +3546,14 @@ react-day-picker@^9.11.3:
|
||||
date-fns "^4.1.0"
|
||||
date-fns-jalali "^4.1.0-0"
|
||||
|
||||
"react-dom@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom@^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^18 || ^19 || ^19.0.0-rc", "react-dom@^18.0.0 || ^19.0.0", "react-dom@^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react-dom@>=16.6.0, react-dom@>=16.8.0, react-dom@19.2.0:
|
||||
react-dom@19.2.0:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz"
|
||||
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
|
||||
dependencies:
|
||||
scheduler "^0.27.0"
|
||||
|
||||
react-hook-form@^7.55.0, react-hook-form@^7.67.0:
|
||||
react-hook-form@^7.67.0:
|
||||
version "7.67.0"
|
||||
resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.67.0.tgz"
|
||||
integrity sha512-E55EOwKJHHIT/I6J9DmQbCWToAYSw9nN5R57MZw9rMtjh+YQreMDxRLfdjfxQbiJ3/qbg3Z02wGzBX4M+5fMtQ==
|
||||
@@ -3210,7 +3619,7 @@ react-transition-group@^4.4.5:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
"react@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^18 || ^19 || ^19.0.0-rc", "react@^18.0.0 || ^19.0.0", "react@^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react@^19.2.0, "react@>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0", react@>=16, react@>=16.6.0, react@>=16.8.0, react@19.2.0:
|
||||
react@19.2.0:
|
||||
version "19.2.0"
|
||||
resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz"
|
||||
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==
|
||||
@@ -3340,17 +3749,7 @@ semver@^6.3.1:
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.6.0:
|
||||
version "7.7.3"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz"
|
||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||
|
||||
semver@^7.7.1:
|
||||
version "7.7.3"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz"
|
||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||
|
||||
semver@^7.7.3:
|
||||
semver@^7.6.0, semver@^7.7.1, semver@^7.7.3:
|
||||
version "7.7.3"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz"
|
||||
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
|
||||
@@ -3597,7 +3996,7 @@ tailwind-merge@^3.4.0:
|
||||
resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz"
|
||||
integrity sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==
|
||||
|
||||
tailwindcss@^4, tailwindcss@4.1.17:
|
||||
tailwindcss@4.1.17, tailwindcss@^4:
|
||||
version "4.1.17"
|
||||
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz"
|
||||
integrity sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==
|
||||
@@ -3714,7 +4113,7 @@ typescript-eslint@^8.46.0:
|
||||
"@typescript-eslint/typescript-estree" "8.48.0"
|
||||
"@typescript-eslint/utils" "8.48.0"
|
||||
|
||||
typescript@^5, typescript@>=3.3.1, typescript@>=4.8.4, "typescript@>=4.8.4 <6.0.0":
|
||||
typescript@^5:
|
||||
version "5.9.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz"
|
||||
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
|
||||
|
||||
Reference in New Issue
Block a user