From 9eef3bd68129b5c79095a699c62bcfd7b47bf343 Mon Sep 17 00:00:00 2001 From: roy <62500486+aazf@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:35:37 +0000 Subject: [PATCH 1/5] feat: simplify localized content routes --- apps/api/src/app.ts | 32 +++ apps/frontend/src/components/Footer.tsx | 8 +- apps/frontend/src/components/Header.tsx | 5 +- .../src/components/LocaleSwitcher.tsx | 7 +- .../src/components/MarkdownArticlePage.astro | 39 +++ .../src/components/ToolDetailPage.astro | 250 +++++++++++++++++ .../ToolDirectoryPage.astro} | 57 ++-- apps/frontend/src/components/ToolSearch.tsx | 14 +- apps/frontend/src/i18n/en.ts | 2 +- apps/frontend/src/i18n/types.ts | 2 +- apps/frontend/src/i18n/zh.ts | 2 +- apps/frontend/src/layouts/Base.astro | 14 +- apps/frontend/src/lib/content.ts | 14 +- apps/frontend/src/lib/routes.ts | 68 +++++ apps/frontend/src/pages/404.astro | 4 +- apps/frontend/src/pages/[...slug].astro | 29 ++ .../src/pages/[locale]/[...slug].astro | 256 +----------------- .../pages/[locale]/category/[category].astro | 84 +----- apps/frontend/src/pages/[locale]/index.astro | 71 +---- .../frontend/src/pages/[locale]/privacy.astro | 84 ------ apps/frontend/src/pages/[locale]/submit.astro | 138 ---------- .../src/pages/[locale]/sys/privacy.astro | 39 +++ .../src/pages/[locale]/sys/submit.astro | 67 +++++ .../src/pages/[locale]/sys/terms.astro | 49 ++++ .../src/pages/[locale]/tags/[tag].astro | 21 ++ .../src/pages/[locale]/template.astro | 210 +------------- apps/frontend/src/pages/[locale]/terms.astro | 104 ------- .../src/pages/category/[category].astro | 21 ++ apps/frontend/src/pages/index.astro | 69 +---- apps/frontend/src/pages/sys/privacy.astro | 35 +++ apps/frontend/src/pages/sys/submit.astro | 64 +++++ apps/frontend/src/pages/sys/terms.astro | 45 +++ apps/frontend/src/pages/tags/[tag].astro | 21 ++ apps/frontend/src/pages/template.astro | 173 ++++++++++++ content/agent-browser/meta.json | 2 +- content/zzci-chrome/meta.json | 2 +- docs/changelog.md | 6 + docs/plan/PLAN-004.md | 145 ++++++++++ docs/plan/index.md | 3 +- docs/task/FE-002.md | 28 ++ docs/task/index.md | 3 +- 41 files changed, 1243 insertions(+), 1044 deletions(-) create mode 100644 apps/frontend/src/components/MarkdownArticlePage.astro create mode 100644 apps/frontend/src/components/ToolDetailPage.astro rename apps/frontend/src/{pages/[locale]/tag/[tag].astro => components/ToolDirectoryPage.astro} (72%) create mode 100644 apps/frontend/src/lib/routes.ts create mode 100644 apps/frontend/src/pages/[...slug].astro delete mode 100644 apps/frontend/src/pages/[locale]/privacy.astro delete mode 100644 apps/frontend/src/pages/[locale]/submit.astro create mode 100644 apps/frontend/src/pages/[locale]/sys/privacy.astro create mode 100644 apps/frontend/src/pages/[locale]/sys/submit.astro create mode 100644 apps/frontend/src/pages/[locale]/sys/terms.astro create mode 100644 apps/frontend/src/pages/[locale]/tags/[tag].astro delete mode 100644 apps/frontend/src/pages/[locale]/terms.astro create mode 100644 apps/frontend/src/pages/category/[category].astro create mode 100644 apps/frontend/src/pages/sys/privacy.astro create mode 100644 apps/frontend/src/pages/sys/submit.astro create mode 100644 apps/frontend/src/pages/sys/terms.astro create mode 100644 apps/frontend/src/pages/tags/[tag].astro create mode 100644 apps/frontend/src/pages/template.astro create mode 100644 docs/plan/PLAN-004.md create mode 100644 docs/task/FE-002.md diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index a2f427a..3efba6f 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -20,6 +20,31 @@ const app = new Hono() app.use('*', logger()) app.use('*', cors()) +function buildRedirectPath(pathname: string): string | null { + const locale = pathname === '/zh' || pathname.startsWith('/zh/') ? 'zh' : 'en' + let path = pathname + + if (path === '/en' || path.startsWith('/en/')) { + path = path.slice(3) || '/' + } else if (path === '/zh' || path.startsWith('/zh/')) { + path = path.slice(3) || '/' + } + + const prefix = locale === 'zh' ? '/zh' : '' + + if (path === '/privacy') return `${prefix}/sys/privacy` + if (path === '/terms') return `${prefix}/sys/terms` + if (path === '/submit') return `${prefix}/sys/submit` + if (path === '/tag') return `${prefix}/tags` + if (path.startsWith('/tag/')) return `${prefix}/tags/${path.slice('/tag/'.length)}` + + if (pathname === '/en' || pathname.startsWith('/en/')) { + return path + } + + return null +} + app.get('/api/health', (c) => { return c.json({ status: 'ok', timestamp: Date.now() }) }) @@ -29,6 +54,13 @@ app.notFound((c) => { return c.json({ message: 'Not Found' }, 404) } + const redirectPath = buildRedirectPath(c.req.path) + if (redirectPath) { + const url = new URL(c.req.url) + url.pathname = redirectPath + return c.redirect(url.toString(), 308) + } + if (c.env.ASSETS) { return c.env.ASSETS.fetch(c.req.raw) } diff --git a/apps/frontend/src/components/Footer.tsx b/apps/frontend/src/components/Footer.tsx index b096ba7..ac380b3 100644 --- a/apps/frontend/src/components/Footer.tsx +++ b/apps/frontend/src/components/Footer.tsx @@ -1,3 +1,5 @@ +import { localizePath } from '@/lib/routes' + const translations = { en: { privacy: 'Privacy Policy', @@ -21,9 +23,9 @@ export function Footer({ locale }: { locale: 'en' | 'zh' }) {
- {t.privacy} - {t.terms} - {t.submit} + {t.privacy} + {t.terms} + {t.submit}

{t.copyright}

diff --git a/apps/frontend/src/components/Header.tsx b/apps/frontend/src/components/Header.tsx index 848c551..1e24b22 100644 --- a/apps/frontend/src/components/Header.tsx +++ b/apps/frontend/src/components/Header.tsx @@ -1,6 +1,7 @@ import { Github } from 'lucide-react' import { LocaleSwitcher } from '@/components/LocaleSwitcher' import { ThemeToggle } from '@/components/ThemeToggle' +import { localizePath } from '@/lib/routes' const translations = { en: { submit: 'Submit' }, @@ -13,7 +14,7 @@ export function Header({ locale }: { locale: 'en' | 'zh' }) { return (
- + BK @@ -30,7 +31,7 @@ export function Header({ locale }: { locale: 'en' | 'zh' }) { {t.submit} diff --git a/apps/frontend/src/components/LocaleSwitcher.tsx b/apps/frontend/src/components/LocaleSwitcher.tsx index c06a8d6..399d2e8 100644 --- a/apps/frontend/src/components/LocaleSwitcher.tsx +++ b/apps/frontend/src/components/LocaleSwitcher.tsx @@ -6,6 +6,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' +import { switchLocalePath } from '@/lib/routes' type Locale = 'en' | 'zh' @@ -22,10 +23,8 @@ export function LocaleSwitcher({ locale: initial }: { locale: Locale }) { const setLocale = useCallback((next: Locale) => { setLocaleState(next) localStorage.setItem(STORAGE_KEY, next) - // Navigate to the same path under the new locale prefix - const path = window.location.pathname - const newPath = path.replace(/^\/(en|zh)/, `/${next}`) - window.location.href = newPath === path ? `/${next}/` : newPath + const newPath = switchLocalePath(window.location.pathname, next) + window.location.href = newPath }, []) return ( diff --git a/apps/frontend/src/components/MarkdownArticlePage.astro b/apps/frontend/src/components/MarkdownArticlePage.astro new file mode 100644 index 0000000..ea61d96 --- /dev/null +++ b/apps/frontend/src/components/MarkdownArticlePage.astro @@ -0,0 +1,39 @@ +--- +import type { Locale } from '@/i18n' +import Base from '@/layouts/Base.astro' +import { renderMarkdown } from '@/lib/content' + +interface Props { + locale: Locale + title: string + description?: string + content: string +} + +const { locale, title, description, content } = Astro.props +const htmlContent = await renderMarkdown(content, locale) +--- + + +
+
+
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} +
+ +
+
+ +
+
+
+
+
+ diff --git a/apps/frontend/src/components/ToolDetailPage.astro b/apps/frontend/src/components/ToolDetailPage.astro new file mode 100644 index 0000000..f7983d3 --- /dev/null +++ b/apps/frontend/src/components/ToolDetailPage.astro @@ -0,0 +1,250 @@ +--- +import type { Locale } from '@/i18n' +import { getTranslations } from '@/i18n' +import Base from '@/layouts/Base.astro' +import { + getToolBadge, + getToolDoc, + getToolDocs, + getToolMeta, + getToolTags, + getToolsJson, + resolveToolLogo, + txt, +} from '@/lib/content' +import { localizePath } from '@/lib/routes' + +interface Props { + locale: Locale + slug: string +} + +type RelatedToolCard = { + slug: string + href: string + title: string + description: string + badge: string + logo: ReturnType +} + +const { locale, slug } = Astro.props +const parts = slug.split('/') +const toolSlug = parts[0] +const docSlug = parts[1] || 'index' + +const t = getTranslations(locale) +const toolsData = getToolsJson() +const toolEntry = toolsData.list.find((item) => item.slug === toolSlug) +const categoryLabel = toolEntry ? getToolBadge(toolsData, toolEntry.category, locale) : '' +const toolTags = toolEntry ? getToolTags(toolsData, toolEntry.tags ?? [], locale) : [] + +const meta = getToolMeta(toolSlug) +const { data: frontmatter, html: htmlContent } = await getToolDoc(toolSlug, docSlug, locale) + +const allDocs = await getToolDocs(toolSlug, locale) +const sidebarDocs = allDocs + .map((doc) => ({ + slug: doc.slug, + href: localizePath( + locale, + doc.slug === 'index' ? `/${toolSlug}` : `/${toolSlug}/${doc.slug}`, + ), + title: doc.frontmatter.title ? txt(doc.frontmatter.title, locale) : doc.slug, + order: doc.frontmatter.order ?? 99, + })) + .sort((a, b) => a.order - b.order) + +const pageTitle = frontmatter.title ? txt(frontmatter.title, locale) : docSlug + +function isSafeUrl(url: string): boolean { + try { + const parsed = new URL(url) + return parsed.protocol === 'https:' || parsed.protocol === 'http:' + } catch { + return false + } +} + +const customLinks = (meta.links ?? []).map((link) => ({ + label: txt(link.label, locale), + href: isSafeUrl(link.url) ? link.url : '#', +})) + +const authorUrl = meta.addedBy?.url && isSafeUrl(meta.addedBy.url) ? meta.addedBy.url : undefined +const websiteUrl = meta.website && isSafeUrl(meta.website) ? meta.website : undefined +const githubUrl = meta.github && isSafeUrl(meta.github) ? meta.github : undefined +const linkedTools: RelatedToolCard[] = (meta.link ?? []) + .map((relatedSlug) => { + const relatedEntry = toolsData.list.find((item) => item.slug === relatedSlug) + if (!relatedEntry) return null + + const relatedMeta = getToolMeta(relatedSlug) + return { + slug: relatedSlug, + href: localizePath(locale, `/${relatedSlug}`), + title: txt(relatedMeta.title, locale), + description: txt(relatedMeta.description, locale), + badge: getToolBadge(toolsData, relatedEntry.category, locale), + logo: resolveToolLogo(relatedSlug, relatedEntry.logo), + } + }) + .filter((item): item is RelatedToolCard => item !== null) +--- + + +
+
+
+

+ {txt(meta.title, locale)} +

+
+ {categoryLabel && toolEntry && ( + {categoryLabel} + )} + {toolTags.map((tag) => ( + {tag.label} + ))} + {meta.addedAt && ( + + + {meta.addedAt} + + )} + {meta.addedBy && ( + authorUrl ? ( + + + {meta.addedBy.name} + + ) : ( + + + {meta.addedBy.name} + + ) + )} +
+

+ {txt(meta.description, locale)} +

+ {(websiteUrl || githubUrl || customLinks.length > 0) && ( +
+ {websiteUrl && ( + + + {locale === 'zh' ? '官网' : 'Website'} + + )} + {githubUrl && ( + + + GitHub + + )} + {customLinks.map((link) => ( + + + {link.label} + + ))} +
+ )} +
+ + {linkedTools.length > 0 && ( + + )} + +
+ + + +
+ +
+ {sidebarDocs.length > 1 && ( + + )} + +
1 && 'min-w-0 lg:pl-5']}> + {sidebarDocs.length > 1 && ( + + )} + +
+
+
+
+
+ + + diff --git a/apps/frontend/src/pages/[locale]/tag/[tag].astro b/apps/frontend/src/components/ToolDirectoryPage.astro similarity index 72% rename from apps/frontend/src/pages/[locale]/tag/[tag].astro rename to apps/frontend/src/components/ToolDirectoryPage.astro index 17a7e60..ad7ee24 100644 --- a/apps/frontend/src/pages/[locale]/tag/[tag].astro +++ b/apps/frontend/src/components/ToolDirectoryPage.astro @@ -1,38 +1,45 @@ --- -import Base from '@/layouts/Base.astro' import { ToolSearch } from '@/components/ToolSearch' -import { getTranslations } from '@/i18n' import type { Locale } from '@/i18n' -import { getToolsJson, getToolMeta, getToolBadge, getToolTags, resolveToolLogo, txt } from '@/lib/content' - -export function getStaticPaths() { - const locales = ['en', 'zh'] as const - const toolsData = getToolsJson() - const paths: { params: { locale: string; tag: string } }[] = [] - - for (const loc of locales) { - for (const tag of toolsData.tags) { - paths.push({ params: { locale: loc, tag: tag.key } }) - } - } +import { getTranslations } from '@/i18n' +import Base from '@/layouts/Base.astro' +import { + getToolBadge, + getToolMeta, + getToolTags, + getToolsJson, + resolveToolLogo, + txt, +} from '@/lib/content' - return paths +interface Props { + locale: Locale + metaTitle?: string + metaDescription?: string + initialCategory?: string + initialTag?: string + detectLocale?: boolean } -const locale = Astro.params.locale as Locale -const tagKey = Astro.params.tag! +const { + locale, + metaTitle, + metaDescription, + initialCategory, + initialTag, + detectLocale = false, +} = Astro.props + const t = getTranslations(locale) const toolsData = getToolsJson() -const tagObj = toolsData.tags.find((t) => t.key === tagKey) -const tagLabel = tagObj ? txt(tagObj, locale) : tagKey - const toolItems = toolsData.list .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) .map((item) => { const meta = getToolMeta(item.slug) const logo = resolveToolLogo(item.slug, item.logo) const title = txt(meta.title, locale) + return { slug: item.slug, category: item.category, @@ -57,7 +64,12 @@ const allTags = toolsData.tags.map((tag) => ({ })) --- - +

@@ -74,7 +86,8 @@ const allTags = toolsData.tags.map((tag) => ({ tools={toolItems} categories={categories} allTags={allTags} - initialTag={tagKey} + initialCategory={initialCategory} + initialTag={initialTag} translations={{ searchPlaceholder: t.hero.searchPlaceholder, all: t.categories.all, diff --git a/apps/frontend/src/components/ToolSearch.tsx b/apps/frontend/src/components/ToolSearch.tsx index 9725930..222394c 100644 --- a/apps/frontend/src/components/ToolSearch.tsx +++ b/apps/frontend/src/components/ToolSearch.tsx @@ -16,6 +16,8 @@ import { useEffect, useMemo, useState } from 'react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' +import type { Locale } from '@/i18n' +import { localizePath } from '@/lib/routes' import { cn } from '@/lib/utils' type ToolTag = { @@ -52,7 +54,7 @@ const iconMap: Record = { const PAGE_SIZE = 12 interface Props { - locale: string + locale: Locale tools: ToolItem[] categories: Category[] allTags: ToolTag[] @@ -133,7 +135,7 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory setActiveCategory(key) setActiveTag('') setPage(1) - const url = key === 'all' ? `/${locale}/` : `/${locale}/category/${key}` + const url = key === 'all' ? localizePath(locale, '/') : localizePath(locale, `/category/${key}`) window.history.replaceState(null, '', url) } @@ -142,9 +144,11 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory setActiveTag(newTag) setPage(1) if (newTag) { - window.history.replaceState(null, '', `/${locale}/tag/${newTag}`) + window.history.replaceState(null, '', localizePath(locale, `/tags/${newTag}`)) } else { - const url = activeCategory === 'all' ? `/${locale}/` : `/${locale}/category/${activeCategory}` + const url = activeCategory === 'all' + ? localizePath(locale, '/') + : localizePath(locale, `/category/${activeCategory}`) window.history.replaceState(null, '', url) } } @@ -225,7 +229,7 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory return (
- + {noindex && } {fullTitle} {description && } diff --git a/apps/frontend/src/lib/content.ts b/apps/frontend/src/lib/content.ts index f89e8d4..09528d2 100644 --- a/apps/frontend/src/lib/content.ts +++ b/apps/frontend/src/lib/content.ts @@ -4,6 +4,7 @@ import matter from 'gray-matter' import { Marked } from 'marked' import { createHighlighter } from 'shiki' import type { Locale } from '@/i18n' +import { localizePath } from '@/lib/routes' type I18nText = { en: string; zh: string } @@ -21,7 +22,7 @@ export type ToolMeta = { addedAt?: string addedBy?: { name: string; url?: string } links?: ToolLink[] - relatedTools?: string[] + link?: string[] } export type ToolListItem = { @@ -131,11 +132,6 @@ function replaceLinkPlaceholders(content: string): string { .join('') } -function buildLocalizedPath(path: string, locale: Locale): string { - if (!path || path === '/') return `/${locale}` - return `/${locale}${path.startsWith('/') ? path : `/${path}`}` -} - function localizeInternalHref(href: string, locale: Locale): string { if (!href.startsWith('/') || href.startsWith('//')) return href if (href.startsWith('/api/') || href.startsWith('/assets/') || href.startsWith('/_')) return href @@ -150,11 +146,11 @@ function localizeInternalHref(href: string, locale: Locale): string { if (segments[0] === 'en' || segments[0] === 'zh') { const rest = segments.slice(1) - const localized = buildLocalizedPath(rest.length > 0 ? `/${rest.join('/')}` : '/', locale) + const localized = localizePath(locale, rest.length > 0 ? `/${rest.join('/')}` : '/') return `${localized}${suffix}` } - return `${buildLocalizedPath(path, locale)}${suffix}` + return `${localizePath(locale, path)}${suffix}` } function rewriteInternalLinks(html: string, locale: Locale): string { @@ -246,7 +242,7 @@ export function getToolMeta(slug: string): ToolMeta { return { title: { en: slug, zh: slug }, description: { en: '', zh: '' }, - relatedTools: [], + link: [], } } } diff --git a/apps/frontend/src/lib/routes.ts b/apps/frontend/src/lib/routes.ts new file mode 100644 index 0000000..dec80c7 --- /dev/null +++ b/apps/frontend/src/lib/routes.ts @@ -0,0 +1,68 @@ +import type { Locale } from '@/i18n' + +export const DEFAULT_LOCALE: Locale = 'en' + +export const RESERVED_ROOT_SEGMENTS = new Set([ + 'api', + 'zh', + 'sys', + 'tags', + 'category', + 'link', + 'template', +]) + +function normalizePath(path: string): string { + if (!path || path === '/') return '/' + + const normalized = `/${path}`.replace(/\/+/g, '/') + if (normalized.length > 1 && normalized.endsWith('/')) { + return normalized.slice(0, -1) + } + + return normalized +} + +export function localizePath(locale: Locale, path: string): string { + const normalized = normalizePath(path) + if (locale === DEFAULT_LOCALE) return normalized + return normalized === '/' ? `/${locale}` : `/${locale}${normalized}` +} + +export function parseLocalizedPath(pathname: string): { + locale: Locale + path: string + hadLocalePrefix: boolean +} { + const normalized = normalizePath(pathname) + const segments = normalized.split('/').filter(Boolean) + const first = segments[0] + + if (first === 'en' || first === 'zh') { + const rest = segments.slice(1) + return { + locale: first, + path: rest.length > 0 ? `/${rest.join('/')}` : '/', + hadLocalePrefix: true, + } + } + + return { + locale: DEFAULT_LOCALE, + path: normalized, + hadLocalePrefix: false, + } +} + +export function getAlternateLocale(locale: Locale): Locale { + return locale === 'en' ? 'zh' : 'en' +} + +export function switchLocalePath(pathname: string, locale: Locale): string { + const parsed = parseLocalizedPath(pathname) + return localizePath(locale, parsed.path) +} + +export function isReservedRootSlug(slug: string): boolean { + return RESERVED_ROOT_SEGMENTS.has(slug) || slug.includes('.') +} diff --git a/apps/frontend/src/pages/404.astro b/apps/frontend/src/pages/404.astro index 00ead1e..05808fe 100644 --- a/apps/frontend/src/pages/404.astro +++ b/apps/frontend/src/pages/404.astro @@ -11,14 +11,14 @@ import Base from '@/layouts/Base.astro'

Back to Home diff --git a/apps/frontend/src/pages/[...slug].astro b/apps/frontend/src/pages/[...slug].astro new file mode 100644 index 0000000..22d1f47 --- /dev/null +++ b/apps/frontend/src/pages/[...slug].astro @@ -0,0 +1,29 @@ +--- +import ToolDetailPage from '@/components/ToolDetailPage.astro' +import { getToolDocSlugs, getToolsJson } from '@/lib/content' +import { isReservedRootSlug } from '@/lib/routes' + +export function getStaticPaths() { + const toolsData = getToolsJson() + const paths: { params: { slug: string } }[] = [] + + for (const item of toolsData.list) { + if (isReservedRootSlug(item.slug)) continue + + paths.push({ params: { slug: item.slug } }) + + const docSlugs = getToolDocSlugs(item.slug) + for (const docSlug of docSlugs) { + if (docSlug !== 'index') { + paths.push({ params: { slug: `${item.slug}/${docSlug}` } }) + } + } + } + + return paths +} + +const slug = Astro.params.slug! +--- + + diff --git a/apps/frontend/src/pages/[locale]/[...slug].astro b/apps/frontend/src/pages/[locale]/[...slug].astro index 5c5ec36..d212264 100644 --- a/apps/frontend/src/pages/[locale]/[...slug].astro +++ b/apps/frontend/src/pages/[locale]/[...slug].astro @@ -1,33 +1,18 @@ --- -import Base from '@/layouts/Base.astro' -import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { - getToolsJson, - getToolMeta, - getToolDocSlugs, - getToolDocs, - getToolDoc, - getToolBadge, - getToolTags, - resolveToolLogo, - txt, -} from '@/lib/content' +import ToolDetailPage from '@/components/ToolDetailPage.astro' +import { getToolDocSlugs, getToolsJson } from '@/lib/content' export function getStaticPaths() { - const locales = ['en', 'zh'] as const const toolsData = getToolsJson() const paths: { params: { locale: string; slug: string } }[] = [] - for (const loc of locales) { - for (const item of toolsData.list) { - paths.push({ params: { locale: loc, slug: item.slug } }) + for (const item of toolsData.list) { + paths.push({ params: { locale: 'zh', slug: item.slug } }) - const docSlugs = getToolDocSlugs(item.slug) - for (const docSlug of docSlugs) { - if (docSlug !== 'index') { - paths.push({ params: { locale: loc, slug: `${item.slug}/${docSlug}` } }) - } + const docSlugs = getToolDocSlugs(item.slug) + for (const docSlug of docSlugs) { + if (docSlug !== 'index') { + paths.push({ params: { locale: 'zh', slug: `${item.slug}/${docSlug}` } }) } } } @@ -35,230 +20,7 @@ export function getStaticPaths() { return paths } -type RelatedToolCard = { - slug: string - href: string - title: string - description: string - badge: string - logo: ReturnType -} - -const locale = Astro.params.locale as Locale const slug = Astro.params.slug! -const parts = slug.split('/') -const toolSlug = parts[0] -const docSlug = parts[1] || 'index' - -const t = getTranslations(locale) -const toolsData = getToolsJson() -const toolEntry = toolsData.list.find((item) => item.slug === toolSlug) -const categoryLabel = toolEntry ? getToolBadge(toolsData, toolEntry.category, locale) : '' -const toolTags = toolEntry ? getToolTags(toolsData, toolEntry.tags ?? [], locale) : [] - -const meta = getToolMeta(toolSlug) -const { data: frontmatter, html: htmlContent } = await getToolDoc(toolSlug, docSlug, locale) - -const allDocs = await getToolDocs(toolSlug, locale) -const sidebarDocs = allDocs - .map((doc) => ({ - slug: doc.slug, - href: doc.slug === 'index' ? `/${locale}/${toolSlug}` : `/${locale}/${toolSlug}/${doc.slug}`, - title: doc.frontmatter.title ? txt(doc.frontmatter.title, locale) : doc.slug, - order: doc.frontmatter.order ?? 99, - })) - .sort((a, b) => a.order - b.order) - -const pageTitle = frontmatter.title ? txt(frontmatter.title, locale) : docSlug - -function isSafeUrl(url: string): boolean { - try { - const parsed = new URL(url) - return parsed.protocol === 'https:' || parsed.protocol === 'http:' - } catch { - return false - } -} - -const customLinks = (meta.links ?? []).map((link) => ({ - label: txt(link.label, locale), - href: isSafeUrl(link.url) ? link.url : '#', -})) - -const authorUrl = meta.addedBy?.url && isSafeUrl(meta.addedBy.url) ? meta.addedBy.url : undefined -const websiteUrl = meta.website && isSafeUrl(meta.website) ? meta.website : undefined -const githubUrl = meta.github && isSafeUrl(meta.github) ? meta.github : undefined -const relatedTools: RelatedToolCard[] = (meta.relatedTools ?? []) - .map((relatedSlug) => { - const relatedEntry = toolsData.list.find((item) => item.slug === relatedSlug) - if (!relatedEntry) return null - - const relatedMeta = getToolMeta(relatedSlug) - return { - slug: relatedSlug, - href: `/${locale}/${relatedSlug}`, - title: txt(relatedMeta.title, locale), - description: txt(relatedMeta.description, locale), - badge: getToolBadge(toolsData, relatedEntry.category, locale), - logo: resolveToolLogo(relatedSlug, relatedEntry.logo), - } - }) - .filter((item): item is RelatedToolCard => item !== null) --- - -
-
-
-

- {txt(meta.title, locale)} -

-
- {categoryLabel && toolEntry && ( - {categoryLabel} - )} - {toolTags.map((tag) => ( - {tag.label} - ))} - {meta.addedAt && ( - - - {meta.addedAt} - - )} - {meta.addedBy && ( - authorUrl ? ( - - - {meta.addedBy.name} - - ) : ( - - - {meta.addedBy.name} - - ) - )} -
-

- {txt(meta.description, locale)} -

- {(websiteUrl || githubUrl || customLinks.length > 0) && ( -
- {websiteUrl && ( - - - {locale === 'zh' ? '官网' : 'Website'} - - )} - {githubUrl && ( - - - GitHub - - )} - {customLinks.map((link) => ( - - - {link.label} - - ))} -
- )} -
- - {relatedTools.length > 0 && ( - - )} - -
- - - -
- -
- {sidebarDocs.length > 1 && ( - - )} - -
1 && 'min-w-0 lg:pl-5']}> - {sidebarDocs.length > 1 && ( - - )} - -
-
-
-
-
- - - + diff --git a/apps/frontend/src/pages/[locale]/category/[category].astro b/apps/frontend/src/pages/[locale]/category/[category].astro index e4daa6b..0bd4ee4 100644 --- a/apps/frontend/src/pages/[locale]/category/[category].astro +++ b/apps/frontend/src/pages/[locale]/category/[category].astro @@ -1,89 +1,21 @@ --- -import Base from '@/layouts/Base.astro' -import { ToolSearch } from '@/components/ToolSearch' +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { getToolsJson, getToolMeta, getToolBadge, getToolTags, resolveToolLogo, txt } from '@/lib/content' +import { getToolsJson, txt } from '@/lib/content' export function getStaticPaths() { - const locales = ['en', 'zh'] as const const toolsData = getToolsJson() - const paths: { params: { locale: string; category: string } }[] = [] - - for (const loc of locales) { - for (const cat of toolsData.categories) { - paths.push({ params: { locale: loc, category: cat.key } }) - } - } - - return paths + return toolsData.categories.map((category) => ({ + params: { locale: 'zh', category: category.key }, + })) } -const locale = Astro.params.locale as Locale +const locale = 'zh' as const const categoryKey = Astro.params.category! const t = getTranslations(locale) const toolsData = getToolsJson() - -const categoryObj = toolsData.categories.find((c) => c.key === categoryKey) +const categoryObj = toolsData.categories.find((item) => item.key === categoryKey) const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey - -const toolItems = toolsData.list - .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) - .map((item) => { - const meta = getToolMeta(item.slug) - const logo = resolveToolLogo(item.slug, item.logo) - const title = txt(meta.title, locale) - return { - slug: item.slug, - category: item.category, - badge: getToolBadge(toolsData, item.category, locale), - logo: logo.type === 'icon' ? item.logo! : logo.type === 'svg' ? 'custom' : '', - logoSvg: logo.type === 'svg' ? logo.content : undefined, - order: item.order, - title, - description: txt(meta.description, locale), - tags: getToolTags(toolsData, item.tags ?? [], locale), - } - }) - -const categories = toolsData.categories.map((cat) => ({ - key: cat.key, - label: txt(cat, locale), -})) - -const allTags = toolsData.tags.map((tag) => ({ - key: tag.key, - label: txt(tag, locale), -})) --- - -
-
-

- {t.hero.title} -

- -

- {t.hero.subtitle} -

- - -
-
- + diff --git a/apps/frontend/src/pages/[locale]/index.astro b/apps/frontend/src/pages/[locale]/index.astro index 1dfe3cb..b2e219c 100644 --- a/apps/frontend/src/pages/[locale]/index.astro +++ b/apps/frontend/src/pages/[locale]/index.astro @@ -1,74 +1,9 @@ --- -import Base from '@/layouts/Base.astro' -import { ToolSearch } from '@/components/ToolSearch' -import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { getToolsJson, getToolMeta, getToolBadge, getToolTags, resolveToolLogo, txt } from '@/lib/content' +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' export function getStaticPaths() { - return [{ params: { locale: 'en' } }, { params: { locale: 'zh' } }] + return [{ params: { locale: 'zh' } }] } - -const locale = Astro.params.locale as Locale -const t = getTranslations(locale) -const toolsData = getToolsJson() - -const toolItems = toolsData.list - .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) - .map((item) => { - const meta = getToolMeta(item.slug) - const logo = resolveToolLogo(item.slug, item.logo) - const title = txt(meta.title, locale) - return { - slug: item.slug, - category: item.category, - badge: getToolBadge(toolsData, item.category, locale), - logo: logo.type === 'icon' ? item.logo! : logo.type === 'svg' ? 'custom' : '', - logoSvg: logo.type === 'svg' ? logo.content : undefined, - order: item.order, - title, - description: txt(meta.description, locale), - tags: getToolTags(toolsData, item.tags ?? [], locale), - } - }) - -const categories = toolsData.categories.map((cat) => ({ - key: cat.key, - label: txt(cat, locale), -})) - -const allTags = toolsData.tags.map((tag) => ({ - key: tag.key, - label: txt(tag, locale), -})) --- - -
-
-

- {t.hero.title} -

- -

- {t.hero.subtitle} -

- - -
-
- + diff --git a/apps/frontend/src/pages/[locale]/privacy.astro b/apps/frontend/src/pages/[locale]/privacy.astro deleted file mode 100644 index c30909d..0000000 --- a/apps/frontend/src/pages/[locale]/privacy.astro +++ /dev/null @@ -1,84 +0,0 @@ ---- -import Base from '@/layouts/Base.astro' -import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { renderMarkdown } from '@/lib/content' - -export function getStaticPaths() { - return [{ params: { locale: 'en' } }, { params: { locale: 'zh' } }] -} - -const locale = Astro.params.locale as Locale -const t = getTranslations(locale) - -const content: Record = { - en: ` -## Information We Collect - -We do not collect personal information or use cookies. This website uses Cloudflare Web Analytics, a privacy-first analytics service that does not use cookies or track individual users. It only collects aggregated data such as page views, referrers, and browser/device types. - -## Third-Party Services - -**Cloudflare Web Analytics** — We use Cloudflare Web Analytics to understand site usage. It does not collect personal data, does not use cookies, and does not track users across sites. Learn more at [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/). - -**Cloudflare CDN** — This site is hosted on Cloudflare. Cloudflare may collect basic access logs (IP address, browser type, access time) as part of its standard CDN service. Please refer to [Cloudflare's Privacy Policy](https://www.cloudflare.com/privacypolicy/) for details. - -## External Links - -This site contains links to third-party websites. We are not responsible for the privacy practices of those sites. We encourage you to review the privacy policies of any site you visit. - -## Changes to This Policy - -We may update this Privacy Policy from time to time. Any changes will be posted on this page. - -## Contact - -If you have questions about this Privacy Policy, please open an issue on our [GitHub repository](https://github.com/bkhq/website). - -*Last updated: 2026-03-07* -`, - zh: ` -## 我们收集的信息 - -我们不收集个人信息,也不使用 Cookie。本网站使用 Cloudflare Web Analytics,这是一项隐私优先的分析服务,不使用 Cookie,也不追踪个人用户。它仅收集汇总数据,如页面浏览量、来源页面和浏览器/设备类型。 - -## 第三方服务 - -**Cloudflare Web Analytics** — 我们使用 Cloudflare Web Analytics 来了解网站使用情况。该服务不收集个人数据、不使用 Cookie、不跨站追踪用户。了解更多:[Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/)。 - -**Cloudflare CDN** — 本站托管于 Cloudflare。Cloudflare 可能会作为其标准 CDN 服务的一部分收集基本访问日志(IP 地址、浏览器类型、访问时间)。详情请参阅 [Cloudflare 隐私政策](https://www.cloudflare.com/privacypolicy/)。 - -## 外部链接 - -本站包含指向第三方网站的链接。我们不对这些网站的隐私做法负责。建议您查阅所访问网站的隐私政策。 - -## 政策变更 - -我们可能会不时更新本隐私政策。任何变更将在本页面上发布。 - -## 联系方式 - -如果您对本隐私政策有任何疑问,请在我们的 [GitHub 仓库](https://github.com/bkhq/website) 上提交 Issue。 - -*最后更新:2026-03-07* -`, -} - -const htmlContent = await renderMarkdown(content[locale], locale) ---- - - -
-
-
-

- {t.footer.privacy} -

-
- -
-
-
-
-
- diff --git a/apps/frontend/src/pages/[locale]/submit.astro b/apps/frontend/src/pages/[locale]/submit.astro deleted file mode 100644 index 7bcfc5b..0000000 --- a/apps/frontend/src/pages/[locale]/submit.astro +++ /dev/null @@ -1,138 +0,0 @@ ---- -import Base from '@/layouts/Base.astro' -import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { renderMarkdown } from '@/lib/content' -import { GithubPrButton } from '@/components/GithubPrButton' - -export function getStaticPaths() { - return [{ params: { locale: 'en' } }, { params: { locale: 'zh' } }] -} - -const locale = Astro.params.locale as Locale -const t = getTranslations(locale) - -const content: Record = { - en: String.raw` -## Quick Submit - -Open an Issue with the tool URL if you want AI to draft the content for you. - -[Submit via Issue →](https://github.com/bkhq/website/issues/new?template=quick-submit.yml&title=%5BSubmit%5D+Tool+Name) - -If you want to add a tool manually, start from the [Template Guide →](/template). - -## Manual Submit - -```bash -# 1. Fork the repo on GitHub -# 2. Clone your fork -# 3. Add content// -# 4. Register the slug in content/list.json -# 5. Commit, push, and open a Pull Request - -git clone https://github.com//website.git -cd website -mkdir -p content/my-tool -``` - -## Minimum Structure - -```text -content/ -├── list.json -└── my-tool/ - ├── meta.json - ├── en.index.md - ├── zh.index.md - ├── en.installation.md - ├── zh.installation.md - ├── en.configuration.md - ├── zh.configuration.md - ├── en.data.md - ├── zh.data.md - ├── en.faq.md - └── zh.faq.md -``` - -## Notes - -- Keep the slug URL-safe. Use `zzci-chrome`, not `zzci/chrome`. -- Put display names such as `zzci/chrome` in `meta.json`. -- Use placeholders like `[[agent-browser]]` or `[[bkd/data|Data & Storage]]` for internal links. They are localized automatically. -- Add `relatedTools` in `meta.json` if this tool should link to another entry. -`, - zh: String.raw` -## 快速投稿 - -如果你只想丢一个工具链接,让 AI 帮你起草内容,可以直接提 Issue。 - -[通过 Issue 提交 →](https://github.com/bkhq/website/issues/new?template=quick-submit.yml&title=%5B%E6%8F%90%E4%BA%A4%5D+%E5%B7%A5%E5%85%B7%E5%90%8D%E7%A7%B0) - -如果你想自己添加工具,先看 [模板说明 →](/template)。 - -## 手动提交 - -```bash -# 1. 在 GitHub 上 Fork 仓库 -# 2. 克隆你的 Fork -# 3. 添加 content// -# 4. 在 content/list.json 里注册 slug -# 5. 提交、推送并创建 Pull Request - -git clone https://github.com//website.git -cd website -mkdir -p content/my-tool -``` - -## 最小目录结构 - -```text -content/ -├── list.json -└── my-tool/ - ├── meta.json - ├── en.index.md - ├── zh.index.md - ├── en.installation.md - ├── zh.installation.md - ├── en.configuration.md - ├── zh.configuration.md - ├── en.data.md - ├── zh.data.md - ├── en.faq.md - └── zh.faq.md -``` - -## 说明 - -- slug 必须是 URL 安全格式,像 `zzci/chrome` 这样的展示名应该写进 `meta.json`,slug 则用 `zzci-chrome`。 -- 文档里的站内链接用 `[[agent-browser]]` 或 `[[bkd/data|数据说明]]` 这种占位符写法,渲染时会自动按语言切换。 -- 如果两个工具需要互相推荐,可以在 `meta.json` 里添加 `relatedTools`。 -`, -} - -const htmlContent = await renderMarkdown(content[locale], locale) ---- - - -
-
-
-

- {t.submit.title} -

-

- {t.submit.description} -

-
- -
-
- -
-
-
-
-
- diff --git a/apps/frontend/src/pages/[locale]/sys/privacy.astro b/apps/frontend/src/pages/[locale]/sys/privacy.astro new file mode 100644 index 0000000..12a10f8 --- /dev/null +++ b/apps/frontend/src/pages/[locale]/sys/privacy.astro @@ -0,0 +1,39 @@ +--- +import { getTranslations } from '@/i18n' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' + +export function getStaticPaths() { + return [{ params: { locale: 'zh' } }] +} + +const locale = 'zh' as const +const t = getTranslations(locale) + +const content = String.raw` +## 我们收集的信息 + +我们不收集个人信息,也不使用 Cookie。本网站使用 Cloudflare Web Analytics,这是一项隐私优先的分析服务,不使用 Cookie,也不追踪个人用户。它仅收集汇总数据,如页面浏览量、来源页面和浏览器/设备类型。 + +## 第三方服务 + +**Cloudflare Web Analytics** — 我们使用 Cloudflare Web Analytics 来了解网站使用情况。该服务不收集个人数据、不使用 Cookie、不跨站追踪用户。了解更多:[Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/)。 + +**Cloudflare CDN** — 本站托管于 Cloudflare。Cloudflare 可能会作为其标准 CDN 服务的一部分收集基本访问日志(IP 地址、浏览器类型、访问时间)。详情请参阅 [Cloudflare 隐私政策](https://www.cloudflare.com/privacypolicy/)。 + +## 外部链接 + +本站包含指向第三方网站的链接。我们不对这些网站的隐私做法负责。建议您查阅所访问网站的隐私政策。 + +## 政策变更 + +我们可能会不时更新本隐私政策。任何变更将在本页面上发布。 + +## 联系方式 + +如果您对本隐私政策有任何疑问,请在我们的 [GitHub 仓库](https://github.com/bkhq/website) 上提交 Issue。 + +*最后更新:2026-03-07* +` +--- + + diff --git a/apps/frontend/src/pages/[locale]/sys/submit.astro b/apps/frontend/src/pages/[locale]/sys/submit.astro new file mode 100644 index 0000000..326f6b4 --- /dev/null +++ b/apps/frontend/src/pages/[locale]/sys/submit.astro @@ -0,0 +1,67 @@ +--- +import { GithubPrButton } from '@/components/GithubPrButton' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' +import { getTranslations } from '@/i18n' + +export function getStaticPaths() { + return [{ params: { locale: 'zh' } }] +} + +const locale = 'zh' as const +const t = getTranslations(locale) + +const content = String.raw` +## 快速投稿 + +如果你只想丢一个工具链接,让 AI 帮你起草内容,可以直接提 Issue。 + +[通过 Issue 提交 →](https://github.com/bkhq/website/issues/new?template=quick-submit.yml&title=%5B%E6%8F%90%E4%BA%A4%5D+%E5%B7%A5%E5%85%B7%E5%90%8D%E7%A7%B0) + +如果你想自己添加工具,先看 [模板说明 →](/template)。 + +## 手动提交 + +\`\`\`bash +# 1. 在 GitHub 上 Fork 仓库 +# 2. 克隆你的 Fork +# 3. 添加 content// +# 4. 在 content/list.json 里注册 slug +# 5. 提交、推送并创建 Pull Request + +git clone https://github.com//website.git +cd website +mkdir -p content/my-tool +\`\`\` + +## 最小目录结构 + +\`\`\`text +content/ +├── list.json +└── my-tool/ + ├── meta.json + ├── en.index.md + ├── zh.index.md + ├── en.installation.md + ├── zh.installation.md + ├── en.configuration.md + ├── zh.configuration.md + ├── en.data.md + ├── zh.data.md + ├── en.faq.md + └── zh.faq.md +\`\`\` + +## 说明 + +- slug 必须是 URL 安全格式,像 \`zzci/chrome\` 这样的展示名应该写进 \`meta.json\`,slug 则用 \`zzci-chrome\`。 +- 文档里的站内链接用 \`[[agent-browser]]\` 或 \`[[bkd/data|数据说明]]\` 这种占位符写法,渲染时会自动按语言切换。 +- 如果两个工具需要互相关联,可以在 \`meta.json\` 里添加 \`link\`。 +` +--- + + +
+ +
+
diff --git a/apps/frontend/src/pages/[locale]/sys/terms.astro b/apps/frontend/src/pages/[locale]/sys/terms.astro new file mode 100644 index 0000000..23f9ac9 --- /dev/null +++ b/apps/frontend/src/pages/[locale]/sys/terms.astro @@ -0,0 +1,49 @@ +--- +import { getTranslations } from '@/i18n' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' + +export function getStaticPaths() { + return [{ params: { locale: 'zh' } }] +} + +const locale = 'zh' as const +const t = getTranslations(locale) + +const content = String.raw` +## 条款接受 + +访问和使用本网站("BK.io")即表示您同意受本服务条款的约束。如果您不同意,请勿使用本站。 + +## 服务描述 + +BK.io 是一个面向开发者的开放内容平台。所有内容,包括工具、教程、指南和文档,均由社区用户贡献。我们不创建、审核或背书任何用户提交的内容,也不托管或运营本站链接的第三方项目。 + +## 用户内容 + +通过提交内容(通过 Pull Request 或 Issue),您授予我们非独占、免版税的许可,允许我们在本站展示和分发该内容。您保证您有权提交此类内容。 + +## 免责声明 + +本站所有内容均由用户贡献,按"原样"提供,不作任何明示或暗示的保证。我们不保证任何信息的准确性、完整性、可靠性或实用性。您应自行判断和评估内容,使用风险由您自行承担。 + +## 第三方链接 + +本站链接到第三方网站和工具。我们不对这些网站的内容、可用性或做法负责。使用这些链接的风险由您自行承担。 + +## 责任限制 + +在任何情况下,BK.io、其运营者或贡献者均不对因使用或无法使用本站、或因依赖本站发布的任何内容而产生的任何损害承担责任。 + +## 条款变更 + +我们保留随时修改这些条款的权利。变更在发布后立即生效。继续使用本站即表示接受更新后的条款。 + +## 联系方式 + +如果您对本服务条款有任何疑问,请在我们的 [GitHub 仓库](https://github.com/bkhq/website) 上提交 Issue。 + +*最后更新:2026-03-07* +` +--- + + diff --git a/apps/frontend/src/pages/[locale]/tags/[tag].astro b/apps/frontend/src/pages/[locale]/tags/[tag].astro new file mode 100644 index 0000000..82fcdbe --- /dev/null +++ b/apps/frontend/src/pages/[locale]/tags/[tag].astro @@ -0,0 +1,21 @@ +--- +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' +import { getTranslations } from '@/i18n' +import { getToolsJson, txt } from '@/lib/content' + +export function getStaticPaths() { + const toolsData = getToolsJson() + return toolsData.tags.map((tag) => ({ + params: { locale: 'zh', tag: tag.key }, + })) +} + +const locale = 'zh' as const +const tagKey = Astro.params.tag! +const t = getTranslations(locale) +const toolsData = getToolsJson() +const tagObj = toolsData.tags.find((item) => item.key === tagKey) +const tagLabel = tagObj ? txt(tagObj, locale) : tagKey +--- + + diff --git a/apps/frontend/src/pages/[locale]/template.astro b/apps/frontend/src/pages/[locale]/template.astro index a7afa5d..f14acfb 100644 --- a/apps/frontend/src/pages/[locale]/template.astro +++ b/apps/frontend/src/pages/[locale]/template.astro @@ -1,181 +1,13 @@ --- -import Base from '@/layouts/Base.astro' -import type { Locale } from '@/i18n' -import { renderMarkdown } from '@/lib/content' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' export function getStaticPaths() { - return [{ params: { locale: 'en' } }, { params: { locale: 'zh' } }] + return [{ params: { locale: 'zh' } }] } -const locale = Astro.params.locale as Locale +const locale = 'zh' as const -const content: Record = { - en: ` -## Tool Template - -Use this page as the copyable starter for a custom tool entry. - -## Directory Layout - -\`\`\`text -content/ -├── list.json -└── my-tool/ - ├── meta.json - ├── en.index.md - ├── zh.index.md - ├── en.installation.md - ├── zh.installation.md - ├── en.configuration.md - ├── zh.configuration.md - ├── en.data.md - ├── zh.data.md - ├── en.faq.md - └── zh.faq.md -\`\`\` - -## \`meta.json\` - -\`\`\`json -{ - "title": { "en": "My Tool", "zh": "我的工具" }, - "description": { - "en": "One-line summary for cards and page headers.", - "zh": "用于卡片和页面头部的一句话说明。" - }, - "github": "https://github.com/example/my-tool", - "relatedTools": ["agent-browser"] -} -\`\`\` - -## \`content/list.json\` Entry - -\`\`\`json -{ - "slug": "my-tool", - "category": "devTools", - "tags": ["open-source", "free", "cli", "web"] -} -\`\`\` - -## Available Categories - -- \`images\` -- \`textCode\` -- \`colors\` -- \`converters\` -- \`devTools\` -- \`containers\` - -## Common Tags - -- \`open-source\` -- \`free\` -- \`desktop\` -- \`cli\` -- \`web\` -- \`privacy\` -- \`docker\` -- \`self-hosted\` -- \`automation\` -- \`proxy\` - -## Starter Pages - -### \`en.index.md\` - -\`\`\`markdown ---- -title: - en: Introduction - zh: 简介 -order: 0 ---- - -## Overview - -Describe what the tool does, who it is for, and why it matters. - -## Related Workflows - -Mention adjacent tools with placeholders like [[agent-browser]] or [[zzci-chrome|zzci/chrome]]. -\`\`\` - -### \`en.installation.md\` - -\`\`\`markdown ---- -title: - en: Installation - zh: 安装 -order: 1 ---- - -## Installation - -Add the shortest working setup path. -\`\`\` - -### \`en.configuration.md\` - -\`\`\`markdown ---- -title: - en: Configuration - zh: 配置 -order: 2 ---- - -## Configuration - -List the switches, ports, files, or environment variables people need. -\`\`\` - -### \`en.data.md\` - -\`\`\`markdown ---- -title: - en: Data & Storage - zh: 数据说明 -order: 3 ---- - -## Data & Storage - -Explain what state is persisted, what is ephemeral, and what should be backed up. -\`\`\` - -### \`en.faq.md\` - -\`\`\`markdown ---- -title: - en: FAQ - zh: 常见问题 -order: 4 ---- - -## FAQ - -Add the real questions people will ask after trying the tool. -\`\`\` - -Duplicate the same files with \`zh.*.md\` and translate the body text. - -## Placeholder Links - -Use placeholders instead of hardcoding locale-specific paths: - -\`\`\`text -[[agent-browser]] -[[zzci-chrome|zzci/chrome]] -[[bkd/data|Data & Storage]] -\`\`\` - -The renderer converts them into the current locale automatically. -`, - zh: ` +const content = String.raw` ## 工具模板 这个页面就是后续新增自定义工具时可直接照抄的 starter template。 @@ -209,7 +41,7 @@ content/ "zh": "用于卡片和页面头部的一句话说明。" }, "github": "https://github.com/example/my-tool", - "relatedTools": ["agent-browser"] + "link": ["agent-browser"] } \`\`\` @@ -245,7 +77,7 @@ content/ - \`automation\` - \`proxy\` -## 页面骨架 +## 初始页面 ### \`en.index.md\` @@ -326,11 +158,11 @@ order: 4 Add the real questions people will ask after trying the tool. \`\`\` -再复制出对应的 \`zh.*.md\` 并翻译正文。 +复制同样结构的 \`zh.*.md\` 文件,再把正文翻译成中文即可。 ## 占位符链接 -不要手写 /en/... 或 /zh/...,直接写: +请不要在 Markdown 里直接写死带语言前缀的路径,统一使用占位符: \`\`\`text [[agent-browser]] @@ -338,28 +170,8 @@ Add the real questions people will ask after trying the tool. [[bkd/data|数据说明]] \`\`\` -渲染层会自动替换成当前语言的站内链接。 -`, -} - -const htmlContent = await renderMarkdown(content[locale], locale) +渲染时会自动替换成当前语言的站内链接。 +` --- - -
-
-
-

- {locale === 'zh' ? '工具模板' : 'Tool Template'} -

-

- {locale === 'zh' ? '用于新增自定义工具的最小模板、分类标签说明,以及站内占位符链接写法。' : 'A copyable starter for custom tool entries, including category/tag guidance and locale-safe placeholder links.'} -

-
- -
-
-
-
-
- + diff --git a/apps/frontend/src/pages/[locale]/terms.astro b/apps/frontend/src/pages/[locale]/terms.astro deleted file mode 100644 index a2dbc26..0000000 --- a/apps/frontend/src/pages/[locale]/terms.astro +++ /dev/null @@ -1,104 +0,0 @@ ---- -import Base from '@/layouts/Base.astro' -import { getTranslations } from '@/i18n' -import type { Locale } from '@/i18n' -import { renderMarkdown } from '@/lib/content' - -export function getStaticPaths() { - return [{ params: { locale: 'en' } }, { params: { locale: 'zh' } }] -} - -const locale = Astro.params.locale as Locale -const t = getTranslations(locale) - -const content: Record = { - en: ` -## Acceptance of Terms - -By accessing and using this website ("BK.io"), you agree to be bound by these Terms of Service. If you do not agree, please do not use this site. - -## Description of Service - -BK.io is an open content platform for developers. All content — including tools, tutorials, guides, and documentation — is contributed by community users. We do not create, verify, or endorse any user-submitted content, nor do we host or operate the third-party projects linked on this site. - -## User Content - -By submitting content (via Pull Requests or Issues), you grant us a non-exclusive, royalty-free license to display and distribute the content on this site. You represent that you have the right to submit such content. - -## Disclaimer - -All content on this site is contributed by users and provided "as is" without warranties of any kind, either express or implied. We do not guarantee the accuracy, completeness, reliability, or usefulness of any information. You are solely responsible for evaluating and using the content at your own discretion. - -## Third-Party Links - -This site links to third-party websites and tools. We are not responsible for the content, availability, or practices of those sites. Use them at your own risk. - -## Limitation of Liability - -In no event shall BK.io, its operators, or its contributors be liable for any damages arising from the use or inability to use this site, or from reliance on any content published on it. - -## Changes to Terms - -We reserve the right to modify these terms at any time. Changes take effect immediately upon posting. Continued use of the site constitutes acceptance of the updated terms. - -## Contact - -If you have questions about these Terms of Service, please open an issue on our [GitHub repository](https://github.com/bkhq/website). - -*Last updated: 2026-03-07* -`, - zh: ` -## 条款接受 - -访问和使用本网站("BK.io")即表示您同意受本服务条款的约束。如果您不同意,请勿使用本站。 - -## 服务描述 - -BK.io 是一个面向开发者的开放内容平台。所有内容——包括工具、教程、指南和文档——均由社区用户贡献。我们不创建、审核或背书任何用户提交的内容,也不托管或运营本站链接的第三方项目。 - -## 用户内容 - -通过提交内容(通过 Pull Request 或 Issue),您授予我们非独占、免版税的许可,允许我们在本站展示和分发该内容。您保证您有权提交此类内容。 - -## 免责声明 - -本站所有内容均由用户贡献,按"原样"提供,不作任何明示或暗示的保证。我们不保证任何信息的准确性、完整性、可靠性或实用性。您应自行判断和评估内容,使用风险由您自行承担。 - -## 第三方链接 - -本站链接到第三方网站和工具。我们不对这些网站的内容、可用性或做法负责。使用这些链接的风险由您自行承担。 - -## 责任限制 - -在任何情况下,BK.io、其运营者或贡献者均不对因使用或无法使用本站、或因依赖本站发布的任何内容而产生的任何损害承担责任。 - -## 条款变更 - -我们保留随时修改这些条款的权利。变更在发布后立即生效。继续使用本站即表示接受更新后的条款。 - -## 联系方式 - -如果您对本服务条款有任何疑问,请在我们的 [GitHub 仓库](https://github.com/bkhq/website) 上提交 Issue。 - -*最后更新:2026-03-07* -`, -} - -const htmlContent = await renderMarkdown(content[locale], locale) ---- - - -
-
-
-

- {t.footer.terms} -

-
- -
-
-
-
-
- diff --git a/apps/frontend/src/pages/category/[category].astro b/apps/frontend/src/pages/category/[category].astro new file mode 100644 index 0000000..d1fd462 --- /dev/null +++ b/apps/frontend/src/pages/category/[category].astro @@ -0,0 +1,21 @@ +--- +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' +import { getTranslations } from '@/i18n' +import { getToolsJson, txt } from '@/lib/content' + +export function getStaticPaths() { + const toolsData = getToolsJson() + return toolsData.categories.map((category) => ({ + params: { category: category.key }, + })) +} + +const locale = 'en' as const +const categoryKey = Astro.params.category! +const t = getTranslations(locale) +const toolsData = getToolsJson() +const categoryObj = toolsData.categories.find((item) => item.key === categoryKey) +const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey +--- + + diff --git a/apps/frontend/src/pages/index.astro b/apps/frontend/src/pages/index.astro index 7d1874d..372fa91 100644 --- a/apps/frontend/src/pages/index.astro +++ b/apps/frontend/src/pages/index.astro @@ -1,70 +1,5 @@ --- -// Root renders English by default (good for SEO), redirects to /zh/ if browser is Chinese -import Base from '@/layouts/Base.astro' -import { ToolSearch } from '@/components/ToolSearch' -import { getTranslations } from '@/i18n' -import { getToolsJson, getToolMeta, getToolBadge, getToolTags, resolveToolLogo, txt } from '@/lib/content' - -const locale = 'en' as const -const t = getTranslations(locale) -const toolsData = getToolsJson() - -const toolItems = toolsData.list - .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) - .map((item) => { - const meta = getToolMeta(item.slug) - const logo = resolveToolLogo(item.slug, item.logo) - const title = txt(meta.title, locale) - return { - slug: item.slug, - category: item.category, - badge: getToolBadge(toolsData, item.category, locale), - logo: logo.type === 'icon' ? item.logo! : logo.type === 'svg' ? 'custom' : '', - logoSvg: logo.type === 'svg' ? logo.content : undefined, - order: item.order, - title, - description: txt(meta.description, locale), - tags: getToolTags(toolsData, item.tags ?? [], locale), - } - }) - -const categories = toolsData.categories.map((cat) => ({ - key: cat.key, - label: txt(cat, locale), -})) - -const allTags = toolsData.tags.map((tag) => ({ - key: tag.key, - label: txt(tag, locale), -})) +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' --- - -
-
-

- {t.hero.title} -

- -

- {t.hero.subtitle} -

- - -
-
- + diff --git a/apps/frontend/src/pages/sys/privacy.astro b/apps/frontend/src/pages/sys/privacy.astro new file mode 100644 index 0000000..dc52ec6 --- /dev/null +++ b/apps/frontend/src/pages/sys/privacy.astro @@ -0,0 +1,35 @@ +--- +import { getTranslations } from '@/i18n' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' + +const locale = 'en' as const +const t = getTranslations(locale) + +const content = String.raw` +## Information We Collect + +We do not collect personal information or use cookies. This website uses Cloudflare Web Analytics, a privacy-first analytics service that does not use cookies or track individual users. It only collects aggregated data such as page views, referrers, and browser/device types. + +## Third-Party Services + +**Cloudflare Web Analytics** — We use Cloudflare Web Analytics to understand site usage. It does not collect personal data, does not use cookies, and does not track users across sites. Learn more at [Cloudflare Web Analytics](https://www.cloudflare.com/web-analytics/). + +**Cloudflare CDN** — This site is hosted on Cloudflare. Cloudflare may collect basic access logs (IP address, browser type, access time) as part of its standard CDN service. Please refer to [Cloudflare's Privacy Policy](https://www.cloudflare.com/privacypolicy/) for details. + +## External Links + +This site contains links to third-party websites. We are not responsible for the privacy practices of those sites. We encourage you to review the privacy policies of any site you visit. + +## Changes to This Policy + +We may update this Privacy Policy from time to time. Any changes will be posted on this page. + +## Contact + +If you have questions about this Privacy Policy, please open an issue on our [GitHub repository](https://github.com/bkhq/website). + +*Last updated: 2026-03-07* +` +--- + + diff --git a/apps/frontend/src/pages/sys/submit.astro b/apps/frontend/src/pages/sys/submit.astro new file mode 100644 index 0000000..cdefe0a --- /dev/null +++ b/apps/frontend/src/pages/sys/submit.astro @@ -0,0 +1,64 @@ +--- +import { GithubPrButton } from '@/components/GithubPrButton' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' +import { getTranslations } from '@/i18n' + +const locale = 'en' as const +const t = getTranslations(locale) + +const content = String.raw` +## Quick Submit + +Open an Issue with the tool URL if you want AI to draft the content for you. + +[Submit via Issue →](https://github.com/bkhq/website/issues/new?template=quick-submit.yml&title=%5BSubmit%5D+Tool+Name) + +If you want to add a tool manually, start from the [Template Guide →](/template). + +## Manual Submit + +\`\`\`bash +# 1. Fork the repo on GitHub +# 2. Clone your fork +# 3. Add content// +# 4. Register the slug in content/list.json +# 5. Commit, push, and open a Pull Request + +git clone https://github.com//website.git +cd website +mkdir -p content/my-tool +\`\`\` + +## Minimum Structure + +\`\`\`text +content/ +├── list.json +└── my-tool/ + ├── meta.json + ├── en.index.md + ├── zh.index.md + ├── en.installation.md + ├── zh.installation.md + ├── en.configuration.md + ├── zh.configuration.md + ├── en.data.md + ├── zh.data.md + ├── en.faq.md + └── zh.faq.md +\`\`\` + +## Notes + +- Keep the slug URL-safe. Use \`zzci-chrome\`, not \`zzci/chrome\`. +- Put display names such as \`zzci/chrome\` in \`meta.json\`. +- Use placeholders like \`[[agent-browser]]\` or \`[[bkd/data|Data & Storage]]\` for internal links. They are localized automatically. +- Add \`link\` in \`meta.json\` if this tool should link to another entry. +` +--- + + +
+ +
+
diff --git a/apps/frontend/src/pages/sys/terms.astro b/apps/frontend/src/pages/sys/terms.astro new file mode 100644 index 0000000..ef52c1b --- /dev/null +++ b/apps/frontend/src/pages/sys/terms.astro @@ -0,0 +1,45 @@ +--- +import { getTranslations } from '@/i18n' +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' + +const locale = 'en' as const +const t = getTranslations(locale) + +const content = String.raw` +## Acceptance of Terms + +By accessing and using this website ("BK.io"), you agree to be bound by these Terms of Service. If you do not agree, please do not use this site. + +## Description of Service + +BK.io is an open content platform for developers. All content, including tools, tutorials, guides, and documentation, is contributed by community users. We do not create, verify, or endorse any user-submitted content, nor do we host or operate the third-party projects linked on this site. + +## User Content + +By submitting content (via Pull Requests or Issues), you grant us a non-exclusive, royalty-free license to display and distribute the content on this site. You represent that you have the right to submit such content. + +## Disclaimer + +All content on this site is contributed by users and provided "as is" without warranties of any kind, either express or implied. We do not guarantee the accuracy, completeness, reliability, or usefulness of any information. You are solely responsible for evaluating and using the content at your own discretion. + +## Third-Party Links + +This site links to third-party websites and tools. We are not responsible for the content, availability, or practices of those sites. Use them at your own risk. + +## Limitation of Liability + +In no event shall BK.io, its operators, or its contributors be liable for any damages arising from the use or inability to use this site, or from reliance on any content published on it. + +## Changes to Terms + +We reserve the right to modify these terms at any time. Changes take effect immediately upon posting. Continued use of the site constitutes acceptance of the updated terms. + +## Contact + +If you have questions about these Terms of Service, please open an issue on our [GitHub repository](https://github.com/bkhq/website). + +*Last updated: 2026-03-07* +` +--- + + diff --git a/apps/frontend/src/pages/tags/[tag].astro b/apps/frontend/src/pages/tags/[tag].astro new file mode 100644 index 0000000..81a7077 --- /dev/null +++ b/apps/frontend/src/pages/tags/[tag].astro @@ -0,0 +1,21 @@ +--- +import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' +import { getTranslations } from '@/i18n' +import { getToolsJson, txt } from '@/lib/content' + +export function getStaticPaths() { + const toolsData = getToolsJson() + return toolsData.tags.map((tag) => ({ + params: { tag: tag.key }, + })) +} + +const locale = 'en' as const +const tagKey = Astro.params.tag! +const t = getTranslations(locale) +const toolsData = getToolsJson() +const tagObj = toolsData.tags.find((item) => item.key === tagKey) +const tagLabel = tagObj ? txt(tagObj, locale) : tagKey +--- + + diff --git a/apps/frontend/src/pages/template.astro b/apps/frontend/src/pages/template.astro new file mode 100644 index 0000000..96a260a --- /dev/null +++ b/apps/frontend/src/pages/template.astro @@ -0,0 +1,173 @@ +--- +import MarkdownArticlePage from '@/components/MarkdownArticlePage.astro' + +const locale = 'en' as const + +const content = String.raw` +## Tool Template + +Use this page as the copyable starter for a custom tool entry. + +## Directory Layout + +\`\`\`text +content/ +├── list.json +└── my-tool/ + ├── meta.json + ├── en.index.md + ├── zh.index.md + ├── en.installation.md + ├── zh.installation.md + ├── en.configuration.md + ├── zh.configuration.md + ├── en.data.md + ├── zh.data.md + ├── en.faq.md + └── zh.faq.md +\`\`\` + +## \`meta.json\` + +\`\`\`json +{ + "title": { "en": "My Tool", "zh": "我的工具" }, + "description": { + "en": "One-line summary for cards and page headers.", + "zh": "用于卡片和页面头部的一句话说明。" + }, + "github": "https://github.com/example/my-tool", + "link": ["agent-browser"] +} +\`\`\` + +## \`content/list.json\` Entry + +\`\`\`json +{ + "slug": "my-tool", + "category": "devTools", + "tags": ["open-source", "free", "cli", "web"] +} +\`\`\` + +## Available Categories + +- \`images\` +- \`textCode\` +- \`colors\` +- \`converters\` +- \`devTools\` +- \`containers\` + +## Common Tags + +- \`open-source\` +- \`free\` +- \`desktop\` +- \`cli\` +- \`web\` +- \`privacy\` +- \`docker\` +- \`self-hosted\` +- \`automation\` +- \`proxy\` + +## Starter Pages + +### \`en.index.md\` + +\`\`\`markdown +--- +title: + en: Introduction + zh: 简介 +order: 0 +--- + +## Overview + +Describe what the tool does, who it is for, and why it matters. + +## Related Workflows + +Mention adjacent tools with placeholders like [[agent-browser]] or [[zzci-chrome|zzci/chrome]]. +\`\`\` + +### \`en.installation.md\` + +\`\`\`markdown +--- +title: + en: Installation + zh: 安装 +order: 1 +--- + +## Installation + +Add the shortest working setup path. +\`\`\` + +### \`en.configuration.md\` + +\`\`\`markdown +--- +title: + en: Configuration + zh: 配置 +order: 2 +--- + +## Configuration + +List the switches, ports, files, or environment variables people need. +\`\`\` + +### \`en.data.md\` + +\`\`\`markdown +--- +title: + en: Data & Storage + zh: 数据说明 +order: 3 +--- + +## Data & Storage + +Explain what state is persisted, what is ephemeral, and what should be backed up. +\`\`\` + +### \`en.faq.md\` + +\`\`\`markdown +--- +title: + en: FAQ + zh: 常见问题 +order: 4 +--- + +## FAQ + +Add the real questions people will ask after trying the tool. +\`\`\` + +Duplicate the same files with \`zh.*.md\` and translate the body text. + +## Placeholder Links + +Use placeholders instead of hardcoding locale-specific paths: + +\`\`\`text +[[agent-browser]] +[[zzci-chrome|zzci/chrome]] +[[bkd/data|Data & Storage]] +\`\`\` + +The renderer converts them into the current locale automatically. +` +--- + + diff --git a/content/agent-browser/meta.json b/content/agent-browser/meta.json index 820ebc4..24d507c 100644 --- a/content/agent-browser/meta.json +++ b/content/agent-browser/meta.json @@ -8,7 +8,7 @@ "github": "https://github.com/vercel-labs/agent-browser", "addedAt": "2026-03-09", "addedBy": { "name": "vercel-labs", "url": "https://github.com/vercel-labs" }, - "relatedTools": ["zzci-chrome"], + "link": ["zzci-chrome"], "links": [ { "label": { "en": "Website", "zh": "官网" }, diff --git a/content/zzci-chrome/meta.json b/content/zzci-chrome/meta.json index d5d7013..a832528 100644 --- a/content/zzci-chrome/meta.json +++ b/content/zzci-chrome/meta.json @@ -7,7 +7,7 @@ "github": "https://github.com/zzci/chrome", "addedAt": "2026-03-09", "addedBy": { "name": "zzci", "url": "https://github.com/zzci" }, - "relatedTools": ["agent-browser"], + "link": ["agent-browser"], "links": [ { "label": { "en": "Report Issue", "zh": "问题反馈" }, diff --git a/docs/changelog.md b/docs/changelog.md index 9af9088..d7c6e2b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -21,3 +21,9 @@ Removed the existing `.git` history and reinitialized the repository on branch ` Moved API TypeScript build output from `apps/api/src` to `apps/api/dist`. Ignored API build artifacts in version control and removed previously committed generated files. Kept the Worker entry on TypeScript source because local development and Wrangler both work directly from source. + +## 2026-03-09 18:20 [decision] + +Changed frontend routing to use English as the default no-prefix path and Chinese under `/zh/*`. +Moved system pages to `/sys/*` and tag pages to `/tags/*`, while keeping `/api/*` reserved. +Updated locale switching, internal link localization, canonical and alternate tags, and Worker redirects for legacy `/en/*` and `/tag/*` paths. diff --git a/docs/plan/PLAN-004.md b/docs/plan/PLAN-004.md new file mode 100644 index 0000000..d525d3f --- /dev/null +++ b/docs/plan/PLAN-004.md @@ -0,0 +1,145 @@ +# PLAN-004 调整默认语言无前缀路由并整理保留路径命名空间 + +- **status**: completed +- **createdAt**: 2026-03-09 14:10 +- **approvedAt**: 2026-03-09 18:05 +- **completedAt**: 2026-03-09 18:20 +- **relatedTask**: FE-002 + +## 现状 + +当前前端路由是“所有语言都必须带前缀”的结构: + +- 首页同时存在 `/` 与 `/[locale]/` +- 工具详情、标签页、分类页、系统页都在 `apps/frontend/src/pages/[locale]/...` +- `Header`、`Footer`、`LocaleSwitcher`、Markdown 内链重写都默认把站内链接写成 `/${locale}/...` +- `Base.astro` 的 canonical、alternate、x-default 也基于带前缀路径生成 +- API 由 Worker 保留在 `/api/*` + +当前实现的直接结果: + +- 默认英文虽然有根首页 `/`,但其它英文内容页仍只能通过 `/en/*` 访问 +- `/tag/*` 当前使用单数命名,与目标的 `/tags/*` 不一致 +- 系统页当前位于 `/${locale}/privacy`、`/${locale}/terms`、`/${locale}/submit`,尚未归入 `/sys/*` +- 相关工具目前仅以内联卡片展示,没有独立的 `/link/*` 命名空间 + +## 调查上下文 + +这次变更会同时影响以下链路: + +- 页面生成:`src/pages/[locale]/index.astro`、`[...slug].astro`、`tag/[tag].astro`、`category/[category].astro`、`privacy.astro`、`terms.astro`、`submit.astro` +- 链接生成:`src/lib/content.ts` +- 布局与 SEO:`src/layouts/Base.astro` +- 客户端语言切换:`src/components/LocaleSwitcher.tsx` +- 公共导航:`src/components/Header.tsx`、`Footer.tsx` +- 旧路径兼容:`apps/api/src/app.ts` + +因此这是一个跨模块的非平凡改动,不适合只在根路由做前端跳转兜底。 + +## 方案 + +推荐采用“英文为默认无前缀主路由,中文保留 `/zh` 前缀,旧 `/en/*` 统一兼容跳转”的方案。 + +目标 URL 结构: + +- 首页 + - 英文:`/` + - 中文:`/zh/` +- 工具页 + - 英文:`/:tool` + - 中文:`/zh/:tool` +- 工具子文档 + - 英文:`/:tool/:doc` + - 中文:`/zh/:tool/:doc` +- 标签页 + - 英文:`/tags/:tag` + - 中文:`/zh/tags/:tag` +- 分类页 + - 英文:`/category/:category` + - 中文:`/zh/category/:category` +- 系统页 + - 英文:`/sys/privacy` `/sys/terms` `/sys/submit` + - 中文:`/zh/sys/privacy` `/zh/sys/terms` `/zh/sys/submit` +- 关系页命名空间 + - 预留:`/link/*` 与 `/zh/link/*` +- API + - 保持:`/api/*` + +实施要点: + +1. 为默认语言新增根级页面路由,不再让英文内容依赖 `/en/*` +2. 将当前 `[locale]` 页面收敛为“仅中文”或“显式语言前缀页面”,避免生成重复英文 canonical +3. 抽出统一的路由辅助方法,集中生成: + - 默认语言路径 + - 非默认语言路径 + - 语言切换目标路径 + - canonical / alternate / x-default +4. 将 Markdown 内链本地化逻辑改为: + - 英文链接默认无前缀 + - 中文链接加 `/zh` + - `/api`、静态资源和保留系统路径按规则跳过或按语言前缀生成 +5. 在 Worker 中兼容旧 `/en/*`,将其重定向到新的无前缀英文路径 + +## 风险 + +- 根级新增 `/:tool`、`/:tool/:doc` 后,必须避免与 `/api`、`/zh`、`/sys`、`/tags`、`/category`、`/link` 等保留前缀冲突 +- 如果只改页面目录、不改 SEO 元数据,会产生英文页面双地址收录问题 +- 如果不处理 `/en/*` 兼容,现有外链与搜索索引会直接失效 +- `link` 命名空间目前只有语义方向,没有现成数据模型;本次若强行实现详情页,容易引入半成品路由 + +## 范围 + +本次建议纳入: + +- 默认语言无前缀路由 +- 中文保留 `/zh` 前缀 +- `/sys` `/tags` `/category` 命名调整 +- 旧 `/en/*` 兼容策略 +- 站内链接、语言切换与 SEO 修正 + +本次不建议纳入: + +- 新的 `/link/*` 页面实体与信息架构实现 +- 内容文件命名从 `en.xxx.md` / `zh.xxx.md` 改成目录式存储 + +`/link/*` 更适合在确认“是按工具聚合、按双向关系对、还是按关系类型”之后单独做一轮。 + +## 备选方案 + +### 方案 A:仅保留现有 `/en/*` `/zh/*`,根路径做语言检测跳转 + +优点: + +- 代码改动最小 + +缺点: + +- 默认语言仍然不是主路由 +- 英文路径冗余 +- 与你提出的 `/bkd`、`/zh/bkd` 目标不一致 + +### 方案 B:增加根级 catch-all,把无前缀请求跳到 `/en/*` 或 `/zh/*` + +优点: + +- 改动较少 + +缺点: + +- 实际 canonical 仍在带前缀路由 +- 站内链接和内容模型继续围绕语言前缀设计 +- 后续若再改成英文无前缀,还要做第二次迁移 + +### 方案 C:英文无前缀为主路由,中文保留 `/zh`,旧 `/en/*` 做兼容跳转 + +优点: + +- URL 结构最干净 +- SEO 语义清晰 +- 与当前内容文件回退机制兼容 + +缺点: + +- 需要同步调整页面生成、链接工具、SEO 和 Worker 兼容逻辑 + +推荐采用方案 C。 diff --git a/docs/plan/index.md b/docs/plan/index.md index c16b50e..5c860ab 100644 --- a/docs/plan/index.md +++ b/docs/plan/index.md @@ -1,6 +1,6 @@ # bkio - Plan Index -> Updated: 2026-03-07 +> Updated: 2026-03-09 ## Usage @@ -31,3 +31,4 @@ Each plan is a single line linking to its detail file. All detailed information - [x] [**PLAN-001 Rename workspace identities and reset git history**](PLAN-001.md) `2026-03-07` - [x] [**PLAN-002 Keep Worker source entry and move API TypeScript output to dist**](PLAN-002.md) `2026-03-07` - [x] [**PLAN-003 迁移前端到 Astro + Content Collections**](PLAN-003.md) `2026-03-07` +- [x] [**PLAN-004 调整默认语言无前缀路由并整理保留路径命名空间**](PLAN-004.md) `2026-03-09` diff --git a/docs/task/FE-002.md b/docs/task/FE-002.md new file mode 100644 index 0000000..8701925 --- /dev/null +++ b/docs/task/FE-002.md @@ -0,0 +1,28 @@ +# FE-002 调整默认语言无前缀路由并保留保留路径命名空间 + +- **status**: completed +- **priority**: P1 +- **createdAt**: 2026-03-09 14:10 +- **owner**: codex +- **completedAt**: 2026-03-09 +- **relatedPlan**: PLAN-004 + +## 描述 + +将当前强制语言前缀的前端路由调整为“默认语言无前缀,非默认语言保留前缀”的结构,并整理保留路径命名空间: + +- 默认语言英文使用无前缀路径,例如 `/bkd` +- 中文使用语言前缀路径,例如 `/zh/bkd` +- 系统页面归入 `/sys/*` +- API 继续保留 `/api/*` +- 标签页使用 `/tags/*` +- 分类页使用 `/category/*` +- `link` 作为关系型页面命名空间保留,后续承载 related 语义 + +## 验收标准 + +- [x] 明确默认语言与非默认语言的 URL 规则 +- [x] 明确 `/sys` `/api` `/tags` `/category` `/link` 的路由边界 +- [x] 默认语言页面可直接通过无前缀 URL 访问 +- [x] 现有 `/en/*` 路径有明确兼容策略 +- [x] 站内链接、语言切换、canonical 与 alternate 规则与新路由一致 diff --git a/docs/task/index.md b/docs/task/index.md index 6500e59..33329f5 100644 --- a/docs/task/index.md +++ b/docs/task/index.md @@ -1,6 +1,6 @@ # bkio - Task List -> Updated: 2026-03-07 +> Updated: 2026-03-09 ## Usage @@ -33,3 +33,4 @@ Each task is a single line linking to its detail file. All detailed information - [x] [**REPO-001 Rename workspace and reinitialize git**](REPO-001.md) `P1` - [x] [**REPO-002 Move API build output out of src and ignore artifacts**](REPO-002.md) `P1` - [x] [**FE-001 迁移前端到 Astro + Content Collections**](FE-001.md) `P1` +- [x] [**FE-002 调整默认语言无前缀路由并保留保留路径命名空间**](FE-002.md) `P1` From 5be4c0935883c66790a2277d1e9ef31c3de9388c Mon Sep 17 00:00:00 2001 From: roy <62500486+aazf@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:57:25 +0000 Subject: [PATCH 2/5] ci: add pull request workflow --- .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ab55178 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: Lint & Format + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} + restore-keys: ${{ runner.os }}-bun- + + - name: Install dependencies + run: bun install + + - name: Lint + run: bun run lint From 706555c85f9eae0785ccc816e24926ce5bc2e4f1 Mon Sep 17 00:00:00 2001 From: roy <62500486+aazf@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:00:56 +0000 Subject: [PATCH 3/5] chore: replace biome with eslint --- CLAUDE.md | 4 +- apps/api/src/app.ts | 19 +- apps/api/tsconfig.json | 4 +- apps/frontend/src/components/Footer.tsx | 4 +- .../src/components/LocaleSwitcher.tsx | 4 +- apps/frontend/src/components/ThemeToggle.tsx | 24 +- .../src/components/ToolDetailPage.astro | 57 +- .../src/components/ToolDirectoryPage.astro | 4 +- apps/frontend/src/components/ToolSearch.tsx | 243 ++++---- apps/frontend/src/i18n/index.ts | 3 +- apps/frontend/src/i18n/types.ts | 2 +- apps/frontend/src/layouts/Base.astro | 30 +- apps/frontend/src/lib/content.ts | 74 +-- apps/frontend/src/lib/routes.ts | 6 +- apps/frontend/src/pages/404.astro | 4 +- apps/frontend/src/pages/[...slug].astro | 3 +- .../src/pages/[locale]/[...slug].astro | 2 +- .../pages/[locale]/category/[category].astro | 4 +- .../src/pages/[locale]/tags/[tag].astro | 4 +- .../src/pages/category/[category].astro | 4 +- apps/frontend/src/pages/tags/[tag].astro | 4 +- apps/frontend/src/styles/global.css | 25 +- apps/frontend/tsconfig.json | 6 +- biome.json | 120 ---- bun.lock | 537 ++++++++++++++++-- docs/architecture.md | 2 +- docs/changelog.md | 2 +- eslint.config.mjs | 69 +++ package.json | 17 +- 29 files changed, 868 insertions(+), 413 deletions(-) delete mode 100644 biome.json create mode 100644 eslint.config.mjs diff --git a/CLAUDE.md b/CLAUDE.md index 378ec96..4fcbc0c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,8 +10,8 @@ bkio — Monorepo template for Bun + Hono + Vite + React + Cloudflare Workers. - `bun run build` — Type-check API + build frontend - `bun run deploy` — Build and deploy to Cloudflare Workers - `bun run test` — Run all tests -- `bun run lint` — Lint with Biome -- `bun run format` — Format with Biome +- `bun run lint` — Lint with ESLint +- `bun run format` — Format with ESLint autofix ## Project Development diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 3efba6f..3fe595a 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -2,11 +2,11 @@ import { Hono } from 'hono' import { cors } from 'hono/cors' import { logger } from 'hono/logger' -type AssetBinding = { +interface AssetBinding { fetch: (request: Request | string, init?: RequestInit) => Promise } -export type Env = { +export interface Env { Bindings: { // Add your bindings here, e.g.: // DB: D1Database @@ -32,11 +32,16 @@ function buildRedirectPath(pathname: string): string | null { const prefix = locale === 'zh' ? '/zh' : '' - if (path === '/privacy') return `${prefix}/sys/privacy` - if (path === '/terms') return `${prefix}/sys/terms` - if (path === '/submit') return `${prefix}/sys/submit` - if (path === '/tag') return `${prefix}/tags` - if (path.startsWith('/tag/')) return `${prefix}/tags/${path.slice('/tag/'.length)}` + if (path === '/privacy') + return `${prefix}/sys/privacy` + if (path === '/terms') + return `${prefix}/sys/terms` + if (path === '/submit') + return `${prefix}/sys/submit` + if (path === '/tag') + return `${prefix}/tags` + if (path.startsWith('/tag/')) + return `${prefix}/tags/${path.slice('/tag/'.length)}` if (pathname === '/en' || pathname.startsWith('/en/')) { return path diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index a35b661..82be280 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -3,11 +3,11 @@ "compilerOptions": { "baseUrl": ".", "rootDir": "src", - "outDir": "dist", "paths": { "@/*": ["src/*"], "@bkio/shared": ["../../packages/shared/src/index.ts"] - } + }, + "outDir": "dist" }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] diff --git a/apps/frontend/src/components/Footer.tsx b/apps/frontend/src/components/Footer.tsx index ac380b3..f7ff522 100644 --- a/apps/frontend/src/components/Footer.tsx +++ b/apps/frontend/src/components/Footer.tsx @@ -5,13 +5,13 @@ const translations = { privacy: 'Privacy Policy', terms: 'Terms of Service', submit: 'Submit', - copyright: '\u00a9 2026 BK.io', + copyright: '\u00A9 2026 BK.io', }, zh: { privacy: '隐私政策', terms: '服务条款', submit: '投稿', - copyright: '\u00a9 2026 BK.io', + copyright: '\u00A9 2026 BK.io', }, } as const diff --git a/apps/frontend/src/components/LocaleSwitcher.tsx b/apps/frontend/src/components/LocaleSwitcher.tsx index 399d2e8..66a6828 100644 --- a/apps/frontend/src/components/LocaleSwitcher.tsx +++ b/apps/frontend/src/components/LocaleSwitcher.tsx @@ -12,7 +12,7 @@ type Locale = 'en' | 'zh' const STORAGE_KEY = 'bkio-locale' -const localeOptions: { value: Locale; label: string }[] = [ +const localeOptions: { value: Locale, label: string }[] = [ { value: 'en', label: 'English' }, { value: 'zh', label: '中文' }, ] @@ -36,7 +36,7 @@ export function LocaleSwitcher({ locale: initial }: { locale: Locale }) { - {localeOptions.map((opt) => ( + {localeOptions.map(opt => ( setLocale(opt.value)} diff --git a/apps/frontend/src/components/ThemeToggle.tsx b/apps/frontend/src/components/ThemeToggle.tsx index cf55b0a..5b7e541 100644 --- a/apps/frontend/src/components/ThemeToggle.tsx +++ b/apps/frontend/src/components/ThemeToggle.tsx @@ -12,9 +12,9 @@ type Theme = 'light' | 'dark' const STORAGE_KEY = 'bkio-theme' function getSystemTheme(): Theme { - return window.matchMedia('(prefers-color-scheme: dark)').matches - ? 'dark' - : 'light' + return window.matchMedia('(prefers-color-scheme: dark)').matches ? + 'dark' : + 'light' } const themeOptions: { @@ -29,9 +29,11 @@ const themeOptions: { export function ThemeToggle({ locale }: { locale: 'en' | 'zh' }) { const [theme, setThemeState] = useState(() => { - if (typeof window === 'undefined') return 'light' + if (typeof window === 'undefined') + return 'light' const saved = localStorage.getItem(STORAGE_KEY) - if (saved === 'light' || saved === 'dark') return saved + if (saved === 'light' || saved === 'dark') + return saved return getSystemTheme() }) @@ -50,11 +52,13 @@ export function ThemeToggle({ locale }: { locale: 'en' | 'zh' }) { className="inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg outline-none hover:bg-muted hover:text-foreground size-7" aria-label="Toggle color theme" > - {theme === 'dark' ? ( - - ) : ( - - )} + {theme === 'dark' ? + ( + + ) : + ( + + )} {themeOptions.map((opt) => { diff --git a/apps/frontend/src/components/ToolDetailPage.astro b/apps/frontend/src/components/ToolDetailPage.astro index f7983d3..8eeb978 100644 --- a/apps/frontend/src/components/ToolDetailPage.astro +++ b/apps/frontend/src/components/ToolDetailPage.astro @@ -35,7 +35,7 @@ const docSlug = parts[1] || 'index' const t = getTranslations(locale) const toolsData = getToolsJson() -const toolEntry = toolsData.list.find((item) => item.slug === toolSlug) +const toolEntry = toolsData.list.find(item => item.slug === toolSlug) const categoryLabel = toolEntry ? getToolBadge(toolsData, toolEntry.category, locale) : '' const toolTags = toolEntry ? getToolTags(toolsData, toolEntry.tags ?? [], locale) : [] @@ -44,7 +44,7 @@ const { data: frontmatter, html: htmlContent } = await getToolDoc(toolSlug, docS const allDocs = await getToolDocs(toolSlug, locale) const sidebarDocs = allDocs - .map((doc) => ({ + .map(doc => ({ slug: doc.slug, href: localizePath( locale, @@ -66,7 +66,7 @@ function isSafeUrl(url: string): boolean { } } -const customLinks = (meta.links ?? []).map((link) => ({ +const customLinks = (meta.links ?? []).map(link => ({ label: txt(link.label, locale), href: isSafeUrl(link.url) ? link.url : '#', })) @@ -76,8 +76,9 @@ const websiteUrl = meta.website && isSafeUrl(meta.website) ? meta.website : unde const githubUrl = meta.github && isSafeUrl(meta.github) ? meta.github : undefined const linkedTools: RelatedToolCard[] = (meta.link ?? []) .map((relatedSlug) => { - const relatedEntry = toolsData.list.find((item) => item.slug === relatedSlug) - if (!relatedEntry) return null + const relatedEntry = toolsData.list.find(item => item.slug === relatedSlug) + if (!relatedEntry) +return null const relatedMeta = getToolMeta(relatedSlug) return { @@ -103,24 +104,26 @@ const linkedTools: RelatedToolCard[] = (meta.link ?? []) {categoryLabel && toolEntry && ( {categoryLabel} )} - {toolTags.map((tag) => ( + {toolTags.map(tag => ( {tag.label} ))} {meta.addedAt && ( - + {meta.addedAt} )} {meta.addedBy && ( - authorUrl ? ( + authorUrl ? +( - + {meta.addedBy.name} - ) : ( + ) : +( - + {meta.addedBy.name} ) @@ -133,19 +136,19 @@ const linkedTools: RelatedToolCard[] = (meta.link ?? [])
{websiteUrl && ( - + {locale === 'zh' ? '官网' : 'Website'} )} {githubUrl && ( - + GitHub )} - {customLinks.map((link) => ( + {customLinks.map(link => ( - + {link.label} ))} @@ -157,13 +160,15 @@ const linkedTools: RelatedToolCard[] = (meta.link ?? [])
{t.toolDetail.link}
- {linkedTools.map((item) => ( + {linkedTools.map(item => (
- {item.logo.type === 'svg' ? ( + {item.logo.type === 'svg' ? +( - ) : ( + ) : +( {item.title.charAt(0).toUpperCase()} )}
@@ -189,7 +194,7 @@ const linkedTools: RelatedToolCard[] = (meta.link ?? []) {sidebarDocs.length > 1 && (
diff --git a/apps/frontend/src/pages/[...slug].astro b/apps/frontend/src/pages/[...slug].astro index 22d1f47..fb9b472 100644 --- a/apps/frontend/src/pages/[...slug].astro +++ b/apps/frontend/src/pages/[...slug].astro @@ -8,7 +8,8 @@ export function getStaticPaths() { const paths: { params: { slug: string } }[] = [] for (const item of toolsData.list) { - if (isReservedRootSlug(item.slug)) continue + if (isReservedRootSlug(item.slug)) +continue paths.push({ params: { slug: item.slug } }) diff --git a/apps/frontend/src/pages/[locale]/[...slug].astro b/apps/frontend/src/pages/[locale]/[...slug].astro index d212264..a86cb4f 100644 --- a/apps/frontend/src/pages/[locale]/[...slug].astro +++ b/apps/frontend/src/pages/[locale]/[...slug].astro @@ -4,7 +4,7 @@ import { getToolDocSlugs, getToolsJson } from '@/lib/content' export function getStaticPaths() { const toolsData = getToolsJson() - const paths: { params: { locale: string; slug: string } }[] = [] + const paths: { params: { locale: string, slug: string } }[] = [] for (const item of toolsData.list) { paths.push({ params: { locale: 'zh', slug: item.slug } }) diff --git a/apps/frontend/src/pages/[locale]/category/[category].astro b/apps/frontend/src/pages/[locale]/category/[category].astro index 0bd4ee4..185c986 100644 --- a/apps/frontend/src/pages/[locale]/category/[category].astro +++ b/apps/frontend/src/pages/[locale]/category/[category].astro @@ -5,7 +5,7 @@ import { getToolsJson, txt } from '@/lib/content' export function getStaticPaths() { const toolsData = getToolsJson() - return toolsData.categories.map((category) => ({ + return toolsData.categories.map(category => ({ params: { locale: 'zh', category: category.key }, })) } @@ -14,7 +14,7 @@ const locale = 'zh' as const const categoryKey = Astro.params.category! const t = getTranslations(locale) const toolsData = getToolsJson() -const categoryObj = toolsData.categories.find((item) => item.key === categoryKey) +const categoryObj = toolsData.categories.find(item => item.key === categoryKey) const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey --- diff --git a/apps/frontend/src/pages/[locale]/tags/[tag].astro b/apps/frontend/src/pages/[locale]/tags/[tag].astro index 82fcdbe..1effc72 100644 --- a/apps/frontend/src/pages/[locale]/tags/[tag].astro +++ b/apps/frontend/src/pages/[locale]/tags/[tag].astro @@ -5,7 +5,7 @@ import { getToolsJson, txt } from '@/lib/content' export function getStaticPaths() { const toolsData = getToolsJson() - return toolsData.tags.map((tag) => ({ + return toolsData.tags.map(tag => ({ params: { locale: 'zh', tag: tag.key }, })) } @@ -14,7 +14,7 @@ const locale = 'zh' as const const tagKey = Astro.params.tag! const t = getTranslations(locale) const toolsData = getToolsJson() -const tagObj = toolsData.tags.find((item) => item.key === tagKey) +const tagObj = toolsData.tags.find(item => item.key === tagKey) const tagLabel = tagObj ? txt(tagObj, locale) : tagKey --- diff --git a/apps/frontend/src/pages/category/[category].astro b/apps/frontend/src/pages/category/[category].astro index d1fd462..34eec84 100644 --- a/apps/frontend/src/pages/category/[category].astro +++ b/apps/frontend/src/pages/category/[category].astro @@ -5,7 +5,7 @@ import { getToolsJson, txt } from '@/lib/content' export function getStaticPaths() { const toolsData = getToolsJson() - return toolsData.categories.map((category) => ({ + return toolsData.categories.map(category => ({ params: { category: category.key }, })) } @@ -14,7 +14,7 @@ const locale = 'en' as const const categoryKey = Astro.params.category! const t = getTranslations(locale) const toolsData = getToolsJson() -const categoryObj = toolsData.categories.find((item) => item.key === categoryKey) +const categoryObj = toolsData.categories.find(item => item.key === categoryKey) const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey --- diff --git a/apps/frontend/src/pages/tags/[tag].astro b/apps/frontend/src/pages/tags/[tag].astro index 81a7077..ec65449 100644 --- a/apps/frontend/src/pages/tags/[tag].astro +++ b/apps/frontend/src/pages/tags/[tag].astro @@ -5,7 +5,7 @@ import { getToolsJson, txt } from '@/lib/content' export function getStaticPaths() { const toolsData = getToolsJson() - return toolsData.tags.map((tag) => ({ + return toolsData.tags.map(tag => ({ params: { tag: tag.key }, })) } @@ -14,7 +14,7 @@ const locale = 'en' as const const tagKey = Astro.params.tag! const t = getTranslations(locale) const toolsData = getToolsJson() -const tagObj = toolsData.tags.find((item) => item.key === tagKey) +const tagObj = toolsData.tags.find(item => item.key === tagKey) const tagLabel = tagObj ? txt(tagObj, locale) : tagKey --- diff --git a/apps/frontend/src/styles/global.css b/apps/frontend/src/styles/global.css index 22b7f19..9d44c45 100644 --- a/apps/frontend/src/styles/global.css +++ b/apps/frontend/src/styles/global.css @@ -1,5 +1,5 @@ -@import "tailwindcss"; -@import "tw-animate-css"; +@import 'tailwindcss'; +@import 'tw-animate-css'; @custom-variant dark (&:is(.dark *)); @@ -111,17 +111,13 @@ body { @apply bg-background text-foreground; - font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, - Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif; } } @layer components { .font-mono-system { - font-family: - ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, - "Liberation Mono", monospace; + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace; } .card-fade { @@ -220,7 +216,7 @@ /* Inline code */ .prose-article :not(pre) > code { - font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; font-size: 0.85em; padding: 0.15em 0.4em; border-radius: 4px; @@ -236,12 +232,12 @@ } /* Language label */ -.prose-article .code-block[data-lang]:not([data-lang=""])::before { +.prose-article .code-block[data-lang]:not([data-lang=''])::before { content: attr(data-lang); position: absolute; top: 0.5rem; right: 3rem; - font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; font-size: 11px; color: var(--muted-foreground); opacity: 0.6; @@ -265,7 +261,10 @@ color: var(--muted-foreground); cursor: pointer; opacity: 0; - transition: opacity 0.15s, background-color 0.15s, border-color 0.15s; + transition: + opacity 0.15s, + background-color 0.15s, + border-color 0.15s; z-index: 2; } @@ -296,7 +295,7 @@ } .prose-article pre code { - font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; background: none; padding: 0; border-radius: 0; diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 1cd058c..65e3130 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -1,12 +1,12 @@ { "extends": "astro/tsconfigs/strict", "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react", "baseUrl": ".", "paths": { "@/*": ["./src/*"] - }, - "jsx": "react-jsx", - "jsxImportSource": "react" + } }, "include": ["**/*.ts", "**/*.tsx", "**/*.astro"], "exclude": ["node_modules", "dist"] diff --git a/biome.json b/biome.json deleted file mode 100644 index 970e3f9..0000000 --- a/biome.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "root": true, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 2 - }, - "css": { - "parser": { - "tailwindDirectives": true - } - }, - "assist": { - "actions": { - "source": { - "organizeImports": "on" - } - } - }, - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, - "linter": { - "enabled": true, - "rules": { - "style": { - "noRestrictedImports": { - "options": { - "paths": { - "zod": { - "importNames": ["z"], - "message": "Use `import * as z from \"zod\"` instead of `import { z }`." - } - } - }, - "level": "error" - }, - "useImportType": { - "level": "error", - "options": { - "style": "separatedType" - } - }, - "useNodejsImportProtocol": "error", - "useConst": "error", - "noRestrictedTypes": "off", - "noRestrictedGlobals": "off" - }, - "recommended": false, - "suspicious": { - "noImplicitAnyLet": "warn", - "noDuplicateObjectKeys": "warn", - "noTsIgnore": "error", - "noDebugger": "error" - }, - "performance": { - "noDelete": "error" - }, - "complexity": { - "useDateNow": "error", - "noUselessSwitchCase": "warn", - "noUselessTypeConstraint": "warn" - }, - "correctness": { - "noUnusedImports": "warn" - }, - "nursery": { - "noMisusedPromises": "error", - "noFloatingPromises": "error" - } - } - }, - "javascript": { - "formatter": { - "semicolons": "asNeeded", - "quoteStyle": "single" - } - }, - "overrides": [ - { - "includes": ["apps/frontend/**/*"], - "linter": { - "domains": { - "react": "recommended" - } - } - }, - { - "includes": ["**/*.astro"], - "linter": { - "rules": { - "correctness": { - "noUnusedImports": "off" - } - } - }, - "assist": { - "actions": { - "source": { - "organizeImports": "off" - } - } - } - } - ], - "files": { - "includes": [ - "**", - "!**/dist", - "!**/build", - "!**/.wrangler", - "!**/.cache", - "!apps/frontend/src/components/ui/**/*.tsx", - "!apps/frontend/.astro/**" - ] - } -} diff --git a/bun.lock b/bun.lock index 73b391b..7ad9e6e 100644 --- a/bun.lock +++ b/bun.lock @@ -5,8 +5,15 @@ "": { "name": "bkio", "devDependencies": { - "@biomejs/biome": "^2.4.4", + "@antfu/eslint-config": "^7.7.0", "@bkio/tsconfig": "workspace:*", + "@eslint-react/eslint-plugin": "^2.13.0", + "eslint": "^10.0.3", + "eslint-plugin-astro": "^1.6.0", + "eslint-plugin-format": "^2.0.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "prettier": "^3.8.1", "typescript": "catalog:", "wrangler": "^4.71.0", }, @@ -86,6 +93,10 @@ "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], + "@antfu/eslint-config": ["@antfu/eslint-config@7.7.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@clack/prompts": "^1.1.0", "@e18e/eslint-plugin": "^0.2.0", "@eslint-community/eslint-plugin-eslint-comments": "^4.7.1", "@eslint/markdown": "^7.5.1", "@stylistic/eslint-plugin": "^5.10.0", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", "@vitest/eslint-plugin": "^1.6.9", "ansis": "^4.2.0", "cac": "^7.0.0", "eslint-config-flat-gitignore": "^2.2.1", "eslint-flat-config-utils": "^3.0.1", "eslint-merge-processors": "^2.0.0", "eslint-plugin-antfu": "^3.2.2", "eslint-plugin-command": "^3.5.2", "eslint-plugin-import-lite": "^0.5.2", "eslint-plugin-jsdoc": "^62.7.1", "eslint-plugin-jsonc": "^3.1.1", "eslint-plugin-n": "^17.24.0", "eslint-plugin-no-only-tests": "^3.3.0", "eslint-plugin-perfectionist": "^5.6.0", "eslint-plugin-pnpm": "^1.6.0", "eslint-plugin-regexp": "^3.0.0", "eslint-plugin-toml": "^1.3.1", "eslint-plugin-unicorn": "^63.0.0", "eslint-plugin-unused-imports": "^4.4.1", "eslint-plugin-vue": "^10.8.0", "eslint-plugin-yml": "^3.3.1", "eslint-processor-vue-blocks": "^2.0.0", "globals": "^17.4.0", "local-pkg": "^1.1.2", "parse-gitignore": "^2.0.0", "toml-eslint-parser": "^1.0.3", "vue-eslint-parser": "^10.4.0", "yaml-eslint-parser": "^2.0.0" }, "peerDependencies": { "@angular-eslint/eslint-plugin": "^21.1.0", "@angular-eslint/eslint-plugin-template": "^21.1.0", "@angular-eslint/template-parser": "^21.1.0", "@eslint-react/eslint-plugin": "^2.11.0", "@next/eslint-plugin-next": ">=15.0.0", "@prettier/plugin-xml": "^3.4.1", "@unocss/eslint-plugin": ">=0.50.0", "astro-eslint-parser": "^1.0.2", "eslint": "^9.10.0 || ^10.0.0", "eslint-plugin-astro": "^1.2.0", "eslint-plugin-format": ">=0.1.0", "eslint-plugin-jsx-a11y": ">=6.10.2", "eslint-plugin-react-hooks": "^7.0.0", "eslint-plugin-react-refresh": "^0.5.0", "eslint-plugin-solid": "^0.14.3", "eslint-plugin-svelte": ">=2.35.1", "eslint-plugin-vuejs-accessibility": "^2.4.1", "prettier-plugin-astro": "^0.14.0", "prettier-plugin-slidev": "^1.0.5", "svelte-eslint-parser": ">=0.37.0" }, "optionalPeers": ["@angular-eslint/eslint-plugin", "@angular-eslint/eslint-plugin-template", "@angular-eslint/template-parser", "@eslint-react/eslint-plugin", "@next/eslint-plugin-next", "@prettier/plugin-xml", "@unocss/eslint-plugin", "astro-eslint-parser", "eslint-plugin-astro", "eslint-plugin-format", "eslint-plugin-jsx-a11y", "eslint-plugin-react-hooks", "eslint-plugin-react-refresh", "eslint-plugin-solid", "eslint-plugin-svelte", "eslint-plugin-vuejs-accessibility", "prettier-plugin-astro", "prettier-plugin-slidev", "svelte-eslint-parser"], "bin": { "eslint-config": "bin/index.mjs" } }, "sha512-lkxb84o8z4v1+me51XlrHHF6zvOZfvTu6Y11t6h6v17JSMl9yoNHwC0Sqp/NfMTHie/LGgjyXOupXpQCXxfs1Q=="], + + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + "@antfu/ni": ["@antfu/ni@25.0.0", "", { "dependencies": { "ansis": "^4.0.0", "fzf": "^0.5.2", "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" }, "bin": { "na": "bin/na.mjs", "ni": "bin/ni.mjs", "nr": "bin/nr.mjs", "nci": "bin/nci.mjs", "nlx": "bin/nlx.mjs", "nun": "bin/nun.mjs", "nup": "bin/nup.mjs" } }, "sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA=="], "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "lru-cache": "^11.2.6" } }, "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw=="], @@ -172,24 +183,6 @@ "@base-ui/utils": ["@base-ui/utils@0.2.5", "", { "dependencies": { "@babel/runtime": "^7.28.6", "@floating-ui/utils": "^0.2.10", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-oYC7w0gp76RI5MxprlGLV0wze0SErZaRl3AAkeP3OnNB/UBMb6RqNf6ZSIlxOc9Qp68Ab3C2VOcJQyRs7Xc7Vw=="], - "@biomejs/biome": ["@biomejs/biome@2.4.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.6", "@biomejs/cli-darwin-x64": "2.4.6", "@biomejs/cli-linux-arm64": "2.4.6", "@biomejs/cli-linux-arm64-musl": "2.4.6", "@biomejs/cli-linux-x64": "2.4.6", "@biomejs/cli-linux-x64-musl": "2.4.6", "@biomejs/cli-win32-arm64": "2.4.6", "@biomejs/cli-win32-x64": "2.4.6" }, "bin": { "biome": "bin/biome" } }, "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ=="], - - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ=="], - - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw=="], - - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew=="], - - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A=="], - - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw=="], - - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.6", "", { "os": "linux", "cpu": "x64" }, "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg=="], - - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg=="], - - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.6", "", { "os": "win32", "cpu": "x64" }, "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg=="], - "@bkio/api": ["@bkio/api@workspace:apps/api"], "@bkio/frontend": ["@bkio/frontend@workspace:apps/frontend"], @@ -202,6 +195,10 @@ "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], + "@clack/core": ["@clack/core@1.1.0", "", { "dependencies": { "sisteransi": "^1.0.5" } }, "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA=="], + + "@clack/prompts": ["@clack/prompts@1.1.0", "", { "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.15.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-EGYmJaGZKWl+X8tXxcnx4v2bOZSjQeNI5dWFeXivgX9+YCT69AkzHHwlNbVpqtEUTbew8eQurpyOpeN8fg00nw=="], @@ -234,10 +231,22 @@ "@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.54.1", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.2", "which": "^4.0.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-41gU3q7v05GM92QPuPUf4CmUw+mmF8p4wLUh6MCRlxpCkJ9ByLcY9jUf6MwrMNmiKyG/rIckNxj9SCfmNCmCqw=="], + "@dprint/formatter": ["@dprint/formatter@0.5.1", "", {}, "sha512-cdZUrm0iv/FnnY3CKE2dEcVhNEzrC551aE2h2mTFwQCRBrqyARLDnb7D+3PlXTUVp3s34ftlnGOVCmhLT9DeKA=="], + + "@dprint/markdown": ["@dprint/markdown@0.21.1", "", {}, "sha512-XbZ/R7vRrBaZFYXG6vAvLvtaMVXHu8XB+xwie7OYrG+dPoGDP8UADGirIbzUyX8TmrAZcl6QBmalipTGlpzRmQ=="], + + "@dprint/toml": ["@dprint/toml@0.7.0", "", {}, "sha512-eFaQTcfxKHB+YyTh83x7GEv+gDPuj9q5NFOTaoj5rZmQTbj6OgjjMxUicmS1R8zYcx8YAq5oA9J3YFa5U6x2gA=="], + + "@e18e/eslint-plugin": ["@e18e/eslint-plugin@0.2.0", "", { "dependencies": { "eslint-plugin-depend": "^1.4.0" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0", "oxlint": "^1.41.0" }, "optionalPeers": ["eslint", "oxlint"] }, "sha512-mXgODVwhuDjTJ+UT+XSvmMmCidtGKfrV5nMIv1UtpWex2pYLsIM3RSpT8HWIMAebS9qANbXPKlSX4BE7ZvuCgA=="], + "@ecies/ciphers": ["@ecies/ciphers@0.2.5", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A=="], "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + "@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.84.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.54.0", "comment-parser": "1.4.5", "esquery": "^1.7.0", "jsdoc-type-pratt-parser": "~7.1.1" } }, "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w=="], + + "@es-joy/resolve.exports": ["@es-joy/resolve.exports@1.2.0", "", {}, "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], @@ -290,6 +299,38 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], + "@eslint-community/eslint-plugin-eslint-comments": ["@eslint-community/eslint-plugin-eslint-comments@4.7.1", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "ignore": "^7.0.5" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "sha512-Ql2nJFwA8wUGpILYGOQaT1glPsmvEwE0d+a+l7AALLzQvInqdbXJdx7aSu0DpUX9dB1wMVBMhm99/++S3MdEtQ=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint-react/ast": ["@eslint-react/ast@2.13.0", "", { "dependencies": { "@eslint-react/eff": "2.13.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/typescript-estree": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "string-ts": "^2.3.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-43+5gmqV3MpatTzKnu/V2i/jXjmepvwhrb9MaGQvnXHQgq9J7/C7VVCCcwp6Rvp2QHAFquAAdvQDSL8IueTpeA=="], + + "@eslint-react/core": ["@eslint-react/core@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m62XDzkf1hpzW4sBc7uh7CT+8rBG2xz/itSADuEntlsg4YA7Jhb8hjU6VHf3wRFDwyfx5VnbV209sbJ7Azey0Q=="], + + "@eslint-react/eff": ["@eslint-react/eff@2.13.0", "", {}, "sha512-rEH2R8FQnUAblUW+v3ZHDU1wEhatbL1+U2B1WVuBXwSKqzF7BGaLqCPIU7o9vofumz5MerVfaCtJgI8jYe2Btg=="], + + "@eslint-react/eslint-plugin": ["@eslint-react/eslint-plugin@2.13.0", "", { "dependencies": { "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/type-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "eslint-plugin-react-dom": "2.13.0", "eslint-plugin-react-hooks-extra": "2.13.0", "eslint-plugin-react-naming-convention": "2.13.0", "eslint-plugin-react-rsc": "2.13.0", "eslint-plugin-react-web-api": "2.13.0", "eslint-plugin-react-x": "2.13.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-iaMXpqnJCTW7317hg8L4wx7u5aIiPzZ+d1p59X8wXFgMHzFX4hNu4IfV8oygyjmWKdLsjKE9sEpv/UYWczlb+A=="], + + "@eslint-react/shared": ["@eslint-react/shared@2.13.0", "", { "dependencies": { "@eslint-react/eff": "2.13.0", "@typescript-eslint/utils": "^8.55.0", "ts-pattern": "^5.9.0", "zod": "^3.25.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-IOloCqrZ7gGBT4lFf9+0/wn7TfzU7JBRjYwTSyb9SDngsbeRrtW95ZpgUpS8/jen1wUEm6F08duAooTZ2FtsWA=="], + + "@eslint-react/var": ["@eslint-react/var@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-dM+QaeiHR16qPQoJYg205MkdHYSWVa2B7ore5OFpOPlSwqDV3tLW7I+475WjbK7potq5QNPTxRa7VLp9FGeQqA=="], + + "@eslint/compat": ["@eslint/compat@2.0.3", "", { "dependencies": { "@eslint/core": "^1.1.1" }, "peerDependencies": { "eslint": "^8.40 || 9 || 10" }, "optionalPeers": ["eslint"] }, "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw=="], + + "@eslint/config-array": ["@eslint/config-array@0.23.3", "", { "dependencies": { "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.5.3", "", { "dependencies": { "@eslint/core": "^1.1.1" } }, "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw=="], + + "@eslint/core": ["@eslint/core@1.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ=="], + + "@eslint/markdown": ["@eslint/markdown@7.5.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "@eslint/plugin-kit": "^0.4.1", "github-slugger": "^2.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.1.0", "micromark-extension-frontmatter": "^2.0.0", "micromark-extension-gfm": "^3.0.0", "micromark-util-normalize-identifier": "^2.0.1" } }, "sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ=="], + + "@eslint/object-schema": ["@eslint/object-schema@3.0.3", "", {}, "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.6.1", "", { "dependencies": { "@eslint/core": "^1.1.1", "levn": "^0.4.1" } }, "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ=="], + "@exodus/bytes": ["@exodus/bytes@1.15.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="], "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], @@ -304,6 +345,14 @@ "@hono/zod-validator": ["@hono/zod-validator@0.7.6", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -372,7 +421,7 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], @@ -398,6 +447,48 @@ "@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="], + "@ota-meshi/ast-token-store": ["@ota-meshi/ast-token-store@0.3.0", "", {}, "sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg=="], + + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.35.0", "", { "os": "android", "cpu": "arm" }, "sha512-BaRKlM3DyG81y/xWTsE6gZiv89F/3pHe2BqX2H4JbiB8HNVlWWtplzgATAE5IDSdwChdeuWLDTQzJ92Lglw3ZA=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.35.0", "", { "os": "android", "cpu": "arm64" }, "sha512-/O+EbuAJYs6nde/anv+aID6uHsGQApyE9JtYBo/79KyU8e6RBN3DMbT0ix97y1SOnCglurmL2iZ+hlohjP2PnQ=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.35.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pGqRtqlNdn9d4VrmGUWVyQjkw79ryhI6je9y2jfqNUIZCfqceob+R97YYAoG7C5TFyt8ILdLVoN+L2vw/hSFyA=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.35.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8GmsDcSozTPjrCJeGpp+sCmS9+9V5yRrdEZ1p/sTWxPG5nYeAfSLuS0nuEYjXSO+CtdSbStIW6dxa+4NM58yRw=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.35.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QyfKfTe0ytHpFKHAcHCGQEzN45QSqq1AHJOYYxQMgLM3KY4xu8OsXHpCnINjDsV4XGnQzczJDU9e04Zmd8XqIQ=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-u+kv3JD6P3J38oOyUaiCqgY5TNESzBRZJ5lyZQ6c2czUW2v5SIN9E/KWWa9vxoc+P8AFXQFUVrdzGy1tK+nbPQ=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.35.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1NiZroCiV57I7Pf8kOH4XGR366kW5zir3VfSMBU2D0V14GpYjiYmPYFAoJboZvp8ACnZKUReWyMkNKSa5ad58A=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7Q0Xeg7ZnW2nxnZ4R7aF6DEbCFls4skgHZg+I63XitpNvJCbVIU8MFOTZlvZGRsY9+rPgWPQGeUpLHlyx0wvMA=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.35.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Okqi+uhYFxwKz8hcnUftNNwdm8BCkf6GSCbcz9xJxYMm87k1E4p7PEmAAbhLTk7cjSdDre6TDL0pDzNX+Y22Q=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.35.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9k66pbZQXM/lBJWys3Xbc5yhl4JexyfqkEf/tvtq8976VIJnLAAL3M127xHA3ifYSqxdVHfVGTg84eiBHCGcNw=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-aUcY9ofKPtjO52idT6t0SAQvEF6ctjzUQa1lLp7GDsRpSBvuTrBQGeq0rYKz3gN8dMIQ7mtMdGD9tT4LhR8jAQ=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.35.0", "", { "os": "linux", "cpu": "none" }, "sha512-C6yhY5Hvc2sGM+mCPek9ZLe5xRUOC/BvhAt2qIWFAeXMn4il04EYIjl3DsWiJr0xDMTJhvMOmD55xTRPlNp39w=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.35.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-RG2hlvOMK4OMZpO3mt8MpxLQ0AAezlFqhn5mI/g5YrVbPFyoCv9a34AAvbSJS501ocOxlFIRcKEuw5hFvddf9g=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-wzmh90Pwvqj9xOKHJjkQYBpydRkaXG77ZvDz+iFDRRQpnqIEqGm5gmim2s6vnZIkDGsvKCuTdtxm0GFmBjM1+w=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.35.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+HCqYCJPCUy5I+b2cf+gUVaApfgtoQT3HdnSg/l7NIcLHOhKstlYaGyrFZLmUpQt4WkFbpGKZZayG6zjRU0KFA=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.35.0", "", { "os": "none", "cpu": "arm64" }, "sha512-kFYmWfR9YL78XyO5ws+1dsxNvZoD973qfVMNFOS4e9bcHXGF7DvGC2tY5UDFwyMCeB33t3sDIuGONKggnVNSJA=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.35.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-uD/NGdM65eKNCDGyTGdO8e9n3IHX+wwuorBvEYrPJXhDXL9qz6gzddmXH8EN04ejUXUujlq4FsoSeCfbg0Y+Jg=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.35.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-oSRD2k8J2uxYDEKR2nAE/YTY9PobOEnhZgCmspHu0+yBQ665yH8lFErQVSTE7fcGJmJp/cC6322/gc8VFuQf7g=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.35.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WCDJjlS95NboR0ugI2BEwzt1tYvRDorDRM9Lvctls1SLyKYuNRCyrPwp1urUPFBnwgBNn9p2/gnmo7gFMySRoQ=="], + + "@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], + "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], @@ -476,6 +567,8 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@sindresorhus/base62": ["@sindresorhus/base62@1.0.0", "", {}, "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA=="], + "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], @@ -484,6 +577,8 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.10.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/types": "^8.56.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ=="], + "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="], @@ -538,10 +633,14 @@ "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], @@ -560,10 +659,34 @@ "@types/validate-npm-package-name": ["@types/validate-npm-package-name@4.0.2", "", {}, "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w=="], + + "@typescript-eslint/rule-tester": ["@typescript-eslint/rule-tester@8.57.0", "", { "dependencies": { "@typescript-eslint/parser": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "ajv": "^6.12.6", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "4.6.2", "semver": "^7.7.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, "sha512-qs4OapXmAIj3so85/20lQG1WrBSSvDE/3b42Orl3lpZkaOlNXtbfKzL+9EPaY5wSEgdlhKEpympAMFHPG9i72Q=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" } }, "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.57.0", "", {}, "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@vitest/eslint-plugin": ["@vitest/eslint-plugin@1.6.10", "", { "dependencies": { "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/utils": "^8.55.0" }, "peerDependencies": { "eslint": ">=8.57.0", "typescript": ">=5.0.0", "vitest": "*" }, "optionalPeers": ["typescript", "vitest"] }, "sha512-/cOf+mTu4HBJIYHTETo8/OFCSZv3T2p+KfGnouzKfjK063cWLZp0TzvK7EU5B3eFG7ypUNtw6l+jK+SA+p1g8g=="], + "@vitest/expect": ["@vitest/expect@4.0.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="], "@vitest/mocker": ["@vitest/mocker@4.0.18", "", { "dependencies": { "@vitest/spy": "4.0.18", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ=="], @@ -578,13 +701,25 @@ "@vitest/utils": ["@vitest/utils@4.0.18", "", { "dependencies": { "@vitest/pretty-format": "4.0.18", "tinyrainbow": "^3.0.3" } }, "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.30", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.30", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.30", "", { "dependencies": { "@vue/compiler-core": "3.5.30", "@vue/shared": "3.5.30" } }, "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.30", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.30", "@vue/compiler-dom": "3.5.30", "@vue/compiler-ssr": "3.5.30", "@vue/shared": "3.5.30", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.8", "source-map-js": "^1.2.1" } }, "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.30", "", { "dependencies": { "@vue/compiler-dom": "3.5.30", "@vue/shared": "3.5.30" } }, "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA=="], + + "@vue/shared": ["@vue/shared@3.5.30", "", {}, "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -598,6 +733,8 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "are-docs-informative": ["are-docs-informative@0.0.2", "", {}, "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], @@ -610,6 +747,10 @@ "astro": ["astro@5.18.0", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.3", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ=="], + "astro-eslint-parser": ["astro-eslint-parser@1.3.0", "", { "dependencies": { "@astrojs/compiler": "^2.0.0", "@typescript-eslint/scope-manager": "^7.0.0 || ^8.0.0", "@typescript-eslint/types": "^7.0.0 || ^8.0.0", "astrojs-compiler-sync": "^1.0.0", "debug": "^4.3.4", "entities": "^6.0.0", "eslint-scope": "^8.0.1", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "fast-glob": "^3.3.3", "is-glob": "^4.0.3", "semver": "^7.3.8" } }, "sha512-aOLc/aDR7lTWAHlytEefwn4Y6qs6uMr69DZvUx2A1AOAZsWhGB/paiRWPtVchh9wzMvLeqr+DkbENhVreVr9AQ=="], + + "astrojs-compiler-sync": ["astrojs-compiler-sync@1.1.1", "", { "dependencies": { "synckit": "^0.11.0" }, "peerDependencies": { "@astrojs/compiler": ">=0.27.0" } }, "sha512-0mKvB9sDQRIZPsEJadw6OaFbGJ92cJPPR++ICca9XEyiUAZqgVuk25jNmzHPT0KF80rI94trSZrUR5iHFXGGOQ=="], + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], @@ -622,6 +763,8 @@ "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], + "birecord": ["birecord@0.1.1", "", {}, "sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw=="], + "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], @@ -636,10 +779,14 @@ "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + "builtin-modules": ["builtin-modules@5.0.0", "", {}, "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], @@ -656,6 +803,8 @@ "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "change-case": ["change-case@5.4.4", "", {}, "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w=="], + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], @@ -668,6 +817,8 @@ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "clean-regexp": ["clean-regexp@1.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw=="], + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], @@ -690,8 +841,14 @@ "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + "comment-parser": ["comment-parser@1.4.5", "", {}, "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw=="], + "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], + + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], @@ -706,6 +863,8 @@ "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "core-js-compat": ["core-js-compat@3.48.0", "", { "dependencies": { "browserslist": "^4.28.1" } }, "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], "cosmiconfig": ["cosmiconfig@9.0.1", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ=="], @@ -742,6 +901,8 @@ "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], @@ -768,6 +929,8 @@ "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], + "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], @@ -794,6 +957,8 @@ "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], "enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="], @@ -820,12 +985,98 @@ "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@10.0.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.2", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.1.1", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ=="], + + "eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="], + + "eslint-config-flat-gitignore": ["eslint-config-flat-gitignore@2.2.1", "", { "dependencies": { "@eslint/compat": "^2.0.2" }, "peerDependencies": { "eslint": "^9.5.0 || ^10.0.0" } }, "sha512-wA5EqN0era7/7Gt5Botlsfin/UNY0etJSEeBgbUlFLFrBi47rAN//+39fI7fpYcl8RENutlFtvp/zRa/M/pZNg=="], + + "eslint-flat-config-utils": ["eslint-flat-config-utils@3.0.2", "", { "dependencies": { "@eslint/config-helpers": "^0.5.3", "pathe": "^2.0.3" } }, "sha512-mPvevWSDQFwgABvyCurwIu6ZdKxGI5NW22/BGDwA1T49NO6bXuxbV9VfJK/tkQoNyPogT6Yu1d57iM0jnZVWmg=="], + + "eslint-formatting-reporter": ["eslint-formatting-reporter@0.0.0", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw=="], + + "eslint-json-compat-utils": ["eslint-json-compat-utils@0.2.2", "", { "dependencies": { "esquery": "^1.6.0" }, "peerDependencies": { "eslint": "*", "jsonc-eslint-parser": "^2.4.0 || ^3.0.0" } }, "sha512-KcTUifi8VSSHkrOY0FzB7smuTZRU9T2nCrcCy6k2b+Q77+uylBQVIxN4baVCIWvWJEpud+IsrYgco4JJ6io05g=="], + + "eslint-merge-processors": ["eslint-merge-processors@2.0.0", "", { "peerDependencies": { "eslint": "*" } }, "sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA=="], + + "eslint-parser-plain": ["eslint-parser-plain@0.1.1", "", {}, "sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw=="], + + "eslint-plugin-antfu": ["eslint-plugin-antfu@3.2.2", "", { "peerDependencies": { "eslint": "*" } }, "sha512-Qzixht2Dmd/pMbb5EnKqw2V8TiWHbotPlsORO8a+IzCLFwE0RxK8a9k4DCTFPzBwyxJzH+0m2Mn8IUGeGQkyUw=="], + + "eslint-plugin-astro": ["eslint-plugin-astro@1.6.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@jridgewell/sourcemap-codec": "^1.4.14", "@typescript-eslint/types": "^7.7.1 || ^8", "astro-eslint-parser": "^1.3.0", "eslint-compat-utils": "^0.6.0", "globals": "^16.0.0", "postcss": "^8.4.14", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "eslint": ">=8.57.0" } }, "sha512-yGIbLHuj5MOUXa0s4sZ6cVhv6ehb+WLF80tsrGaxMk6VTUExruMzubQDzhOYt8fbR1c9vILCCRSCsKI7M1whig=="], + + "eslint-plugin-command": ["eslint-plugin-command@3.5.2", "", { "dependencies": { "@es-joy/jsdoccomment": "^0.84.0" }, "peerDependencies": { "@typescript-eslint/rule-tester": "*", "@typescript-eslint/typescript-estree": "*", "@typescript-eslint/utils": "*", "eslint": "*" } }, "sha512-PA59QAkQDwvcCMEt5lYLJLI3zDGVKJeC4id/pcRY2XdRYhSGW7iyYT1VC1N3bmpuvu6Qb/9QptiS3GJMjeGTJg=="], + + "eslint-plugin-depend": ["eslint-plugin-depend@1.5.0", "", { "dependencies": { "empathic": "^2.0.0", "module-replacements": "^2.10.1", "semver": "^7.6.3" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A=="], + + "eslint-plugin-es-x": ["eslint-plugin-es-x@7.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", "@eslint-community/regexpp": "^4.11.0", "eslint-compat-utils": "^0.5.1" }, "peerDependencies": { "eslint": ">=8" } }, "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ=="], + + "eslint-plugin-format": ["eslint-plugin-format@2.0.1", "", { "dependencies": { "@dprint/formatter": "^0.5.1", "@dprint/markdown": "^0.21.1", "@dprint/toml": "^0.7.0", "eslint-formatting-reporter": "^0.0.0", "eslint-parser-plain": "^0.1.1", "ohash": "^2.0.11", "oxfmt": "^0.35.0", "prettier": "^3.8.1", "synckit": "^0.11.12" }, "peerDependencies": { "eslint": "^8.40.0 || ^9.0.0 || ^10.0.0" } }, "sha512-0BA65p5DAiuKtx5MmMJfPk9WaTjoHHbyVW7ZXRhaZoA1fdiMHhay9QRiDL2wr0hJWZxdF7CRThOK/70VUKVg2g=="], + + "eslint-plugin-import-lite": ["eslint-plugin-import-lite@0.5.2", "", { "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-XvfdWOC5dSLEI9krIPRlNmKSI2ViIE9pVylzfV9fCq0ZpDaNeUk6o0wZv0OzN83QdadgXp1NsY0qjLINxwYCsw=="], + + "eslint-plugin-jsdoc": ["eslint-plugin-jsdoc@62.7.1", "", { "dependencies": { "@es-joy/jsdoccomment": "~0.84.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.5", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", "espree": "^11.1.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "sha512-4Zvx99Q7d1uggYBUX/AIjvoyqXhluGbbKrRmG8SQTLprPFg6fa293tVJH1o1GQwNe3lUydd8ZHzn37OaSncgSQ=="], + + "eslint-plugin-jsonc": ["eslint-plugin-jsonc@3.1.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.1", "@eslint/core": "^1.0.1", "@eslint/plugin-kit": "^0.6.0", "@ota-meshi/ast-token-store": "^0.3.0", "diff-sequences": "^29.6.3", "eslint-json-compat-utils": "^0.2.1", "jsonc-eslint-parser": "^3.1.0", "natural-compare": "^1.4.0", "synckit": "^0.11.12" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-7TSQO8ZyvOuXWb0sYke3KUSh0DJA4/QviKfuzD3/Cy3XDjtrIrTWQbjb7j/Yy2l/DgwuM+lCS2c/jqJifv5jhg=="], + + "eslint-plugin-n": ["eslint-plugin-n@17.24.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", "globals": "^15.11.0", "globrex": "^0.1.2", "ignore": "^5.3.2", "semver": "^7.6.3", "ts-declaration-location": "^1.0.6" }, "peerDependencies": { "eslint": ">=8.23.0" } }, "sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw=="], + + "eslint-plugin-no-only-tests": ["eslint-plugin-no-only-tests@3.3.0", "", {}, "sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q=="], + + "eslint-plugin-perfectionist": ["eslint-plugin-perfectionist@5.6.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.56.0", "natural-orderby": "^5.0.0" }, "peerDependencies": { "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" } }, "sha512-pxrLrfRp5wl1Vol1fAEa/G5yTXxefTPJjz07qC7a8iWFXcOZNuWBItMQ2OtTzfQIvMq6bMyYcrzc3Wz++na55Q=="], + + "eslint-plugin-pnpm": ["eslint-plugin-pnpm@1.6.0", "", { "dependencies": { "empathic": "^2.0.0", "jsonc-eslint-parser": "^3.1.0", "pathe": "^2.0.3", "pnpm-workspace-yaml": "1.6.0", "tinyglobby": "^0.2.15", "yaml": "^2.8.2", "yaml-eslint-parser": "^2.0.0" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-dxmt9r3zvPaft6IugS4i0k16xag3fTbOvm/road5uV9Y8qUCQT0xzheSh3gMlYAlC6vXRpfArBDsTZ7H7JKCbg=="], + + "eslint-plugin-react-dom": ["eslint-plugin-react-dom@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/core": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "compare-versions": "^6.1.1", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-+2IZzQ1WEFYOWatW+xvNUqmZn55YBCufzKA7hX3XQ/8eu85Mp4vnlOyNvdVHEOGhUnGuC6+9+zLK+IlEHKdKLQ=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], + + "eslint-plugin-react-hooks-extra": ["eslint-plugin-react-hooks-extra@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/core": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/type-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qIbha1nzuyhXM9SbEfrcGVqmyvQu7GAOB2sy9Y4Qo5S8nCqw4fSBxq+8lSce5Tk5Y7XzIkgHOhNyXEvUHRWFMQ=="], + + "eslint-plugin-react-naming-convention": ["eslint-plugin-react-naming-convention@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/core": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/type-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "compare-versions": "^6.1.1", "string-ts": "^2.3.1", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-uSd25JzSg2R4p81s3Wqck0AdwRlO9Yc+cZqTEXv7vW8exGGAM3mWnF6hgrgdqVJqBEGJIbS/Vx1r5BdKcY/MHA=="], + + "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.5.2", "", { "peerDependencies": { "eslint": "^9 || ^10" } }, "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA=="], + + "eslint-plugin-react-rsc": ["eslint-plugin-react-rsc@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-RaftgITDLQm1zIgYyvR51sBdy4FlVaXFts5VISBaKbSUB0oqXyzOPxMHasfr9BCSjPLKus9zYe+G/Hr6rjFLXQ=="], + + "eslint-plugin-react-web-api": ["eslint-plugin-react-web-api@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/core": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "birecord": "^0.1.1", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-nmJbzIAte7PeAkp22CwcKEASkKi49MshSdiDGO1XuN3f4N4/8sBfDcWbQuLPde6JiuzDT/0+l7Gi8wwTHtR1kg=="], + + "eslint-plugin-react-x": ["eslint-plugin-react-x@2.13.0", "", { "dependencies": { "@eslint-react/ast": "2.13.0", "@eslint-react/core": "2.13.0", "@eslint-react/eff": "2.13.0", "@eslint-react/shared": "2.13.0", "@eslint-react/var": "2.13.0", "@typescript-eslint/scope-manager": "^8.55.0", "@typescript-eslint/type-utils": "^8.55.0", "@typescript-eslint/types": "^8.55.0", "@typescript-eslint/utils": "^8.55.0", "compare-versions": "^6.1.1", "is-immutable-type": "^5.0.1", "ts-api-utils": "^2.4.0", "ts-pattern": "^5.9.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-cMNX0+ws/fWTgVxn52qAQbaFF2rqvaDAtjrPUzY6XOzPjY0rJQdR2tSlWJttz43r2yBfqu+LGvHlGpWL2wfpTQ=="], + + "eslint-plugin-regexp": ["eslint-plugin-regexp@3.1.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "comment-parser": "^1.4.0", "jsdoc-type-pratt-parser": "^7.0.0", "refa": "^0.12.1", "regexp-ast-analysis": "^0.7.1", "scslre": "^0.3.0" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-qGXIC3DIKZHcK1H9A9+Byz9gmndY6TTSRkSMTZpNXdyCw2ObSehRgccJv35n9AdUakEjQp5VFNLas6BMXizCZg=="], + + "eslint-plugin-toml": ["eslint-plugin-toml@1.3.1", "", { "dependencies": { "@eslint/core": "^1.0.1", "@eslint/plugin-kit": "^0.6.0", "@ota-meshi/ast-token-store": "^0.3.0", "debug": "^4.1.1", "toml-eslint-parser": "^1.0.1" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-1l00fBP03HIt9IPV7ZxBi7x0y0NMdEZmakL1jBD6N/FoKBvfKxPw5S8XkmzBecOnFBTn5Z8sNJtL5vdf9cpRMQ=="], + + "eslint-plugin-unicorn": ["eslint-plugin-unicorn@63.0.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "@eslint-community/eslint-utils": "^4.9.0", "change-case": "^5.4.4", "ci-info": "^4.3.1", "clean-regexp": "^1.0.0", "core-js-compat": "^3.46.0", "find-up-simple": "^1.0.1", "globals": "^16.4.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.13.0", "semver": "^7.7.3", "strip-indent": "^4.1.1" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA=="], + + "eslint-plugin-unused-imports": ["eslint-plugin-unused-imports@4.4.1", "", { "peerDependencies": { "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" }, "optionalPeers": ["@typescript-eslint/eslint-plugin"] }, "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ=="], + + "eslint-plugin-vue": ["eslint-plugin-vue@10.8.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^7.1.0", "semver": "^7.6.3", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "@stylistic/eslint-plugin": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "vue-eslint-parser": "^10.0.0" }, "optionalPeers": ["@stylistic/eslint-plugin", "@typescript-eslint/parser"] }, "sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA=="], + + "eslint-plugin-yml": ["eslint-plugin-yml@3.3.1", "", { "dependencies": { "@eslint/core": "^1.0.1", "@eslint/plugin-kit": "^0.6.0", "@ota-meshi/ast-token-store": "^0.3.0", "debug": "^4.3.2", "diff-sequences": "^29.0.0", "escape-string-regexp": "5.0.0", "natural-compare": "^1.4.0", "yaml-eslint-parser": "^2.0.0" }, "peerDependencies": { "eslint": ">=9.38.0" } }, "sha512-isntsZchaTqDMNNkD+CakrgA/pdUoJ45USWBKpuqfAW1MCuw731xX/vrXfoJFZU3tTFr24nCbDYmDfT2+g4QtQ=="], + + "eslint-processor-vue-blocks": ["eslint-processor-vue-blocks@2.0.0", "", { "peerDependencies": { "@vue/compiler-sfc": "^3.3.0", "eslint": ">=9.0.0" } }, "sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q=="], + + "eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "espree": ["espree@11.2.0", "", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], @@ -842,34 +1093,56 @@ "express-rate-limit": ["express-rate-limit@8.3.0", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-KJzBawY6fB9FiZGdE/0aftepZ91YlaGIrV8vgblRM3J8X+dHx/aiowJWwkx6LIGyuqGiANsjSwwrbb8mifOJ4Q=="], + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + "fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "find-up-simple": ["find-up-simple@1.0.1", "", {}, "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.1", "", {}, "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ=="], + "flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], "fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="], "fontkitten": ["fontkitten@1.0.3", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw=="], + "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], @@ -900,9 +1173,15 @@ "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="], + + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -940,10 +1219,16 @@ "headers-polyfill": ["headers-polyfill@4.0.3", "", {}, "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ=="], + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + "hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="], "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], + "html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="], + "html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="], "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], @@ -966,7 +1251,9 @@ "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], - "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -978,6 +1265,8 @@ "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + "is-builtin-module": ["is-builtin-module@5.0.0", "", { "dependencies": { "builtin-modules": "^5.0.0" } }, "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA=="], + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], @@ -988,6 +1277,8 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-immutable-type": ["is-immutable-type@5.0.1", "", { "dependencies": { "@typescript-eslint/type-utils": "^8.0.0", "ts-api-utils": "^2.0.0", "ts-declaration-location": "^1.0.4" }, "peerDependencies": { "eslint": "*", "typescript": ">=4.7.4" } }, "sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg=="], + "is-in-ssh": ["is-in-ssh@1.0.0", "", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="], "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], @@ -1014,7 +1305,7 @@ "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], - "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], @@ -1024,24 +1315,36 @@ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "jsdoc-type-pratt-parser": ["jsdoc-type-pratt-parser@7.1.1", "", {}, "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA=="], + "jsdom": ["jsdom@28.1.0", "", { "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", "@bramus/specificity": "^2.4.2", "@exodus/bytes": "^1.11.0", "cssstyle": "^6.0.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "undici": "^7.21.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonc-eslint-parser": ["jsonc-eslint-parser@3.1.0", "", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^5.0.0", "semver": "^7.3.5" } }, "sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng=="], + "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="], "lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="], @@ -1068,6 +1371,12 @@ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -1094,6 +1403,8 @@ "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + "mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="], + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], @@ -1128,6 +1439,8 @@ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + "micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="], + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], @@ -1198,6 +1511,10 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "mlly": ["mlly@1.8.1", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ=="], + + "module-replacements": ["module-replacements@2.11.0", "", {}, "sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -1208,6 +1525,10 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "natural-orderby": ["natural-orderby@5.0.0", "", {}, "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], @@ -1232,6 +1553,8 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-deep-merge": ["object-deep-merge@2.0.0", "", {}, "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-treeify": ["object-treeify@1.1.33", "", {}, "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A=="], @@ -1254,12 +1577,18 @@ "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], "outvariant": ["outvariant@1.4.3", "", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="], + "oxfmt": ["oxfmt@0.35.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.35.0", "@oxfmt/binding-android-arm64": "0.35.0", "@oxfmt/binding-darwin-arm64": "0.35.0", "@oxfmt/binding-darwin-x64": "0.35.0", "@oxfmt/binding-freebsd-x64": "0.35.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.35.0", "@oxfmt/binding-linux-arm-musleabihf": "0.35.0", "@oxfmt/binding-linux-arm64-gnu": "0.35.0", "@oxfmt/binding-linux-arm64-musl": "0.35.0", "@oxfmt/binding-linux-ppc64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-gnu": "0.35.0", "@oxfmt/binding-linux-riscv64-musl": "0.35.0", "@oxfmt/binding-linux-s390x-gnu": "0.35.0", "@oxfmt/binding-linux-x64-gnu": "0.35.0", "@oxfmt/binding-linux-x64-musl": "0.35.0", "@oxfmt/binding-openharmony-arm64": "0.35.0", "@oxfmt/binding-win32-arm64-msvc": "0.35.0", "@oxfmt/binding-win32-ia32-msvc": "0.35.0", "@oxfmt/binding-win32-x64-msvc": "0.35.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-QYeXWkP+aLt7utt5SLivNIk09glWx9QE235ODjgcEZ3sd1VMaUBSpLymh6ZRCA76gD2rMP4bXanUz/fx+nLM9Q=="], + "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], @@ -1268,18 +1597,26 @@ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + "parse-gitignore": ["parse-gitignore@2.0.0", "", {}, "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog=="], + + "parse-imports-exports": ["parse-imports-exports@0.2.4", "", { "dependencies": { "parse-statements": "1.0.11" } }, "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ=="], + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], "parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="], "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + "parse-statements": ["parse-statements@1.0.11", "", {}, "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA=="], + "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], @@ -1294,12 +1631,24 @@ "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], + + "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], + + "pnpm-workspace-yaml": ["pnpm-workspace-yaml@1.6.0", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-uUy4dK3E11sp7nK+hnT7uAWfkBMe00KaUw8OG3NuNlYQoTk4sc9pcdIy1+XIP85v9Tvr02mK3JPaNNrP0QyRaw=="], + "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.1", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], @@ -1316,6 +1665,8 @@ "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="], @@ -1338,12 +1689,20 @@ "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + "refa": ["refa@0.12.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0" } }, "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g=="], + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + "regexp-ast-analysis": ["regexp-ast-analysis@0.7.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.1" } }, "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A=="], + + "regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="], + + "regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + "rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="], "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], @@ -1368,8 +1727,12 @@ "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + "reserved-identifiers": ["reserved-identifiers@1.2.0", "", {}, "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw=="], + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], "retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="], @@ -1400,6 +1763,8 @@ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scslre": ["scslre@0.3.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.0", "regexp-ast-analysis": "^0.7.0" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="], + "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -1444,6 +1809,12 @@ "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + + "spdx-expression-parse": ["spdx-expression-parse@4.0.0", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ=="], + + "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], @@ -1456,6 +1827,8 @@ "strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], + "string-ts": ["string-ts@2.3.1", "", {}, "sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw=="], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], @@ -1470,7 +1843,7 @@ "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], - "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + "strip-indent": ["strip-indent@4.1.1", "", {}, "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA=="], "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], @@ -1478,6 +1851,8 @@ "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + "synckit": ["synckit@0.11.12", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ=="], + "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], @@ -1498,6 +1873,8 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], "tldts": ["tldts@7.0.25", "", { "dependencies": { "tldts-core": "^7.0.25" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w=="], @@ -1506,8 +1883,12 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "toml-eslint-parser": ["toml-eslint-parser@1.0.3", "", { "dependencies": { "eslint-visitor-keys": "^5.0.0" } }, "sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw=="], + "tough-cookie": ["tough-cookie@6.0.0", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w=="], "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], @@ -1516,8 +1897,14 @@ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], + + "ts-declaration-location": ["ts-declaration-location@1.0.7", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="], + "ts-morph": ["ts-morph@26.0.0", "", { "dependencies": { "@ts-morph/common": "~0.27.0", "code-block-writer": "^13.0.3" } }, "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug=="], + "ts-pattern": ["ts-pattern@5.9.0", "", {}, "sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg=="], + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], @@ -1526,6 +1913,8 @@ "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + "type-fest": ["type-fest@5.4.4", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw=="], "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], @@ -1578,6 +1967,8 @@ "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -1598,6 +1989,8 @@ "vitest": ["vitest@4.0.18", "", { "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", "@vitest/pretty-format": "4.0.18", "@vitest/runner": "4.0.18", "@vitest/snapshot": "4.0.18", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], + "vue-eslint-parser": ["vue-eslint-parser@10.4.0", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0 || ^9.0.0", "eslint-visitor-keys": "^4.2.0 || ^5.0.0", "espree": "^10.3.0 || ^11.0.0", "esquery": "^1.6.0", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, "sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg=="], + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], @@ -1610,7 +2003,7 @@ "whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], - "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], @@ -1618,6 +2011,8 @@ "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + "workerd": ["workerd@1.20260301.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260301.1", "@cloudflare/workerd-darwin-arm64": "1.20260301.1", "@cloudflare/workerd-linux-64": "1.20260301.1", "@cloudflare/workerd-linux-arm64": "1.20260301.1", "@cloudflare/workerd-windows-64": "1.20260301.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-oterQ1IFd3h7PjCfT4znSFOkJCvNQ6YMOyZ40YsnO3nrSpgB4TbJVYWFOnyJAw71/RQuupfVqZZWKvsy8GO3fw=="], "wrangler": ["wrangler@4.71.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.15.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260301.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260226.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-j6pSGAncOLNQDRzqtp0EqzYj52CldDP7uz/C9cxVrIgqa5p+cc0b4pIwnapZZAGv9E1Loa3tmPD0aXonH7KTkw=="], @@ -1640,6 +2035,10 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + + "yaml-eslint-parser": ["yaml-eslint-parser@2.0.0", "", { "dependencies": { "eslint-visitor-keys": "^5.0.0", "yaml": "^2.0.0" } }, "sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw=="], + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -1662,6 +2061,8 @@ "zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="], + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], "@astrojs/markdown-remark/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], @@ -1670,26 +2071,38 @@ "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-create-class-features-plugin/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "@dotenvx/dotenvx/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], + "@dotenvx/dotenvx/which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + + "@eslint-community/eslint-plugin-eslint-comments/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/markdown/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/markdown/@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], - "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@modelcontextprotocol/sdk/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@stylistic/eslint-plugin/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "@stylistic/eslint-plugin/espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], @@ -1706,6 +2119,16 @@ "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + + "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "ajv-formats/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1716,22 +2139,42 @@ "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "astro-eslint-parser/eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "astro-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "astro-eslint-parser/espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + "boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "clean-regexp/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "eslint-plugin-astro/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], + + "eslint-plugin-es-x/eslint-compat-utils": ["eslint-compat-utils@0.5.1", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q=="], + + "eslint-plugin-n/globals": ["globals@15.15.0", "", {}, "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg=="], + + "eslint-plugin-unicorn/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], + + "eslint-plugin-vue/xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="], + + "eslint-plugin-yml/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], "hast-util-from-html/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], @@ -1740,14 +2183,26 @@ "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "miniflare/undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "redent/indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "redent/strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], @@ -1790,12 +2245,18 @@ "@dotenvx/dotenvx/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "@inquirer/core/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@inquirer/core/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "@inquirer/core/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -1818,12 +2279,14 @@ "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], diff --git a/docs/architecture.md b/docs/architecture.md index a9ce18a..1b1d61b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -24,5 +24,5 @@ packages/ - **Single deployment**: Frontend and API deploy as one Cloudflare Worker to simplify infrastructure. - **Bun workspaces**: Monorepo managed by Bun with `catalog:` for shared dependency versions. -- **Biome**: Single tool for linting and formatting (replaces ESLint + Prettier). +- **ESLint + @antfu/eslint-config**: Single lint and formatting workflow via ESLint flat config. - **shadcn + Base UI**: shadcn (base-nova style) for styled components, Base UI for unstyled primitives. diff --git a/docs/changelog.md b/docs/changelog.md index d7c6e2b..e85cbd3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,7 +7,7 @@ Project initialized with monorepo template: - apps/frontend: Vite + React 19 + Tailwind v4 + shadcn + Base UI - packages/tsconfig + packages/shared - GitHub Actions deploy workflow (manual trigger) -- Biome for lint/format, Vitest for testing +- ESLint-based lint/format workflow, Vitest for testing ## 2026-03-07 03:45 [decision] diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..7287605 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,69 @@ +import antfu from '@antfu/eslint-config' + +export default antfu( + { + astro: true, + react: true, + typescript: true, + markdown: false, + yaml: false, + toml: false, + formatters: { + css: true, + }, + stylistic: { + indent: 2, + quotes: 'single', + semi: false, + }, + ignores: [ + '**/dist', + '**/build', + '**/.wrangler', + '**/.cache', + 'apps/frontend/.astro/**', + 'apps/frontend/src/components/ui/**/*.tsx', + ], + }, + { + rules: { + 'no-restricted-imports': ['error', { + paths: [{ + name: 'zod', + importNames: ['z'], + message: 'Use `import * as z from "zod"` instead of `import { z }`.', + }], + }], + 'unused-imports/no-unused-imports': 'warn', + 'no-console': 'off', + 'no-debugger': 'error', + 'antfu/no-top-level-await': 'off', + 'antfu/if-newline': 'off', + 'e18e/prefer-array-from-map': 'off', + 'e18e/prefer-static-regex': 'off', + 'jsonc/sort-keys': 'off', + 'node/prefer-global/process': 'off', + 'prefer-arrow-callback': 'off', + 'react-dom/no-dangerously-set-innerhtml': 'off', + 'react-naming-convention/use-state': 'off', + 'react-hooks-extra/no-direct-set-state-in-use-effect': 'off', + 'style/brace-style': ['error', '1tbs', { allowSingleLine: true }], + 'style/no-tabs': 'error', + 'style/operator-linebreak': 'off', + 'style/multiline-ternary': 'off', + 'style/quote-props': 'off', + 'perfectionist/sort-imports': 'off', + 'perfectionist/sort-named-imports': 'off', + 'perfectionist/sort-exports': 'off', + 'perfectionist/sort-named-exports': 'off', + 'ts/consistent-type-definitions': 'off', + 'unicorn/escape-case': 'off', + }, + }, + { + files: ['**/*.astro'], + rules: { + 'unused-imports/no-unused-imports': 'off', + }, + }, +) diff --git a/package.json b/package.json index 74e7d4f..22b7250 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bkio", - "private": true, "type": "module", + "private": true, "workspaces": { "packages": [ "apps/*", @@ -19,13 +19,20 @@ "build:frontend": "bun --filter @bkio/frontend build", "deploy": "bun run build && wrangler deploy", "test": "bun --parallel --filter '*' test", - "lint": "biome check .", - "lint:fix": "biome check . --fix", - "format": "biome format . --write" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "eslint . --fix" }, "devDependencies": { - "@biomejs/biome": "^2.4.4", + "@antfu/eslint-config": "^7.7.0", "@bkio/tsconfig": "workspace:*", + "@eslint-react/eslint-plugin": "^2.13.0", + "eslint": "^10.0.3", + "eslint-plugin-astro": "^1.6.0", + "eslint-plugin-format": "^2.0.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "prettier": "^3.8.1", "typescript": "catalog:", "wrangler": "^4.71.0" } From e94742563980a99ca4318044ce0fb4459c5f8f9d Mon Sep 17 00:00:00 2001 From: roy <62500486+aazf@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:01:57 +0000 Subject: [PATCH 4/5] feat: simplify tool taxonomy and content paths --- .../src/components/ToolDetailPage.astro | 72 +++--- .../src/components/ToolDirectoryPage.astro | 18 +- apps/frontend/src/components/ToolSearch.tsx | 74 ++---- apps/frontend/src/i18n/en.ts | 8 +- apps/frontend/src/i18n/types.ts | 8 +- apps/frontend/src/i18n/zh.ts | 8 +- apps/frontend/src/lib/content.ts | 59 +++-- apps/frontend/src/lib/routes.ts | 1 - apps/frontend/src/pages/[...slug].astro | 2 +- .../src/pages/[locale]/[...slug].astro | 2 +- .../pages/[locale]/category/[category].astro | 21 -- .../src/pages/[locale]/template.astro | 15 +- .../src/pages/category/[category].astro | 21 -- apps/frontend/src/pages/template.astro | 15 +- apps/frontend/src/styles/global.css | 6 +- content/demo/configuration.md | 10 - content/demo/index.md | 92 ------- content/demo/logo.svg | 6 - content/demo/meta.json | 22 -- content/demo/troubleshooting.md | 10 - content/demo/usage.md | 233 ------------------ content/list.json | 42 ++-- .../chrome}/en.configuration.md | 0 .../{zzci-chrome => zzci/chrome}/en.data.md | 0 .../{zzci-chrome => zzci/chrome}/en.faq.md | 0 .../{zzci-chrome => zzci/chrome}/en.index.md | 0 .../chrome}/en.installation.md | 0 .../{zzci-chrome => zzci/chrome}/meta.json | 0 .../chrome}/zh.configuration.md | 0 .../{zzci-chrome => zzci/chrome}/zh.data.md | 0 .../{zzci-chrome => zzci/chrome}/zh.faq.md | 0 .../{zzci-chrome => zzci/chrome}/zh.index.md | 0 .../chrome}/zh.installation.md | 0 .../traefik}/en.configuration.md | 0 .../{zzci-traefik => zzci/traefik}/en.data.md | 0 .../{zzci-traefik => zzci/traefik}/en.faq.md | 0 .../traefik}/en.index.md | 0 .../traefik}/en.installation.md | 0 .../{zzci-traefik => zzci/traefik}/meta.json | 0 .../traefik}/zh.configuration.md | 0 .../{zzci-traefik => zzci/traefik}/zh.data.md | 0 .../{zzci-traefik => zzci/traefik}/zh.faq.md | 0 .../traefik}/zh.index.md | 0 .../traefik}/zh.installation.md | 0 docs/changelog.md | 15 ++ docs/plan/PLAN-005.md | 62 +++++ docs/plan/PLAN-006.md | 52 ++++ docs/plan/PLAN-007.md | 57 +++++ docs/plan/index.md | 3 + docs/task/FE-003.md | 26 ++ docs/task/FE-004.md | 24 ++ docs/task/FE-005.md | 24 ++ docs/task/index.md | 3 + 53 files changed, 379 insertions(+), 632 deletions(-) delete mode 100644 apps/frontend/src/pages/[locale]/category/[category].astro delete mode 100644 apps/frontend/src/pages/category/[category].astro delete mode 100644 content/demo/configuration.md delete mode 100644 content/demo/index.md delete mode 100644 content/demo/logo.svg delete mode 100644 content/demo/meta.json delete mode 100644 content/demo/troubleshooting.md delete mode 100644 content/demo/usage.md rename content/{zzci-chrome => zzci/chrome}/en.configuration.md (100%) rename content/{zzci-chrome => zzci/chrome}/en.data.md (100%) rename content/{zzci-chrome => zzci/chrome}/en.faq.md (100%) rename content/{zzci-chrome => zzci/chrome}/en.index.md (100%) rename content/{zzci-chrome => zzci/chrome}/en.installation.md (100%) rename content/{zzci-chrome => zzci/chrome}/meta.json (100%) rename content/{zzci-chrome => zzci/chrome}/zh.configuration.md (100%) rename content/{zzci-chrome => zzci/chrome}/zh.data.md (100%) rename content/{zzci-chrome => zzci/chrome}/zh.faq.md (100%) rename content/{zzci-chrome => zzci/chrome}/zh.index.md (100%) rename content/{zzci-chrome => zzci/chrome}/zh.installation.md (100%) rename content/{zzci-traefik => zzci/traefik}/en.configuration.md (100%) rename content/{zzci-traefik => zzci/traefik}/en.data.md (100%) rename content/{zzci-traefik => zzci/traefik}/en.faq.md (100%) rename content/{zzci-traefik => zzci/traefik}/en.index.md (100%) rename content/{zzci-traefik => zzci/traefik}/en.installation.md (100%) rename content/{zzci-traefik => zzci/traefik}/meta.json (100%) rename content/{zzci-traefik => zzci/traefik}/zh.configuration.md (100%) rename content/{zzci-traefik => zzci/traefik}/zh.data.md (100%) rename content/{zzci-traefik => zzci/traefik}/zh.faq.md (100%) rename content/{zzci-traefik => zzci/traefik}/zh.index.md (100%) rename content/{zzci-traefik => zzci/traefik}/zh.installation.md (100%) create mode 100644 docs/plan/PLAN-005.md create mode 100644 docs/plan/PLAN-006.md create mode 100644 docs/plan/PLAN-007.md create mode 100644 docs/task/FE-003.md create mode 100644 docs/task/FE-004.md create mode 100644 docs/task/FE-005.md diff --git a/apps/frontend/src/components/ToolDetailPage.astro b/apps/frontend/src/components/ToolDetailPage.astro index 8eeb978..c358b99 100644 --- a/apps/frontend/src/components/ToolDetailPage.astro +++ b/apps/frontend/src/components/ToolDetailPage.astro @@ -3,7 +3,6 @@ import type { Locale } from '@/i18n' import { getTranslations } from '@/i18n' import Base from '@/layouts/Base.astro' import { - getToolBadge, getToolDoc, getToolDocs, getToolMeta, @@ -24,7 +23,6 @@ type RelatedToolCard = { href: string title: string description: string - badge: string logo: ReturnType } @@ -36,13 +34,12 @@ const docSlug = parts[1] || 'index' const t = getTranslations(locale) const toolsData = getToolsJson() const toolEntry = toolsData.list.find(item => item.slug === toolSlug) -const categoryLabel = toolEntry ? getToolBadge(toolsData, toolEntry.category, locale) : '' const toolTags = toolEntry ? getToolTags(toolsData, toolEntry.tags ?? [], locale) : [] -const meta = getToolMeta(toolSlug) -const { data: frontmatter, html: htmlContent } = await getToolDoc(toolSlug, docSlug, locale) +const meta = getToolMeta(toolSlug, toolsData) +const { data: frontmatter, html: htmlContent } = await getToolDoc(toolSlug, docSlug, locale, toolsData) -const allDocs = await getToolDocs(toolSlug, locale) +const allDocs = await getToolDocs(toolSlug, locale, toolsData) const sidebarDocs = allDocs .map(doc => ({ slug: doc.slug, @@ -80,14 +77,13 @@ const linkedTools: RelatedToolCard[] = (meta.link ?? []) if (!relatedEntry) return null - const relatedMeta = getToolMeta(relatedSlug) + const relatedMeta = getToolMeta(relatedSlug, toolsData) return { slug: relatedSlug, href: localizePath(locale, `/${relatedSlug}`), title: txt(relatedMeta.title, locale), description: txt(relatedMeta.description, locale), - badge: getToolBadge(toolsData, relatedEntry.category, locale), - logo: resolveToolLogo(relatedSlug, relatedEntry.logo), + logo: resolveToolLogo(relatedSlug, relatedEntry.logo, toolsData), } }) .filter((item): item is RelatedToolCard => item !== null) @@ -101,9 +97,6 @@ return null {txt(meta.title, locale)}

- {categoryLabel && toolEntry && ( - {categoryLabel} - )} {toolTags.map(tag => ( {tag.label} ))} @@ -156,34 +149,6 @@ return null )}
- {linkedTools.length > 0 && ( - - )} -
@@ -227,6 +192,33 @@ return null )}
diff --git a/apps/frontend/src/components/ToolDirectoryPage.astro b/apps/frontend/src/components/ToolDirectoryPage.astro index 5f5944d..1145e62 100644 --- a/apps/frontend/src/components/ToolDirectoryPage.astro +++ b/apps/frontend/src/components/ToolDirectoryPage.astro @@ -4,7 +4,6 @@ import type { Locale } from '@/i18n' import { getTranslations } from '@/i18n' import Base from '@/layouts/Base.astro' import { - getToolBadge, getToolMeta, getToolTags, getToolsJson, @@ -16,7 +15,6 @@ interface Props { locale: Locale metaTitle?: string metaDescription?: string - initialCategory?: string initialTag?: string detectLocale?: boolean } @@ -25,7 +23,6 @@ const { locale, metaTitle, metaDescription, - initialCategory, initialTag, detectLocale = false, } = Astro.props @@ -36,14 +33,12 @@ const toolsData = getToolsJson() const toolItems = toolsData.list .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) .map((item) => { - const meta = getToolMeta(item.slug) - const logo = resolveToolLogo(item.slug, item.logo) + const meta = getToolMeta(item.slug, toolsData) + const logo = resolveToolLogo(item.slug, item.logo, toolsData) const title = txt(meta.title, locale) return { slug: item.slug, - category: item.category, - badge: getToolBadge(toolsData, item.category, locale), logo: logo.type === 'icon' ? item.logo! : logo.type === 'svg' ? 'custom' : '', logoSvg: logo.type === 'svg' ? logo.content : undefined, order: item.order, @@ -53,11 +48,6 @@ const toolItems = toolsData.list } }) -const categories = toolsData.categories.map(cat => ({ - key: cat.key, - label: txt(cat, locale), -})) - const allTags = toolsData.tags.map(tag => ({ key: tag.key, label: txt(tag, locale), @@ -84,13 +74,11 @@ const allTags = toolsData.tags.map(tag => ({ client:load locale={locale} tools={toolItems} - categories={categories} allTags={allTags} - initialCategory={initialCategory} initialTag={initialTag} translations={{ searchPlaceholder: t.hero.searchPlaceholder, - all: t.categories.all, + all: t.filters.all, view: t.card.view, noResults: t.card.noResults, tags: t.card.tags, diff --git a/apps/frontend/src/components/ToolSearch.tsx b/apps/frontend/src/components/ToolSearch.tsx index 7239c03..5f89196 100644 --- a/apps/frontend/src/components/ToolSearch.tsx +++ b/apps/frontend/src/components/ToolSearch.tsx @@ -27,8 +27,6 @@ interface ToolTag { interface ToolItem { slug: string - category: string - badge: string logo: string logoSvg?: string order: number @@ -37,11 +35,6 @@ interface ToolItem { tags?: ToolTag[] } -interface Category { - key: string - label: string -} - const iconMap: Record = { 'image': ImageIcon, 'braces': Braces, @@ -56,9 +49,7 @@ const PAGE_SIZE = 12 interface Props { locale: Locale tools: ToolItem[] - categories: Category[] allTags: ToolTag[] - initialCategory?: string initialTag?: string translations: { searchPlaceholder: string @@ -75,28 +66,13 @@ interface Props { } } -export function ToolSearch({ locale, tools, categories, allTags, initialCategory, initialTag, translations }: Props) { +export function ToolSearch({ locale, tools, allTags, initialTag, translations }: Props) { const [query, setQuery] = useState('') const [page, setPage] = useState(1) - const [activeCategory, setActiveCategory] = useState(initialCategory ?? 'all') const [activeTag, setActiveTag] = useState(initialTag ?? '') const normalizedQuery = query.trim().toLowerCase() - const allCategories: Category[] = [ - { key: 'all', label: translations.all }, - ...categories, - ] - - const categoryCounts = useMemo(() => { - const counts = new Map() - counts.set('all', tools.length) - for (const cat of categories) { - counts.set(cat.key, tools.filter(t => t.category === cat.key).length) - } - return counts - }, [tools, categories]) - const tagCounts = useMemo(() => { const counts = new Map() for (const tag of allTags) { @@ -107,17 +83,15 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory const filteredTools = useMemo(() => { return tools.filter((tool) => { - const matchesCategory = - activeCategory === 'all' || tool.category === activeCategory const matchesTag = activeTag === '' || tool.tags?.some(t => t.key === activeTag) const searchText = - `${tool.title} ${tool.description} ${tool.badge} ${(tool.tags ?? []).map(t => t.label).join(' ')}`.toLowerCase() + `${tool.title} ${tool.description} ${(tool.tags ?? []).map(t => t.label).join(' ')}`.toLowerCase() const matchesQuery = normalizedQuery.length === 0 || searchText.includes(normalizedQuery) - return matchesCategory && matchesTag && matchesQuery + return matchesTag && matchesQuery }) - }, [tools, activeCategory, activeTag, normalizedQuery]) + }, [tools, activeTag, normalizedQuery]) const totalPages = Math.max(1, Math.ceil(filteredTools.length / PAGE_SIZE)) const currentPage = Math.min(page, totalPages) @@ -131,14 +105,6 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory setPage(1) }, [normalizedQuery]) - function handleCategoryChange(key: string) { - setActiveCategory(key) - setActiveTag('') - setPage(1) - const url = key === 'all' ? localizePath(locale, '/') : localizePath(locale, `/category/${key}`) - window.history.replaceState(null, '', url) - } - function handleTagChange(key: string) { const newTag = activeTag === key ? '' : key setActiveTag(newTag) @@ -146,10 +112,7 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory if (newTag) { window.history.replaceState(null, '', localizePath(locale, `/tags/${newTag}`)) } else { - const url = activeCategory === 'all' ? - localizePath(locale, '/') : - localizePath(locale, `/category/${activeCategory}`) - window.history.replaceState(null, '', url) + window.history.replaceState(null, '', localizePath(locale, '/')) } } @@ -166,32 +129,26 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory />
-
- {allCategories.map(cat => ( + {allTags.length > 0 && ( +
- ))} -
- - {allTags.length > 0 && ( -
{allTags.map((tag) => { const count = tagCounts.get(tag.key) ?? 0 @@ -264,10 +221,7 @@ export function ToolSearch({ locale, tools, categories, allTags, initialCategory
- - {tool.badge} - - {tool.tags?.slice(0, 3).map(tag => ( + {tool.tags?.slice(0, 4).map(tag => ( segment.length > 0 && segment !== '.') +} + +function getToolContentPath(slug: string, toolsData: ToolsJson = getToolsJson()): string { + const item = toolsData.list.find(entry => entry.slug === slug) + const contentPath = item?.path ?? slug + return isSafeContentPath(contentPath) ? contentPath : slug +} + function replaceLinkPlaceholders(content: string): string { const segments = content.split(/(```[\s\S]*?```)/g) return segments @@ -167,16 +177,17 @@ function rewriteInternalLinks(html: string, locale: Locale): string { }) } -export function resolveToolLogo(slug: string, logo?: string): { type: 'icon', name: string } | { type: 'svg', content: string } | { type: 'none' } { +export function resolveToolLogo(slug: string, logo?: string, toolsData?: ToolsJson): { type: 'icon', name: string } | { type: 'svg', content: string } | { type: 'none' } { if (!logo) return { type: 'none' } - if (!isValidSlug(slug) || (logo.includes('.') && !isValidSlug(logo.replace('.svg', '')))) { + const contentPath = getToolContentPath(slug, toolsData) + if (!isSafeContentPath(contentPath) || (logo.includes('.') && !isValidSlug(logo.replace('.svg', '')))) { return { type: 'none' } } if (logo.includes('.')) { try { - const logoPath = resolve(CONTENT_ROOT, slug, logo) + const logoPath = resolve(CONTENT_ROOT, contentPath, logo) if (!logoPath.startsWith(resolve(CONTENT_ROOT))) return { type: 'none' } const content = readFileSync(logoPath, 'utf-8') @@ -196,13 +207,6 @@ export function getToolTags(toolsData: ToolsJson, tagKeys: string[], locale: Loc }) } -export function getToolBadge(toolsData: ToolsJson, category: string, locale: Locale): string { - const categoryItem = toolsData.categories.find(item => item.key === category) - if (!categoryItem) - return category - return txt(categoryItem.badge, locale) -} - export async function renderMarkdown(content: string, locale?: Locale): Promise { const highlighter = await getHighlighter() const prepared = replaceLinkPlaceholders(content) @@ -243,9 +247,9 @@ export function getToolsJson(): ToolsJson { return JSON.parse(readFileSync(join(CONTENT_ROOT, 'list.json'), 'utf-8')) } -export function getToolMeta(slug: string): ToolMeta { +export function getToolMeta(slug: string, toolsData?: ToolsJson): ToolMeta { try { - const metaPath = join(CONTENT_ROOT, slug, 'meta.json') + const metaPath = join(CONTENT_ROOT, getToolContentPath(slug, toolsData), 'meta.json') return JSON.parse(readFileSync(metaPath, 'utf-8')) } catch { return { @@ -264,9 +268,9 @@ function parseDocFilename(filename: string): { locale: string | null, docSlug: s return { locale: null, docSlug: name } } -export function getToolDocSlugs(slug: string): string[] { +export function getToolDocSlugs(slug: string, toolsData?: ToolsJson): string[] { try { - const toolDir = join(CONTENT_ROOT, slug) + const toolDir = join(CONTENT_ROOT, getToolContentPath(slug, toolsData)) const files = readdirSync(toolDir).filter(filename => filename.endsWith('.md')) const slugs = new Set() for (const filename of files) { @@ -278,9 +282,9 @@ export function getToolDocSlugs(slug: string): string[] { } } -export async function getToolDocs(slug: string, locale?: Locale): Promise { +export async function getToolDocs(slug: string, locale?: Locale, toolsData?: ToolsJson): Promise { let files: string[] - const toolDir = join(CONTENT_ROOT, slug) + const toolDir = join(CONTENT_ROOT, getToolContentPath(slug, toolsData)) try { files = readdirSync(toolDir).filter(filename => filename.endsWith('.md')) @@ -312,12 +316,13 @@ export async function getToolDocs(slug: string, locale?: Locale): Promise ({ - params: { locale: 'zh', category: category.key }, - })) -} - -const locale = 'zh' as const -const categoryKey = Astro.params.category! -const t = getTranslations(locale) -const toolsData = getToolsJson() -const categoryObj = toolsData.categories.find(item => item.key === categoryKey) -const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey ---- - - diff --git a/apps/frontend/src/pages/[locale]/template.astro b/apps/frontend/src/pages/[locale]/template.astro index f14acfb..8b50e73 100644 --- a/apps/frontend/src/pages/[locale]/template.astro +++ b/apps/frontend/src/pages/[locale]/template.astro @@ -50,22 +50,19 @@ content/ \`\`\`json { "slug": "my-tool", - "category": "devTools", - "tags": ["open-source", "free", "cli", "web"] + "path": "my-group/my-tool", + "tags": ["devTools", "open-source", "free", "cli", "web"] } \`\`\` -## 可用分类 +只有当内容目录和 URL slug 不一致时才需要写 \`path\`。 +例如 \`slug: "zzci-chrome"\` 可以对应到 \`path: "zzci/chrome"\`。 + +## 常用标签 - \`images\` -- \`textCode\` -- \`colors\` -- \`converters\` - \`devTools\` - \`containers\` - -## 常用标签 - - \`open-source\` - \`free\` - \`desktop\` diff --git a/apps/frontend/src/pages/category/[category].astro b/apps/frontend/src/pages/category/[category].astro deleted file mode 100644 index 34eec84..0000000 --- a/apps/frontend/src/pages/category/[category].astro +++ /dev/null @@ -1,21 +0,0 @@ ---- -import ToolDirectoryPage from '@/components/ToolDirectoryPage.astro' -import { getTranslations } from '@/i18n' -import { getToolsJson, txt } from '@/lib/content' - -export function getStaticPaths() { - const toolsData = getToolsJson() - return toolsData.categories.map(category => ({ - params: { category: category.key }, - })) -} - -const locale = 'en' as const -const categoryKey = Astro.params.category! -const t = getTranslations(locale) -const toolsData = getToolsJson() -const categoryObj = toolsData.categories.find(item => item.key === categoryKey) -const categoryLabel = categoryObj ? txt(categoryObj, locale) : categoryKey ---- - - diff --git a/apps/frontend/src/pages/template.astro b/apps/frontend/src/pages/template.astro index 96a260a..6b36b16 100644 --- a/apps/frontend/src/pages/template.astro +++ b/apps/frontend/src/pages/template.astro @@ -46,22 +46,19 @@ content/ \`\`\`json { "slug": "my-tool", - "category": "devTools", - "tags": ["open-source", "free", "cli", "web"] + "path": "my-group/my-tool", + "tags": ["devTools", "open-source", "free", "cli", "web"] } \`\`\` -## Available Categories +Use \`path\` only when the content directory should differ from the URL slug. +For example, \`slug: "zzci-chrome"\` can point to \`path: "zzci/chrome"\`. + +## Common Tags - \`images\` -- \`textCode\` -- \`colors\` -- \`converters\` - \`devTools\` - \`containers\` - -## Common Tags - - \`open-source\` - \`free\` - \`desktop\` diff --git a/apps/frontend/src/styles/global.css b/apps/frontend/src/styles/global.css index 9d44c45..27f8e71 100644 --- a/apps/frontend/src/styles/global.css +++ b/apps/frontend/src/styles/global.css @@ -260,7 +260,7 @@ background: transparent; color: var(--muted-foreground); cursor: pointer; - opacity: 0; + opacity: 1; transition: opacity 0.15s, background-color 0.15s, @@ -268,10 +268,6 @@ z-index: 2; } -.prose-article .code-block:hover .code-copy-btn { - opacity: 1; -} - .prose-article .code-block .code-copy-btn:hover { background: color-mix(in srgb, var(--foreground) 8%, transparent); border-color: var(--border); diff --git a/content/demo/configuration.md b/content/demo/configuration.md deleted file mode 100644 index b688020..0000000 --- a/content/demo/configuration.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: - en: Configuration - zh: 配置 -order: 2 ---- - -Image Compressor ships with sensible defaults, but advanced users can fine-tune output quality, rename patterns, overwrite behavior, and destination handling from the preferences panel. Each option updates instantly and applies to batch exports. - -图片压缩器默认配置合理,高级用户可以在偏好设置面板中微调输出质量、重命名模式、覆盖行为和目标处理。每个选项都会即时更新并应用于批量导出。 diff --git a/content/demo/index.md b/content/demo/index.md deleted file mode 100644 index e78b6d5..0000000 --- a/content/demo/index.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: - en: Introduction - zh: 简介 -order: 0 ---- - -## Overview - -Image Compressor is a lightweight desktop application that reduces the file size of your PNG, JPG, and WebP images without losing visible quality. All processing happens locally on your machine — your files never leave your device. - -Built with performance in mind, Image Compressor leverages modern compression algorithms including MozJPEG, OptiPNG, and libwebp to achieve optimal file size reduction while preserving visual fidelity. Whether you're a web developer optimizing assets, a photographer managing large libraries, or anyone who needs smaller image files, this tool provides a simple drag-and-drop workflow. - -### Key Features - -- **Batch Processing** — Compress hundreds of images at once with parallel processing. Simply drag a folder onto the app and let it handle the rest. -- **Smart Quality Detection** — Automatically analyzes each image and selects the optimal compression level. No manual tweaking required for most use cases. -- **Format Conversion** — Convert between PNG, JPG, and WebP formats during compression. Easily migrate your entire image library to modern WebP format. -- **Lossless Mode** — When every pixel matters, enable lossless mode to reduce file size without any quality degradation. -- **Watch Mode** — Monitor folders for new images and compress them automatically as they appear. -- **EXIF Data Control** — Choose to preserve or strip metadata. Remove GPS coordinates for privacy while keeping camera settings. - -### How It Works - -Image Compressor uses a multi-stage pipeline to process your images: - -1. **Analysis** — The image is loaded and analyzed for color depth, transparency, and complexity. This determines which compression strategy will be most effective. -2. **Pre-processing** — Optional steps like resizing, color profile conversion (sRGB normalization), and alpha channel optimization are applied. -3. **Compression** — The appropriate encoder processes the image using the selected quality settings. For JPEG, MozJPEG provides superior compression compared to standard libraries. For PNG, OptiPNG finds the optimal filter and compression parameters. For WebP, Google's libwebp encoder handles both lossy and lossless modes. -4. **Verification** — The output is compared against the original to ensure quality thresholds are met. If the compressed file is somehow larger than the original, the original is kept. - -### Supported Formats - -| Format | Input | Output | Notes | -|--------|-------|--------|-------| -| JPEG/JPG | Yes | Yes | Progressive JPEG support | -| PNG | Yes | Yes | 8-bit and 24-bit with alpha | -| WebP | Yes | Yes | Lossy and lossless modes | -| AVIF | Yes | No | Read-only, convert to other formats | -| GIF | Yes | No | Static frames only | - -### System Requirements - -- **macOS** 12.0 or later (Universal Binary — Apple Silicon and Intel) -- **Windows** 10 version 1903 or later (64-bit) -- **Linux** — Ubuntu 20.04+, Fedora 36+, or equivalent (x86_64 and arm64) -- Minimum 4 GB RAM (8 GB recommended for batch processing) -- 100 MB disk space for installation - ---- - -## 概述 - -图片压缩器是一款轻量级桌面应用,可以在不损失可见质量的情况下减小 PNG、JPG 和 WebP 图片的文件大小。所有处理都在本地完成——你的文件永远不会离开你的设备。 - -图片压缩器以性能为核心设计,利用 MozJPEG、OptiPNG 和 libwebp 等现代压缩算法,在保持视觉保真度的同时实现最佳的文件大小缩减。无论你是优化资源的 Web 开发者、管理大量图片库的摄影师,还是任何需要更小图片文件的人,这款工具都提供了简单的拖放式工作流程。 - -### 主要功能 - -- **批量处理** — 通过并行处理一次压缩数百张图片。只需将文件夹拖到应用上,让它处理剩下的工作。 -- **智能质量检测** — 自动分析每张图片并选择最佳压缩级别。大多数情况下无需手动调整。 -- **格式转换** — 在压缩过程中转换 PNG、JPG 和 WebP 格式。轻松将整个图片库迁移到现代 WebP 格式。 -- **无损模式** — 当每个像素都很重要时,启用无损模式可在不降低任何质量的情况下减小文件大小。 -- **监视模式** — 监控文件夹中的新图片,并在出现时自动压缩。 -- **EXIF 数据控制** — 选择保留或删除元数据。删除 GPS 坐标以保护隐私,同时保留相机设置。 - -### 工作原理 - -图片压缩器使用多阶段管道处理你的图片: - -1. **分析** — 加载并分析图片的色深、透明度和复杂度,以确定最有效的压缩策略。 -2. **预处理** — 应用可选步骤,如调整大小、颜色配置文件转换(sRGB 标准化)和 Alpha 通道优化。 -3. **压缩** — 使用所选质量设置处理图片。JPEG 使用 MozJPEG,PNG 使用 OptiPNG,WebP 使用 Google 的 libwebp 编码器。 -4. **验证** — 将输出与原始文件进行比较,确保满足质量阈值。如果压缩文件比原始文件大,则保留原始文件。 - -### 支持的格式 - -| 格式 | 输入 | 输出 | 备注 | -|------|------|------|------| -| JPEG/JPG | 支持 | 支持 | 支持渐进式 JPEG | -| PNG | 支持 | 支持 | 8 位和 24 位(含 Alpha) | -| WebP | 支持 | 支持 | 有损和无损模式 | -| AVIF | 支持 | 不支持 | 仅读取,转换为其他格式 | -| GIF | 支持 | 不支持 | 仅静态帧 | - -### 系统要求 - -- **macOS** 12.0 或更高版本(通用二进制 — 支持 Apple Silicon 和 Intel) -- **Windows** 10 1903 版本或更高(64 位) -- **Linux** — Ubuntu 20.04+、Fedora 36+ 或同等版本(x86_64 和 arm64) -- 最低 4 GB 内存(批量处理建议 8 GB) -- 100 MB 磁盘空间用于安装 diff --git a/content/demo/logo.svg b/content/demo/logo.svg deleted file mode 100644 index 9b5f89c..0000000 --- a/content/demo/logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/content/demo/meta.json b/content/demo/meta.json deleted file mode 100644 index bfdf24b..0000000 --- a/content/demo/meta.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "title": { "en": "Demo Tool", "zh": "示例工具" }, - "description": { - "en": "A fast, privacy-focused desktop app that reduces PNG, JPG, and WebP file sizes using MozJPEG, OptiPNG, and libwebp — with batch processing, smart quality detection, and format conversion. All processing stays on your device.", - "zh": "一款快速、注重隐私的桌面应用,使用 MozJPEG、OptiPNG 和 libwebp 压缩 PNG、JPG 和 WebP 图片——支持批量处理、智能质量检测和格式转换。所有处理均在本地完成。" - }, - "version": "2.4.1", - "website": "https://example.com/image-compressor", - "github": "https://github.com/example/image-compressor", - "addedAt": "2026-01-15", - "addedBy": { "name": "Alex", "url": "https://github.com/alex" }, - "links": [ - { - "label": { "en": "Changelog", "zh": "更新日志" }, - "url": "https://github.com/example/image-compressor/releases" - }, - { - "label": { "en": "Report Issue", "zh": "报告问题" }, - "url": "https://github.com/example/image-compressor/issues" - } - ] -} diff --git a/content/demo/troubleshooting.md b/content/demo/troubleshooting.md deleted file mode 100644 index 77a76a8..0000000 --- a/content/demo/troubleshooting.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: - en: Troubleshooting - zh: 故障排除 -order: 3 ---- - -If an export fails, check write permissions for your destination folder and confirm the image format is supported. For reproducible issues, visit the GitHub repository and include your operating system, app version, and a sample file when filing a report. - -如果导出失败,请检查目标文件夹的写入权限并确认图片格式是否受支持。对于可复现的问题,请访问 GitHub 仓库,在提交报告时附上操作系统、应用版本和示例文件。 diff --git a/content/demo/usage.md b/content/demo/usage.md deleted file mode 100644 index c1940d6..0000000 --- a/content/demo/usage.md +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: - en: Usage Guide - zh: 使用指南 -order: 1 ---- - -## Quick Start - -Install Image Compressor via your preferred package manager: - -```bash -# macOS -brew install image-compressor - -# Windows (winget) -winget install ImageCompressor - -# Linux (snap) -sudo snap install image-compressor -``` - -Or download the installer directly from the [releases page](https://example.com). - -## CLI Usage - -Image Compressor includes a powerful command-line interface for scripting and automation: - -```bash -# Compress a single image -imgc compress photo.jpg - -# Compress with custom quality (0-100) -imgc compress --quality 80 photo.jpg - -# Batch compress an entire directory -imgc compress --recursive ./images/ - -# Convert to WebP format -imgc convert --format webp --quality 85 ./images/*.png - -# Watch a folder for new images -imgc watch --quality 75 --format webp ~/Downloads/ -``` - -### CLI Options Reference - -``` -Usage: imgc [options] - -Commands: - compress Compress images in place or to output directory - convert Convert images between formats - watch Monitor directory for new images - info Display image metadata and compression stats - -Options: - -q, --quality Quality level 0-100 (default: 80) - -f, --format Output format: jpg, png, webp (default: same as input) - -o, --output Output directory (default: overwrite in place) - -r, --recursive Process subdirectories - --lossless Use lossless compression - --strip-metadata Remove EXIF/IPTC data - --keep-gps Preserve GPS coordinates (stripped by default) - --max-width Resize if wider than specified pixels - --max-height Resize if taller than specified pixels - --threads Number of parallel workers (default: CPU cores) - -v, --verbose Show detailed processing info - --dry-run Preview changes without writing files -``` - -## Node.js API - -Use Image Compressor programmatically in your build pipeline: - -```typescript -import { compress, convert, ImageOptions } from 'image-compressor'; - -// Basic compression -const result = await compress('photo.jpg', { quality: 80 }); -console.log(`Saved ${result.savedBytes} bytes (${result.savedPercent}%)`); - -// Batch compression with options -const options: ImageOptions = { - quality: 75, - format: 'webp', - stripMetadata: true, - maxWidth: 1920, - progressive: true, -}; - -const files = await compress('./images/**/*.{jpg,png}', options); -for (const file of files) { - console.log(`${file.path}: ${file.originalSize} → ${file.compressedSize}`); -} -``` - -### Integration with Build Tools - -**Vite Plugin:** - -```typescript -// vite.config.ts -import { defineConfig } from 'vite'; -import imageCompressor from 'image-compressor/vite'; - -export default defineConfig({ - plugins: [ - imageCompressor({ - include: ['**/*.{jpg,png,webp}'], - exclude: ['node_modules/**'], - quality: 80, - format: 'webp', - // Only compress in production builds - enabled: process.env.NODE_ENV === 'production', - }), - ], -}); -``` - -**Webpack Loader:** - -```javascript -// webpack.config.js -module.exports = { - module: { - rules: [ - { - test: /\.(jpe?g|png|webp)$/i, - use: [ - { - loader: 'image-compressor/webpack', - options: { - quality: 80, - progressive: true, - stripMetadata: true, - }, - }, - ], - }, - ], - }, -}; -``` - -## GitHub Actions - -Automate image compression in your CI/CD pipeline: - -```yaml -# .github/workflows/compress-images.yml -name: Compress Images - -on: - pull_request: - paths: - - '**.jpg' - - '**.jpeg' - - '**.png' - - '**.webp' - -jobs: - compress: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Compress new/changed images - uses: example/image-compressor-action@v2 - with: - quality: 80 - format: preserve - strip-metadata: true - commit-changes: true - commit-message: 'chore: compress images [skip ci]' -``` - -## Configuration File - -Create an `.imgcrc.json` in your project root for persistent settings: - -```json -{ - "quality": 80, - "format": "preserve", - "stripMetadata": true, - "keepGps": false, - "progressive": true, - "lossless": false, - "maxWidth": 2560, - "threads": 4, - "ignore": [ - "node_modules/**", - ".git/**", - "dist/**", - "**/*.ico" - ], - "overrides": [ - { - "match": "**/thumbnails/**", - "quality": 60, - "maxWidth": 400 - }, - { - "match": "**/hero-*", - "quality": 90, - "lossless": false - } - ] -} -``` - -## Output Example - -When running in verbose mode, you'll see detailed compression stats: - -``` -$ imgc compress --verbose --recursive ./photos/ - -Processing 47 images with 8 threads... - - photos/landscape.jpg 4.2 MB → 1.1 MB (73.8% saved) ✓ - photos/portrait.png 8.7 MB → 2.3 MB (73.6% saved) ✓ - photos/icon.png 24 KB → 18 KB (25.0% saved) ✓ - photos/banner.webp 1.8 MB → 890 KB (51.7% saved) ✓ - photos/tiny.jpg 3.2 KB → 3.2 KB (skipped, already optimal) - -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - Total: 47 files processed in 2.3s - Saved: 89.4 MB (68.2% average reduction) - Skipped: 3 files (already optimal) -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -``` diff --git a/content/list.json b/content/list.json index 969e7a5..ccde59e 100644 --- a/content/list.json +++ b/content/list.json @@ -1,13 +1,8 @@ { - "categories": [ - { "key": "images", "en": "Images", "zh": "图片", "badge": { "en": "Images", "zh": "图片" } }, - { "key": "textCode", "en": "Text & Code", "zh": "文本 & 代码", "badge": { "en": "Text & Code", "zh": "文本 & 代码" } }, - { "key": "colors", "en": "Colors", "zh": "颜色", "badge": { "en": "Colors", "zh": "颜色" } }, - { "key": "converters", "en": "Converters", "zh": "转换器", "badge": { "en": "Converters", "zh": "转换器" } }, - { "key": "devTools", "en": "Dev Tools", "zh": "开发工具", "badge": { "en": "Dev Tools", "zh": "开发工具" } }, - { "key": "containers", "en": "Containers", "zh": "容器", "badge": { "en": "Containers", "zh": "容器" } } - ], "tags": [ + { "key": "images", "en": "Images", "zh": "图片" }, + { "key": "devTools", "en": "Dev Tools", "zh": "开发工具" }, + { "key": "containers", "en": "Containers", "zh": "容器" }, { "key": "open-source", "en": "Open Source", "zh": "开源" }, { "key": "free", "en": "Free", "zh": "免费" }, { "key": "desktop", "en": "Desktop", "zh": "桌面端" }, @@ -19,38 +14,29 @@ { "key": "automation", "en": "Automation", "zh": "自动化" }, { "key": "proxy", "en": "Proxy", "zh": "代理" } ], - "featured": ["demo", "bkd"], + "featured": ["bkd"], "list": [ - { - "slug": "demo", - "category": "images", - "logo": "logo.svg", - "order": 1, - "tags": ["open-source", "free", "desktop", "privacy"] - }, { "slug": "bkd", - "category": "devTools", - "order": 2, - "tags": ["open-source", "web"] + "order": 1, + "tags": ["devTools", "open-source", "web"] }, { "slug": "zzci-traefik", - "category": "containers", - "order": 3, - "tags": ["open-source", "free", "cli", "web", "docker", "self-hosted", "proxy"] + "path": "zzci/traefik", + "order": 2, + "tags": ["containers", "open-source", "free", "cli", "web", "docker", "self-hosted", "proxy"] }, { "slug": "zzci-chrome", - "category": "containers", - "order": 4, - "tags": ["open-source", "free", "web", "docker", "self-hosted", "automation"] + "path": "zzci/chrome", + "order": 3, + "tags": ["containers", "open-source", "free", "web", "docker", "self-hosted", "automation"] }, { "slug": "agent-browser", - "category": "devTools", - "order": 5, - "tags": ["open-source", "free", "cli", "web", "automation"] + "order": 4, + "tags": ["devTools", "open-source", "free", "cli", "web", "automation"] } ] } diff --git a/content/zzci-chrome/en.configuration.md b/content/zzci/chrome/en.configuration.md similarity index 100% rename from content/zzci-chrome/en.configuration.md rename to content/zzci/chrome/en.configuration.md diff --git a/content/zzci-chrome/en.data.md b/content/zzci/chrome/en.data.md similarity index 100% rename from content/zzci-chrome/en.data.md rename to content/zzci/chrome/en.data.md diff --git a/content/zzci-chrome/en.faq.md b/content/zzci/chrome/en.faq.md similarity index 100% rename from content/zzci-chrome/en.faq.md rename to content/zzci/chrome/en.faq.md diff --git a/content/zzci-chrome/en.index.md b/content/zzci/chrome/en.index.md similarity index 100% rename from content/zzci-chrome/en.index.md rename to content/zzci/chrome/en.index.md diff --git a/content/zzci-chrome/en.installation.md b/content/zzci/chrome/en.installation.md similarity index 100% rename from content/zzci-chrome/en.installation.md rename to content/zzci/chrome/en.installation.md diff --git a/content/zzci-chrome/meta.json b/content/zzci/chrome/meta.json similarity index 100% rename from content/zzci-chrome/meta.json rename to content/zzci/chrome/meta.json diff --git a/content/zzci-chrome/zh.configuration.md b/content/zzci/chrome/zh.configuration.md similarity index 100% rename from content/zzci-chrome/zh.configuration.md rename to content/zzci/chrome/zh.configuration.md diff --git a/content/zzci-chrome/zh.data.md b/content/zzci/chrome/zh.data.md similarity index 100% rename from content/zzci-chrome/zh.data.md rename to content/zzci/chrome/zh.data.md diff --git a/content/zzci-chrome/zh.faq.md b/content/zzci/chrome/zh.faq.md similarity index 100% rename from content/zzci-chrome/zh.faq.md rename to content/zzci/chrome/zh.faq.md diff --git a/content/zzci-chrome/zh.index.md b/content/zzci/chrome/zh.index.md similarity index 100% rename from content/zzci-chrome/zh.index.md rename to content/zzci/chrome/zh.index.md diff --git a/content/zzci-chrome/zh.installation.md b/content/zzci/chrome/zh.installation.md similarity index 100% rename from content/zzci-chrome/zh.installation.md rename to content/zzci/chrome/zh.installation.md diff --git a/content/zzci-traefik/en.configuration.md b/content/zzci/traefik/en.configuration.md similarity index 100% rename from content/zzci-traefik/en.configuration.md rename to content/zzci/traefik/en.configuration.md diff --git a/content/zzci-traefik/en.data.md b/content/zzci/traefik/en.data.md similarity index 100% rename from content/zzci-traefik/en.data.md rename to content/zzci/traefik/en.data.md diff --git a/content/zzci-traefik/en.faq.md b/content/zzci/traefik/en.faq.md similarity index 100% rename from content/zzci-traefik/en.faq.md rename to content/zzci/traefik/en.faq.md diff --git a/content/zzci-traefik/en.index.md b/content/zzci/traefik/en.index.md similarity index 100% rename from content/zzci-traefik/en.index.md rename to content/zzci/traefik/en.index.md diff --git a/content/zzci-traefik/en.installation.md b/content/zzci/traefik/en.installation.md similarity index 100% rename from content/zzci-traefik/en.installation.md rename to content/zzci/traefik/en.installation.md diff --git a/content/zzci-traefik/meta.json b/content/zzci/traefik/meta.json similarity index 100% rename from content/zzci-traefik/meta.json rename to content/zzci/traefik/meta.json diff --git a/content/zzci-traefik/zh.configuration.md b/content/zzci/traefik/zh.configuration.md similarity index 100% rename from content/zzci-traefik/zh.configuration.md rename to content/zzci/traefik/zh.configuration.md diff --git a/content/zzci-traefik/zh.data.md b/content/zzci/traefik/zh.data.md similarity index 100% rename from content/zzci-traefik/zh.data.md rename to content/zzci/traefik/zh.data.md diff --git a/content/zzci-traefik/zh.faq.md b/content/zzci/traefik/zh.faq.md similarity index 100% rename from content/zzci-traefik/zh.faq.md rename to content/zzci/traefik/zh.faq.md diff --git a/content/zzci-traefik/zh.index.md b/content/zzci/traefik/zh.index.md similarity index 100% rename from content/zzci-traefik/zh.index.md rename to content/zzci/traefik/zh.index.md diff --git a/content/zzci-traefik/zh.installation.md b/content/zzci/traefik/zh.installation.md similarity index 100% rename from content/zzci-traefik/zh.installation.md rename to content/zzci/traefik/zh.installation.md diff --git a/docs/changelog.md b/docs/changelog.md index e85cbd3..0cb7176 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -27,3 +27,18 @@ Kept the Worker entry on TypeScript source because local development and Wrangle Changed frontend routing to use English as the default no-prefix path and Chinese under `/zh/*`. Moved system pages to `/sys/*` and tag pages to `/tags/*`, while keeping `/api/*` reserved. Updated locale switching, internal link localization, canonical and alternate tags, and Worker redirects for legacy `/en/*` and `/tag/*` paths. + +## 2026-03-09 20:27 [decision] + +Removed the `category` concept from content and frontend routing. +Moved former category semantics into `tags`, deleted `/category/*` page generation, and updated directory/detail UI to use tags only. + +## 2026-03-09 20:30 [decision] + +Removed the `demo` tool from the content registry and deleted the `content/demo/` directory. +Static generation no longer produces `/demo/*` or `/zh/demo/*` pages. + +## 2026-03-09 20:50 [decision] + +Added optional `path` mapping for tool content directories while keeping URL `slug` unchanged. +Migrated `zzci-chrome` and `zzci-traefik` to nested `content/zzci/...` directories and updated content loading to resolve files by `path`. diff --git a/docs/plan/PLAN-005.md b/docs/plan/PLAN-005.md new file mode 100644 index 0000000..1e299a3 --- /dev/null +++ b/docs/plan/PLAN-005.md @@ -0,0 +1,62 @@ +# PLAN-005 删除 category 并统一使用 tags 分类 + +- **status**: completed +- **createdAt**: 2026-03-09 20:23 +- **approvedAt**: 2026-03-09 20:23 +- **completedAt**: 2026-03-09 20:27 +- **relatedTask**: FE-003 + +## 现状 + +当前站点同时维护两套分类语义: + +- `content/list.json` 中存在 `categories[]` +- `content/list.json` 中每个工具条目存在 `category` +- 首页目录页同时渲染 category filters 与 tags filters +- 详情页与关联卡片使用 category badge +- 静态路由单独生成 `/category/*` 页面 + +这导致分类语义重复,且与“只保留 tags 做分类”的目标不一致。 + +## 调查上下文 + +本次变更会影响: + +- 数据与类型:`content/list.json`、`apps/frontend/src/lib/content.ts` +- 目录页:`apps/frontend/src/components/ToolDirectoryPage.astro`、`apps/frontend/src/components/ToolSearch.tsx` +- 详情页:`apps/frontend/src/components/ToolDetailPage.astro` +- 路由:`apps/frontend/src/pages/category/[category].astro`、`apps/frontend/src/pages/[locale]/category/[category].astro` +- 路由保留字与模板示例:`apps/frontend/src/lib/routes.ts`、`apps/frontend/src/pages/template.astro` + +这是一个跨模块改动,需要同步清理类型、页面和数据。 + +## 方案 + +采用“彻底删除 category,只保留 tags”的方案: + +1. 删除 `content/list.json` 中的 `categories[]` +2. 删除 `content/list.json` 中每个工具条目的 `category` +3. 将原分类 key 直接加入对应工具的 `tags[]` +4. 首页只保留 tag 过滤,不再渲染 category tabs +5. 详情页与关联卡片移除 category badge +6. 删除 `/category/*` 页面生成和路由保留字 + +## 风险 + +- 首页目录页的交互会从“双维度过滤”变成“单维度 tag 过滤” +- 若页面仍残留 `category` 类型引用,构建会直接失败 +- 旧 `/category/*` 链接会失效,但这次按要求不做兼容 + +## 范围 + +本次纳入: + +- 数据结构清理 +- category UI 清理 +- category 页面删除 +- 构建验证 + +本次不纳入: + +- 旧 `/category/*` 重定向兼容 +- tags 重新命名或重新分组 diff --git a/docs/plan/PLAN-006.md b/docs/plan/PLAN-006.md new file mode 100644 index 0000000..184c5f8 --- /dev/null +++ b/docs/plan/PLAN-006.md @@ -0,0 +1,52 @@ +# PLAN-006 删除 demo tool 内容与目录 + +- **status**: completed +- **createdAt**: 2026-03-09 20:29 +- **approvedAt**: 2026-03-09 20:29 +- **completedAt**: 2026-03-09 20:30 +- **relatedTask**: FE-004 + +## 现状 + +`demo` 当前仍然注册在 `content/list.json` 中,同时存在完整的 `content/demo/` 内容目录。 + +- `featured` 仍包含 `demo` +- `list[]` 仍包含 `demo` +- `[...slug].astro` 与 `[locale]/[...slug].astro` 的静态路由生成完全依赖 `toolsData.list` + +因此只要 `demo` 保留在 `list.json`,页面就会继续生成 `/demo/*` 与 `/zh/demo/*`。 + +## 调查上下文 + +本次变更影响面很小,主要集中在: + +- `content/list.json` +- `content/demo/` +- 文档记录与变更日志 + +页面生成逻辑本身不需要改,只要数据源删掉 `demo` 即可自然收敛。 + +## 方案 + +采用“彻底删除 demo tool”的方案: + +1. 从 `featured` 删除 `demo` +2. 从 `list[]` 删除 `demo` +3. 删除整个 `content/demo/` 目录 +4. 运行前端构建,确认 `/demo/*` 不再生成 + +## 风险 + +- 如果还有其它内容引用 `demo` slug,删除后会变成死链 +- 当前检索中没有发现其它业务代码依赖 `demo` + +## 范围 + +本次纳入: + +- `demo` 数据与内容目录删除 +- 构建验证 + +本次不纳入: + +- 为旧 `/demo/*` 做兼容重定向 diff --git a/docs/plan/PLAN-007.md b/docs/plan/PLAN-007.md new file mode 100644 index 0000000..7dc862c --- /dev/null +++ b/docs/plan/PLAN-007.md @@ -0,0 +1,57 @@ +# PLAN-007 为工具内容目录增加 path 映射并支持嵌套目录 + +- **status**: completed +- **createdAt**: 2026-03-09 20:31 +- **approvedAt**: 2026-03-09 20:31 +- **completedAt**: 2026-03-09 20:50 +- **relatedTask**: FE-005 + +## 现状 + +当前内容读取逻辑直接把 `slug` 当作 `content/` 下的目录名使用。 + +- `getToolMeta(slug)` 读取 `content//meta.json` +- `getToolDocSlugs(slug)`、`getToolDocs(slug)`、`getToolDoc(slug, doc)` 都读取 `content//` +- `resolveToolLogo(slug, logo)` 也从 `content//` 取文件 + +这意味着如果保留 URL slug 为 `zzci-chrome`,就无法把内容实际存成 `content/zzci/chrome/`。 + +## 调查上下文 + +当前路由层仍以 `slug` 作为页面主键,这正符合目标: + +- URL 继续使用 `slug` +- 站内 `link` 关系继续用 `slug` +- 只需要把内容读取层从“按 slug 找目录”改成“按 path 找目录,未配置时回退 slug” + +因此不需要改路由结构,只需要改数据与内容加载层。 + +## 方案 + +采用 `path` 字段方案: + +1. 在 `content/list.json` 的 `list[]` 中增加可选 `path` +2. 所有内容读取函数优先解析 `path`,未配置时回退 `slug` +3. 路径校验允许多级目录,但继续禁止 `..`、空段、绝对路径和越界访问 +4. 将 `zzci-chrome` / `zzci-traefik` 迁移到 `content/zzci/chrome` / `content/zzci/traefik` +5. 模板页增加 `path` 用法说明 + +## 风险 + +- 如果路径校验不严,可能引入目录穿越风险 +- 如果只有部分读取函数支持 `path`,会出现 logo / meta / docs 不一致 +- 迁移目录后若 `list.json` 漏填 `path`,页面会直接读不到内容 + +## 范围 + +本次纳入: + +- `path` 字段支持 +- 读取逻辑统一收口 +- `zzci-*` 目录迁移 +- 构建验证 + +本次不纳入: + +- slug 改成多级路径 +- 其它工具目录重组 diff --git a/docs/plan/index.md b/docs/plan/index.md index 5c860ab..8923ad0 100644 --- a/docs/plan/index.md +++ b/docs/plan/index.md @@ -32,3 +32,6 @@ Each plan is a single line linking to its detail file. All detailed information - [x] [**PLAN-002 Keep Worker source entry and move API TypeScript output to dist**](PLAN-002.md) `2026-03-07` - [x] [**PLAN-003 迁移前端到 Astro + Content Collections**](PLAN-003.md) `2026-03-07` - [x] [**PLAN-004 调整默认语言无前缀路由并整理保留路径命名空间**](PLAN-004.md) `2026-03-09` +- [x] [**PLAN-005 删除 category 并统一使用 tags 分类**](PLAN-005.md) `2026-03-09` +- [x] [**PLAN-006 删除 demo tool 内容与目录**](PLAN-006.md) `2026-03-09` +- [x] [**PLAN-007 为工具内容目录增加 path 映射并支持嵌套目录**](PLAN-007.md) `2026-03-09` diff --git a/docs/task/FE-003.md b/docs/task/FE-003.md new file mode 100644 index 0000000..b42100b --- /dev/null +++ b/docs/task/FE-003.md @@ -0,0 +1,26 @@ +# FE-003 删除 category 数据与路由并统一使用 tags 分类 + +- **status**: completed +- **priority**: P1 +- **createdAt**: 2026-03-09 20:23 +- **owner**: codex +- **completedAt**: 2026-03-09 +- **relatedPlan**: PLAN-005 + +## 描述 + +彻底移除站点中的 `category` 概念,只保留 `tags` 作为内容分类方式: + +- 删除 `content/list.json` 中的 `categories` 字典 +- 删除每个工具列表项上的 `category` 字段 +- 将原分类语义并入 `tags` +- 删除前端 `/category/*` 页面与相关筛选逻辑 +- 详情页、列表页、关联项卡片不再依赖 category badge + +## 验收标准 + +- [x] `content/list.json` 不再包含 `categories` 与 `list[].category` +- [x] 首页筛选与列表展示仅基于 tags +- [x] 详情页不再渲染 category badge +- [x] `/category/*` 页面与相关生成逻辑被删除 +- [x] 构建通过 diff --git a/docs/task/FE-004.md b/docs/task/FE-004.md new file mode 100644 index 0000000..8c4b03a --- /dev/null +++ b/docs/task/FE-004.md @@ -0,0 +1,24 @@ +# FE-004 删除 demo tool 内容与目录 + +- **status**: completed +- **priority**: P1 +- **createdAt**: 2026-03-09 20:29 +- **owner**: codex +- **completedAt**: 2026-03-09 +- **relatedPlan**: PLAN-006 + +## 描述 + +彻底删除仓库中的 `demo` 工具示例内容: + +- 从 `content/list.json` 移除 `demo` +- 从 `featured` 移除 `demo` +- 删除 `content/demo/` 目录 +- 确认静态页面不再生成 `/demo/*` + +## 验收标准 + +- [x] `content/list.json` 中不再包含 `demo` +- [x] `content/demo/` 目录被删除 +- [x] 构建结果不再生成 `/demo/*` +- [x] 前端构建通过 diff --git a/docs/task/FE-005.md b/docs/task/FE-005.md new file mode 100644 index 0000000..77f82fc --- /dev/null +++ b/docs/task/FE-005.md @@ -0,0 +1,24 @@ +# FE-005 为工具内容目录增加 path 映射并支持嵌套目录 + +- **status**: completed +- **priority**: P1 +- **createdAt**: 2026-03-09 20:31 +- **owner**: codex +- **completedAt**: 2026-03-09 +- **relatedPlan**: PLAN-007 + +## 描述 + +在不改变 URL slug 的前提下,为工具内容目录增加 `path` 映射字段,使内容可以放在嵌套目录中: + +- `slug` 继续用于 URL 和站内引用 +- `path` 用于指向 `content/` 下的实际目录 +- 未配置 `path` 时默认回退到 `slug` +- 将 `zzci-chrome` / `zzci-traefik` 迁移到 `content/zzci/chrome` 与 `content/zzci/traefik` + +## 验收标准 + +- [x] `list.json` 支持 `path` +- [x] 内容读取逻辑优先按 `path` 读取目录 +- [x] `zzci-*` 内容迁移到嵌套目录后仍可正常构建 +- [x] URL slug 保持不变 diff --git a/docs/task/index.md b/docs/task/index.md index 33329f5..b8f93fd 100644 --- a/docs/task/index.md +++ b/docs/task/index.md @@ -34,3 +34,6 @@ Each task is a single line linking to its detail file. All detailed information - [x] [**REPO-002 Move API build output out of src and ignore artifacts**](REPO-002.md) `P1` - [x] [**FE-001 迁移前端到 Astro + Content Collections**](FE-001.md) `P1` - [x] [**FE-002 调整默认语言无前缀路由并保留保留路径命名空间**](FE-002.md) `P1` +- [x] [**FE-003 删除 category 数据与路由并统一使用 tags 分类**](FE-003.md) `P1` +- [x] [**FE-004 删除 demo tool 内容与目录**](FE-004.md) `P1` +- [x] [**FE-005 为工具内容目录增加 path 映射并支持嵌套目录**](FE-005.md) `P1` From 36fa4e09fbb602df4a7a21772973851810938660 Mon Sep 17 00:00:00 2001 From: roy <62500486+aazf@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:11:13 +0000 Subject: [PATCH 5/5] refactor: split tags into dedicated content file --- apps/frontend/src/lib/content.ts | 14 ++++- .../src/pages/[locale]/sys/submit.astro | 7 ++- .../src/pages/[locale]/template.astro | 11 ++++ apps/frontend/src/pages/sys/submit.astro | 7 ++- apps/frontend/src/pages/template.astro | 11 ++++ content/list.json | 15 ------ content/tags.json | 15 ++++++ docs/changelog.md | 5 ++ docs/plan/PLAN-008.md | 51 +++++++++++++++++++ docs/plan/index.md | 1 + docs/task/FE-006.md | 23 +++++++++ docs/task/index.md | 1 + 12 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 content/tags.json create mode 100644 docs/plan/PLAN-008.md create mode 100644 docs/task/FE-006.md diff --git a/apps/frontend/src/lib/content.ts b/apps/frontend/src/lib/content.ts index 91c933d..a8e55dd 100644 --- a/apps/frontend/src/lib/content.ts +++ b/apps/frontend/src/lib/content.ts @@ -39,6 +39,11 @@ export interface TagItem { zh: string } +interface ToolListJson { + featured: string[] + list: ToolListItem[] +} + export interface ToolsJson { tags: TagItem[] featured: string[] @@ -244,7 +249,14 @@ export async function renderMarkdown(content: string, locale?: Locale): Promise< } export function getToolsJson(): ToolsJson { - return JSON.parse(readFileSync(join(CONTENT_ROOT, 'list.json'), 'utf-8')) + const listJson = JSON.parse(readFileSync(join(CONTENT_ROOT, 'list.json'), 'utf-8')) as ToolListJson + const tagsJson = JSON.parse(readFileSync(join(CONTENT_ROOT, 'tags.json'), 'utf-8')) as TagItem[] + + return { + tags: tagsJson, + featured: listJson.featured, + list: listJson.list, + } } export function getToolMeta(slug: string, toolsData?: ToolsJson): ToolMeta { diff --git a/apps/frontend/src/pages/[locale]/sys/submit.astro b/apps/frontend/src/pages/[locale]/sys/submit.astro index 326f6b4..08484bb 100644 --- a/apps/frontend/src/pages/[locale]/sys/submit.astro +++ b/apps/frontend/src/pages/[locale]/sys/submit.astro @@ -25,8 +25,9 @@ const content = String.raw` # 1. 在 GitHub 上 Fork 仓库 # 2. 克隆你的 Fork # 3. 添加 content// -# 4. 在 content/list.json 里注册 slug -# 5. 提交、推送并创建 Pull Request +# 4. 如果需要,先在 content/tags.json 里补标签文案 +# 5. 在 content/list.json 里注册 slug +# 6. 提交、推送并创建 Pull Request git clone https://github.com//website.git cd website @@ -37,6 +38,7 @@ mkdir -p content/my-tool \`\`\`text content/ +├── tags.json ├── list.json └── my-tool/ ├── meta.json @@ -55,6 +57,7 @@ content/ ## 说明 - slug 必须是 URL 安全格式,像 \`zzci/chrome\` 这样的展示名应该写进 \`meta.json\`,slug 则用 \`zzci-chrome\`。 +- 新标签的文案定义写在 \`content/tags.json\`,工具条目里只引用对应的 tag key。 - 文档里的站内链接用 \`[[agent-browser]]\` 或 \`[[bkd/data|数据说明]]\` 这种占位符写法,渲染时会自动按语言切换。 - 如果两个工具需要互相关联,可以在 \`meta.json\` 里添加 \`link\`。 ` diff --git a/apps/frontend/src/pages/[locale]/template.astro b/apps/frontend/src/pages/[locale]/template.astro index 8b50e73..736363b 100644 --- a/apps/frontend/src/pages/[locale]/template.astro +++ b/apps/frontend/src/pages/[locale]/template.astro @@ -16,6 +16,7 @@ const content = String.raw` \`\`\`text content/ +├── tags.json ├── list.json └── my-tool/ ├── meta.json @@ -45,6 +46,15 @@ content/ } \`\`\` +## \`content/tags.json\` + +\`\`\`json +[ + { "key": "devTools", "en": "Dev Tools", "zh": "开发工具" }, + { "key": "open-source", "en": "Open Source", "zh": "开源" } +] +\`\`\` + ## \`content/list.json\` 注册示例 \`\`\`json @@ -57,6 +67,7 @@ content/ 只有当内容目录和 URL slug 不一致时才需要写 \`path\`。 例如 \`slug: "zzci-chrome"\` 可以对应到 \`path: "zzci/chrome"\`。 +标签文案定义放在 \`content/tags.json\`,工具条目里只引用 tag key。 ## 常用标签 diff --git a/apps/frontend/src/pages/sys/submit.astro b/apps/frontend/src/pages/sys/submit.astro index cdefe0a..164ce60 100644 --- a/apps/frontend/src/pages/sys/submit.astro +++ b/apps/frontend/src/pages/sys/submit.astro @@ -21,8 +21,9 @@ If you want to add a tool manually, start from the [Template Guide →](/templat # 1. Fork the repo on GitHub # 2. Clone your fork # 3. Add content// -# 4. Register the slug in content/list.json -# 5. Commit, push, and open a Pull Request +# 4. Add tag labels in content/tags.json if needed +# 5. Register the slug in content/list.json +# 6. Commit, push, and open a Pull Request git clone https://github.com//website.git cd website @@ -33,6 +34,7 @@ mkdir -p content/my-tool \`\`\`text content/ +├── tags.json ├── list.json └── my-tool/ ├── meta.json @@ -52,6 +54,7 @@ content/ - Keep the slug URL-safe. Use \`zzci-chrome\`, not \`zzci/chrome\`. - Put display names such as \`zzci/chrome\` in \`meta.json\`. +- Add new tag label definitions to \`content/tags.json\`; use their keys in \`content/list.json\`. - Use placeholders like \`[[agent-browser]]\` or \`[[bkd/data|Data & Storage]]\` for internal links. They are localized automatically. - Add \`link\` in \`meta.json\` if this tool should link to another entry. ` diff --git a/apps/frontend/src/pages/template.astro b/apps/frontend/src/pages/template.astro index 6b36b16..c51af6e 100644 --- a/apps/frontend/src/pages/template.astro +++ b/apps/frontend/src/pages/template.astro @@ -12,6 +12,7 @@ Use this page as the copyable starter for a custom tool entry. \`\`\`text content/ +├── tags.json ├── list.json └── my-tool/ ├── meta.json @@ -41,6 +42,15 @@ content/ } \`\`\` +## \`content/tags.json\` + +\`\`\`json +[ + { "key": "devTools", "en": "Dev Tools", "zh": "开发工具" }, + { "key": "open-source", "en": "Open Source", "zh": "开源" } +] +\`\`\` + ## \`content/list.json\` Entry \`\`\`json @@ -53,6 +63,7 @@ content/ Use \`path\` only when the content directory should differ from the URL slug. For example, \`slug: "zzci-chrome"\` can point to \`path: "zzci/chrome"\`. +Keep tag label definitions in \`content/tags.json\`, and reference their keys from \`content/list.json\`. ## Common Tags diff --git a/content/list.json b/content/list.json index ccde59e..10c567f 100644 --- a/content/list.json +++ b/content/list.json @@ -1,19 +1,4 @@ { - "tags": [ - { "key": "images", "en": "Images", "zh": "图片" }, - { "key": "devTools", "en": "Dev Tools", "zh": "开发工具" }, - { "key": "containers", "en": "Containers", "zh": "容器" }, - { "key": "open-source", "en": "Open Source", "zh": "开源" }, - { "key": "free", "en": "Free", "zh": "免费" }, - { "key": "desktop", "en": "Desktop", "zh": "桌面端" }, - { "key": "cli", "en": "CLI", "zh": "命令行" }, - { "key": "web", "en": "Web", "zh": "网页" }, - { "key": "privacy", "en": "Privacy", "zh": "隐私" }, - { "key": "docker", "en": "Docker", "zh": "Docker" }, - { "key": "self-hosted", "en": "Self-Hosted", "zh": "自托管" }, - { "key": "automation", "en": "Automation", "zh": "自动化" }, - { "key": "proxy", "en": "Proxy", "zh": "代理" } - ], "featured": ["bkd"], "list": [ { diff --git a/content/tags.json b/content/tags.json new file mode 100644 index 0000000..16a7e9d --- /dev/null +++ b/content/tags.json @@ -0,0 +1,15 @@ +[ + { "key": "images", "en": "Images", "zh": "图片" }, + { "key": "devTools", "en": "Dev Tools", "zh": "开发工具" }, + { "key": "containers", "en": "Containers", "zh": "容器" }, + { "key": "open-source", "en": "Open Source", "zh": "开源" }, + { "key": "free", "en": "Free", "zh": "免费" }, + { "key": "desktop", "en": "Desktop", "zh": "桌面端" }, + { "key": "cli", "en": "CLI", "zh": "命令行" }, + { "key": "web", "en": "Web", "zh": "网页" }, + { "key": "privacy", "en": "Privacy", "zh": "隐私" }, + { "key": "docker", "en": "Docker", "zh": "Docker" }, + { "key": "self-hosted", "en": "Self-Hosted", "zh": "自托管" }, + { "key": "automation", "en": "Automation", "zh": "自动化" }, + { "key": "proxy", "en": "Proxy", "zh": "代理" } +] diff --git a/docs/changelog.md b/docs/changelog.md index 0cb7176..691b26a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -42,3 +42,8 @@ Static generation no longer produces `/demo/*` or `/zh/demo/*` pages. Added optional `path` mapping for tool content directories while keeping URL `slug` unchanged. Migrated `zzci-chrome` and `zzci-traefik` to nested `content/zzci/...` directories and updated content loading to resolve files by `path`. + +## 2026-03-09 21:06 [decision] + +Split the site-wide tag dictionary out of `content/list.json` into `content/tags.json`. +Kept the frontend data API stable by having `getToolsJson()` compose `tags.json` and `list.json` into the existing structure. diff --git a/docs/plan/PLAN-008.md b/docs/plan/PLAN-008.md new file mode 100644 index 0000000..b149c92 --- /dev/null +++ b/docs/plan/PLAN-008.md @@ -0,0 +1,51 @@ +# PLAN-008 拆分 tags 到独立 content/tags.json + +- **status**: completed +- **createdAt**: 2026-03-09 21:04 +- **approvedAt**: 2026-03-09 21:04 +- **completedAt**: 2026-03-09 21:06 +- **relatedTask**: FE-006 + +## 现状 + +当前 `content/list.json` 同时承担两种职责: + +- 工具注册表:`featured` 与 `list[]` +- 站点级 tag 字典:`tags[]` + +这会让文件在后续扩展 tag 元数据时不断膨胀,也不利于分别维护“工具索引”和“标签配置”。 + +## 调查上下文 + +前端绝大多数代码都通过 `getToolsJson()` 读取统一结构,再访问 `toolsData.tags`。 + +因此最小改法不是全站改成新接口,而是: + +1. 新增 `content/tags.json` +2. `getToolsJson()` 内部同时读取 `list.json` 与 `tags.json` +3. 对调用方继续返回相同的 `toolsData.tags` + +这样页面层几乎不用改,只需要同步更新模板与投稿说明文档。 + +## 方案 + +采用“物理拆分、逻辑兼容”的方案: + +1. 新建 `content/tags.json` +2. 从 `content/list.json` 移除 `tags[]` +3. 在 `content.ts` 中分别读取两个文件并组装成现有 `ToolsJson` +4. 更新模板页与投稿页文档,说明 `tags.json` 与 `list.json` 的职责划分 + +## 风险 + +- 如果 `tags.json` 路径或格式写错,标签页生成会直接失败 +- 如果只改数据文件、不改文档,后续投稿仍容易按旧结构操作 + +## 范围 + +本次纳入: + +- `tags.json` 拆分 +- 读取逻辑更新 +- 模板与投稿文档同步 +- 构建验证 diff --git a/docs/plan/index.md b/docs/plan/index.md index 8923ad0..9a75ce3 100644 --- a/docs/plan/index.md +++ b/docs/plan/index.md @@ -35,3 +35,4 @@ Each plan is a single line linking to its detail file. All detailed information - [x] [**PLAN-005 删除 category 并统一使用 tags 分类**](PLAN-005.md) `2026-03-09` - [x] [**PLAN-006 删除 demo tool 内容与目录**](PLAN-006.md) `2026-03-09` - [x] [**PLAN-007 为工具内容目录增加 path 映射并支持嵌套目录**](PLAN-007.md) `2026-03-09` +- [x] [**PLAN-008 拆分 tags 到独立 content/tags.json**](PLAN-008.md) `2026-03-09` diff --git a/docs/task/FE-006.md b/docs/task/FE-006.md new file mode 100644 index 0000000..5e3d459 --- /dev/null +++ b/docs/task/FE-006.md @@ -0,0 +1,23 @@ +# FE-006 拆分 tags 到独立 content/tags.json + +- **status**: completed +- **priority**: P1 +- **createdAt**: 2026-03-09 21:04 +- **owner**: codex +- **completedAt**: 2026-03-09 +- **relatedPlan**: PLAN-008 + +## 描述 + +将站点级 tag 字典从 `content/list.json` 中拆分到独立的 `content/tags.json`: + +- `content/list.json` 只保留 `featured` 与 `list` +- `content/tags.json` 维护 tag key 与中英文标签名 +- 前端继续通过统一数据接口读取 tags,不改变页面使用方式 + +## 验收标准 + +- [x] `content/tags.json` 创建完成 +- [x] `content/list.json` 不再内嵌 `tags` +- [x] 前端仍可正常读取 tag 列表并生成标签页 +- [x] 构建和 lint 通过 diff --git a/docs/task/index.md b/docs/task/index.md index b8f93fd..a5b2cd0 100644 --- a/docs/task/index.md +++ b/docs/task/index.md @@ -37,3 +37,4 @@ Each task is a single line linking to its detail file. All detailed information - [x] [**FE-003 删除 category 数据与路由并统一使用 tags 分类**](FE-003.md) `P1` - [x] [**FE-004 删除 demo tool 内容与目录**](FE-004.md) `P1` - [x] [**FE-005 为工具内容目录增加 path 映射并支持嵌套目录**](FE-005.md) `P1` +- [x] [**FE-006 拆分 tags 到独立 content/tags.json**](FE-006.md) `P1`