From bebd9b06ec6d33729737b9bbb9eb2207064f077b Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Tue, 10 Mar 2026 17:46:07 +0000 Subject: [PATCH 1/6] fix: replace broken Algolia search with client-side search, add filters to package showcase Fixes #264 - Search was completely broken due to invalid Algolia API key (403). Changes: - Search.tsx: Replace Algolia dependency with client-side search against pkgs/index.json. Adds score-based relevance ranking, keyboard navigation (arrows/enter/escape), recent searches via localStorage, and label chips. - pkgx.dev.tsx: Remove algoliasearch/react-instantsearch wrapper (no longer needed since search is client-side). - PackageShowcase.tsx: Add category filters (Node.js, Python, Rust, Go, Ruby), sort options (newest/oldest/alphabetical), inline text filter, and empty state messaging. Pagination resets on filter change. All four host variants (pkgx.dev, pkgx.sh, pkgx.app, mash.pkgx.sh) build successfully with TypeScript strict mode. --- src/components/Search.tsx | 296 +++++++++++++++++++++++++------ src/pkgx.dev.tsx | 8 +- src/pkgx.dev/PackageShowcase.tsx | 159 ++++++++++++++--- 3 files changed, 369 insertions(+), 94 deletions(-) diff --git a/src/components/Search.tsx b/src/components/Search.tsx index 0888d2a3..29c8fce1 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -1,92 +1,272 @@ -import { Fade, Grow, InputAdornment, List, ListItem, ListItemButton, ListItemText, Paper, Popper, Skeleton, TextField, Typography, useMediaQuery, useTheme } from '@mui/material'; +import { Fade, Grow, InputAdornment, List, ListItem, ListItemButton, ListItemText, Paper, Popper, TextField, Typography, useMediaQuery, useTheme, Divider, Box, Chip, Stack } from '@mui/material'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { useHits, useSearchBox } from 'react-instantsearch'; + +const RECENT_SEARCHES_KEY = 'pkgx_recent_searches'; +const MAX_RECENT = 5; +const MAX_RESULTS = 15; + +interface PkgEntry { + project: string; + name?: string; + description?: string; + labels?: string[]; +} + +function getRecentSearches(): string[] { + try { + const raw = localStorage.getItem(RECENT_SEARCHES_KEY); + return raw ? JSON.parse(raw) : []; + } catch { + return []; + } +} + +function saveRecentSearch(query: string) { + try { + const existing = getRecentSearches().filter(s => s !== query); + existing.unshift(query); + localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(existing.slice(0, MAX_RECENT))); + } catch { + // localStorage unavailable + } +} + +let pkgCache: PkgEntry[] | null = null; + +async function loadPkgIndex(): Promise { + if (pkgCache) return pkgCache; + const rsp = await fetch('https://pkgx.dev/pkgs/index.json'); + if (!rsp.ok) throw new Error(rsp.statusText); + pkgCache = await rsp.json() as PkgEntry[]; + return pkgCache; +} + +function searchPackages(query: string, packages: PkgEntry[]): PkgEntry[] { + const q = query.toLowerCase().trim(); + if (!q) return []; + + // Score-based matching for relevance ranking + const scored = packages.map(pkg => { + const project = pkg.project.toLowerCase(); + const name = (pkg.name || '').toLowerCase(); + const desc = (pkg.description || '').toLowerCase(); + + let score = 0; + + // Exact match on project or name + if (project === q || name === q) score += 100; + // Starts with query + else if (project.startsWith(q) || name.startsWith(q)) score += 50; + // Last segment of project matches (e.g., "yarn" matches "classic.yarnpkg.com") + else if (project.split('/').pop()?.startsWith(q) || project.split('.').some(seg => seg.startsWith(q))) score += 30; + // Contains query + else if (project.includes(q) || name.includes(q)) score += 20; + // Description contains query + else if (desc.includes(q)) score += 5; + // No match + else return null; + + return { pkg, score }; + }).filter(Boolean) as { pkg: PkgEntry; score: number }[]; + + scored.sort((a, b) => b.score - a.score); + return scored.slice(0, MAX_RESULTS).map(s => s.pkg); +} export default function Search() { - const memoizedSearch = useCallback((query: any, search: (arg0: string) => void) => { - search(query); - }, []); - const { refine } = useSearchBox({ - queryHook: memoizedSearch, - }); const inputRef = useRef(null); + const [isopen, setopen] = useState(false); + const [query, setQuery] = useState(''); + const [results, setResults] = useState([]); + const [packages, setPackages] = useState([]); + const [selectedIndex, setSelectedIndex] = useState(-1); + const [recentSearches, setRecentSearches] = useState(getRecentSearches()); + + const theme = useTheme(); + const isxs = useMediaQuery(theme.breakpoints.down('md')); + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + const shortcut_txt = isMac ? '⌘K' : 'Ctrl+K'; + + // Load package index on mount + useEffect(() => { + loadPkgIndex().then(setPackages).catch(() => {}); + }, []); + + // Cmd+K handler useEffect(() => { - const searchHandler = (event: KeyboardEvent) => { - if (event.key === "k" && event.metaKey) { - if (document.activeElement != inputRef.current) { - inputRef.current!.focus(); + const handler = (event: KeyboardEvent) => { + if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { + event.preventDefault(); + if (document.activeElement !== inputRef.current) { + inputRef.current?.focus(); } else { - inputRef.current!.blur(); + inputRef.current?.blur(); } } }; - document.addEventListener("keydown", searchHandler); - return () => { - document.removeEventListener("keydown", searchHandler); - }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); }, []); - const [isopen, setopen] = useState(false) - const [has_text, set_has_text] = useState(false) - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); + // Keyboard navigation + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (!isopen) return; - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; - const shortcut_txt = isMac ? '⌘K' : 'Ctrl+K' + const items = query ? results : []; + + if (event.key === 'ArrowDown') { + event.preventDefault(); + setSelectedIndex(i => Math.min(i + 1, items.length - 1)); + } else if (event.key === 'ArrowUp') { + event.preventDefault(); + setSelectedIndex(i => Math.max(i - 1, -1)); + } else if (event.key === 'Enter' && selectedIndex >= 0 && selectedIndex < items.length) { + event.preventDefault(); + const pkg = items[selectedIndex]; + saveRecentSearch(query); + window.location.href = `/pkgs/${pkg.project}/`; + } else if (event.key === 'Escape') { + inputRef.current?.blur(); + setopen(false); + } + }; + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, [isopen, query, results, selectedIndex]); + + const handleSearch = useCallback((value: string) => { + setQuery(value); + setSelectedIndex(-1); + if (value.trim()) { + setResults(searchPackages(value, packages)); + } else { + setResults([]); + } + }, [packages]); + + const handleResultClick = (project: string) => { + if (query) saveRecentSearch(query); + setRecentSearches(getRecentSearches()); + }; + + const handleRecentClick = (term: string) => { + setQuery(term); + handleSearch(term); + inputRef.current?.focus(); + }; return <> setopen(true)} - // disabled as prevented clicks on the input in some cases - // onBlur={(e) => setopen(false)} - onChange={e => { - set_has_text(!!e.target.value); - if (!!e.target.value) { - refine(e.target.value); - } - }} + onChange={e => handleSearch(e.target.value)} inputRef={inputRef} InputProps={isxs ? undefined : { endAdornment: {shortcut_txt}, }} /> - {/* - NOTE always open so fade away works - FIXME this occludes content :/ - */} - + - - {has_text && } + + {query.trim() ? ( + + ) : isopen && recentSearches.length > 0 ? ( + + ) : null} - + ; } +function SearchResults({ results, selectedIndex, onClick }: { + results: PkgEntry[]; + selectedIndex: number; + onClick: (project: string) => void; +}) { + if (results.length === 0) { + return ( + + No packages found + + ); + } -function SearchResults() { - const { hits } = useHits() + return ( + + {results.map((pkg, index) => { + const { project, name, description, labels } = pkg; + const displayName = name || project; + const secondary = ( + + {name && name !== project && ( + {project} + )} + {description && ( + + {name && name !== project ? ' — ' : ''}{description} + + )} + + ); - if (hits.length) { - return - {hits.map(fu)} + return ( + + onClick(project)} + sx={{ + '&.Mui-selected': { + backgroundColor: 'action.selected', + }, + }} + > + + {displayName} + {(labels || []).map(l => ( + + ))} + + } + secondary={secondary} + /> + + + ); + })} - } else { - No results - } + ); +} - function fu(input: any) { - const {project, displayName, brief} = input - const tertiary = displayName != project ? project : undefined; - return - - - - - } -} \ No newline at end of file +function RecentSearches({ searches, onSelect }: { searches: string[]; onSelect: (term: string) => void }) { + return ( + + Recent searches + + {searches.map(term => ( + + onSelect(term)}> + + + + ))} + + + ); +} diff --git a/src/pkgx.dev.tsx b/src/pkgx.dev.tsx index 7b93abcb..9bcec329 100644 --- a/src/pkgx.dev.tsx +++ b/src/pkgx.dev.tsx @@ -3,8 +3,6 @@ import { Route, BrowserRouter as Router, Routes, useLocation } from 'react-route import PackageShowcase from './pkgx.dev/PackageShowcase'; import PackageListing from './pkgx.dev/PackageListing'; import PrivacyPolicy from './pkgx.dev/PrivacyPolicy'; -import { InstantSearch } from 'react-instantsearch'; -import algoliasearch from 'algoliasearch/lite'; import TermsOfUse from './pkgx.dev/TermsOfUse'; import * as ReactDOM from 'react-dom/client'; import Masthead from './components/Masthead'; @@ -20,8 +18,6 @@ import TeaProtocol from './pkgx.dev/TeaProtocol'; import CoinListLandingPage from './pkgx.dev/CoinListLandingPage'; -const searchClient = algoliasearch('UUTLHX01W7', '__819a841ca219754c38918b8bcbbbfea7'); - ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( @@ -53,9 +49,7 @@ function MyMasthead() { let gh = `https://github.com/pkgxdev/` if (pathname.startsWith('/pkgs')) gh += 'pantry/' - const search = - - + const search = ; const stuff = <> diff --git a/src/pkgx.dev/PackageShowcase.tsx b/src/pkgx.dev/PackageShowcase.tsx index dddaf2af..17740450 100644 --- a/src/pkgx.dev/PackageShowcase.tsx +++ b/src/pkgx.dev/PackageShowcase.tsx @@ -1,49 +1,157 @@ import Grid from '@mui/material/Grid2'; -import { Alert, Box, Card, CardActionArea, CardContent, CardMedia, Chip, Skeleton, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { Alert, Box, Card, CardActionArea, CardContent, CardMedia, Chip, FormControl, InputLabel, MenuItem, Select, Skeleton, Stack, TextField, ToggleButton, ToggleButtonGroup, Typography, useMediaQuery, useTheme } from "@mui/material"; import useInfiniteScroll from 'react-infinite-scroll-hook'; -import { CSSProperties, useState } from "react"; +import { CSSProperties, useMemo, useState } from "react"; import get_pkg_name from "../utils/pkg-name"; import { useAsync } from "react-use"; +// Category definitions for filtering +const CATEGORIES: Record boolean }> = { + all: { label: 'All', match: () => true }, + node: { label: 'Node.js', match: (p) => p.labels?.includes('node') ?? false }, + python: { label: 'Python', match: (p) => p.labels?.includes('python') ?? false }, + rust: { label: 'Rust', match: (p) => p.labels?.includes('rust') ?? false }, + go: { label: 'Go', match: (p) => p.labels?.includes('go') ?? false }, + ruby: { label: 'Ruby', match: (p) => p.labels?.includes('ruby') ?? false }, +}; + +type SortOption = 'newest' | 'oldest' | 'alphabetical'; + export default function Showcase() { const theme = useTheme(); const isxs = useMediaQuery(theme.breakpoints.down('md')); - const { loading, items, hasNextPage, error, loadMore, total } = useLoadItems(); + const [category, setCategory] = useState('all'); + const [sortBy, setSortBy] = useState('newest'); + const [filterText, setFilterText] = useState(''); + + const { loading, allItems, error } = useLoadAllItems(); + + // Filter and sort + const processedItems = useMemo(() => { + let items = [...allItems]; + + // Category filter + if (category !== 'all' && CATEGORIES[category]) { + items = items.filter(CATEGORIES[category].match); + } + + // Text filter + if (filterText.trim()) { + const q = filterText.toLowerCase().trim(); + items = items.filter(pkg => { + const project = pkg.project?.toLowerCase() || ''; + const name = (pkg.name || '').toLowerCase(); + const desc = (pkg.description || pkg.brief || '').toLowerCase(); + return project.includes(q) || name.includes(q) || desc.includes(q); + }); + } + + // Sort + switch (sortBy) { + case 'newest': + items.sort((a, b) => new Date(b.birthtime || 0).getTime() - new Date(a.birthtime || 0).getTime()); + break; + case 'oldest': + items.sort((a, b) => new Date(a.birthtime || 0).getTime() - new Date(b.birthtime || 0).getTime()); + break; + case 'alphabetical': + items.sort((a, b) => (a.name || a.project || '').localeCompare(b.name || b.project || '')); + break; + } + + return items; + }, [allItems, category, sortBy, filterText]); + + // Paginate the processed results + const [visibleCount, setVisibleCount] = useState(25); + const visibleItems = processedItems.slice(0, visibleCount); + const hasNextPage = visibleCount < processedItems.length; const [sentryRef] = useInfiniteScroll({ loading, hasNextPage, - onLoadMore: loadMore, - // When there is an error, we stop infinite loading. - // It can be reactivated by setting "error" state as undefined. + onLoadMore: () => setVisibleCount(c => Math.min(c + 25, processedItems.length)), disabled: !!error, - // `rootMargin` is passed to `IntersectionObserver`. - // We can use it to trigger 'onLoadMore' when the sentry comes near to become - // visible, instead of becoming fully visible on the screen. rootMargin: '0px 0px 800px 0px', - delayInMs: 0 + delayInMs: 0, }); - const count = total && { + setVisibleCount(25); + }, [category, sortBy, filterText]); + + const count = {new Intl.NumberFormat().format(total)} + >{new Intl.NumberFormat().format(processedItems.length)}; return <> Available Packages {count} + + {/* Filter Controls */} + + setFilterText(e.target.value)} + sx={{ minWidth: 200 }} + /> + + val && setCategory(val)} + size="small" + sx={{ flexWrap: 'wrap' }} + > + {Object.entries(CATEGORIES).map(([key, { label }]) => ( + + {label} + + ))} + + + + Sort by + + + + - {items.map(item => + {visibleItems.map(item => )} {(loading || hasNextPage) && Array.from({ length: isxs ? 2 : 4 }).map((_, index) => ( - + ))} {error && {error.message}} + {!loading && processedItems.length === 0 && !error && ( + + + No packages match your filters. Try adjusting your search or category. + + + )} } @@ -58,25 +166,18 @@ interface Package { isLoader?: boolean; } -function useLoadItems() { - const [index, setIndex] = useState(0); - - const async = useAsync(async () => { +function useLoadAllItems() { + const async_result = useAsync(async () => { const rsp = await fetch('https://pkgx.dev/pkgs/index.json') if (!rsp.ok) throw new Error(rsp.statusText) - const data = await rsp.json() as Package[] - setIndex(Math.min(data.length, 25)) - return data + return await rsp.json() as Package[] }); return { - loading: async.loading, - items: (async.value ?? []).slice(0, index), - hasNextPage: async.value ? index < async.value.length : false, - error: async.error, - loadMore: () => setIndex(index => Math.min(index + 25, async.value?.length ?? 0)), - total: async.value?.length - } + loading: async_result.loading, + allItems: async_result.value ?? [], + error: async_result.error, + }; } function PkgCard({project, description, brief, name, labels, isLoader}: Package) { @@ -126,4 +227,4 @@ function PkgCard({project, description, brief, name, labels, isLoader}: Package) ) -} \ No newline at end of file +} From 641004477d63497b734ae1fefd54098b6d470044 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Tue, 10 Mar 2026 18:50:32 +0000 Subject: [PATCH 2/6] feat: Phase 2 - Tailwind v4 migration + MUI removal - Replaced all MUI (Material UI) components with Tailwind v4 CSS - Added @tailwindcss/vite plugin (Lightning CSS engine) - Created utility: cn() (clsx + tailwind-merge) - Created utility: useIsMobile() (replaces MUI useMediaQuery) - Migrated 27 components/pages from MUI to Tailwind - Replaced lucide-react icons for MUI icons - Replaced mui-markdown with native Markdown component - Removed: @mui/material, @mui/icons-material, @emotion/react, @emotion/styled, mui-markdown - Added: tailwindcss v4.2.1, @tailwindcss/vite, class-variance-authority, clsx, tailwind-merge, lucide-react - All 4 VITE_HOST variants build successfully (0 TypeScript errors) - Bundle size reduced: pkgx.sh 222KB (was ~400KB+ with MUI) --- package-lock.json | 1466 ++++++++++++-------------- package.json | 11 +- src/assets/app.css | 168 +++ src/assets/main.css | 66 -- src/components/Discord.tsx | 17 +- src/components/Footer.tsx | 176 ++-- src/components/HeroTypography.tsx | 32 +- src/components/HomebrewBadge.tsx | 70 +- src/components/Masthead.tsx | 39 +- src/components/Search.tsx | 309 +++--- src/components/Stars.tsx | 67 +- src/components/Terminal.tsx | 81 +- src/mash.pkgx.sh.tsx | 90 +- src/mash.pkgx.sh/Hero.tsx | 104 +- src/mash.pkgx.sh/Listing.tsx | 43 +- src/mash.pkgx.sh/Script.tsx | 135 ++- src/mash.pkgx.sh/ScriptDetail.tsx | 121 ++- src/pkgx.app.tsx | 161 ++- src/pkgx.dev.tsx | 105 +- src/pkgx.dev/CoinListLandingPage.tsx | 637 ++++------- src/pkgx.dev/HomeFeed.tsx | 333 +++--- src/pkgx.dev/PackageListing.tsx | 513 +++++---- src/pkgx.dev/PackageShowcase.tsx | 284 +++-- src/pkgx.dev/PrivacyPolicy.tsx | 8 +- src/pkgx.dev/TeaProtocol.tsx | 312 +++--- src/pkgx.dev/TermsOfUse.tsx | 11 +- src/pkgx.sh.tsx | 62 +- src/pkgx.sh/Hero.tsx | 141 ++- src/pkgx.sh/Landing.tsx | 747 ++++++------- src/utils/cn.ts | 6 + src/utils/theme.tsx | 39 - src/utils/useIsMobile.ts | 13 + vite.config.ts | 5 +- 33 files changed, 3023 insertions(+), 3349 deletions(-) create mode 100644 src/assets/app.css delete mode 100644 src/assets/main.css create mode 100644 src/utils/cn.ts delete mode 100644 src/utils/theme.tsx create mode 100644 src/utils/useIsMobile.ts diff --git a/package-lock.json b/package-lock.json index 0b764c67..0c33d503 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,14 +9,12 @@ "version": "5.0.0", "dependencies": { "@aws-sdk/client-s3": "^3.438.0", - "@emotion/react": "latest", - "@emotion/styled": "latest", - "@mui/icons-material": "latest", - "@mui/material": "latest", "algoliasearch": "^4.20.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "is-what": "^4.1.16", "libpkgx": "^0.15.1", - "mui-markdown": "^1.1.9", + "lucide-react": "^0.577.0", "react": "latest", "react-dom": "latest", "react-helmet": "^6.1.0", @@ -26,15 +24,18 @@ "react-router-dom": "^6.18.0", "react-use": "^17.4.0", "showdown": "^2.1.0", + "tailwind-merge": "^3.5.0", "yaml": "^2.3.3" }, "devDependencies": { + "@tailwindcss/vite": "^4.2.1", "@types/node": "^20.6.2", "@types/react": "latest", "@types/react-dom": "latest", "@types/react-helmet": "^6.1.11", "@types/showdown": "^2.0.3", "@vitejs/plugin-react": "latest", + "tailwindcss": "^4.2.1", "typescript": "latest", "vite": "latest" } @@ -1061,6 +1062,7 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", @@ -1123,6 +1125,7 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.26.5", @@ -1156,6 +1159,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", @@ -1197,6 +1201,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1206,6 +1211,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1239,6 +1245,7 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.26.5" @@ -1298,6 +1305,7 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.9", @@ -1312,6 +1320,7 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", @@ -1330,6 +1339,7 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1361,152 +1371,6 @@ "integrity": "sha512-oYWcD7CpERZy/TXMTM9Tgh1HD/POHlbY9WpzmAk+5H8DohcxG415Qws8yLGlim3EaKBT2v3lJv01x4G0BosnaQ==", "license": "MIT" }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", - "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", - "license": "MIT", - "dependencies": { - "@emotion/memoize": "^0.9.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "license": "MIT", - "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT" - }, - "node_modules/@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/is-prop-valid": "^1.3.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "license": "MIT" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "license": "MIT" - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", @@ -1945,6 +1809,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -1955,10 +1820,22 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1968,267 +1845,29 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz", - "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/icons-material": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.1.tgz", - "integrity": "sha512-wsxFcUTQxt4s+7Bg4GgobqRjyaHLmZGNOs+HJpbwrwmLbT6mhIJxhpqsKzzWq9aDY8xIe7HCjhpH7XI5UD6teA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^6.4.1", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz", - "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.1", - "@mui/system": "^6.4.1", - "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.1", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.12", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^19.0.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.1", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@mui/material-pigment-css": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz", - "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", - "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.5", - "@emotion/serialize": "^1.3.3", - "@emotion/sheet": "^1.4.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz", - "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.1", - "@mui/styled-engine": "^6.4.0", - "@mui/types": "^7.2.21", - "@mui/utils": "^6.4.1", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.21", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", - "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz", - "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.21", - "@types/prop-types": "^15.7.14", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/@remix-run/router": { "version": "1.21.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.1.tgz", @@ -3221,53 +2860,325 @@ "node": ">=18.0.0" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "node_modules/@tailwindcss/node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.1.tgz", + "integrity": "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@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" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "node_modules/@tailwindcss/oxide": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.1.tgz", + "integrity": "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "engines": { + "node": ">= 20" + }, + "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" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz", + "integrity": "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz", + "integrity": "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/dom-speech-recognition": { - "version": "0.0.1", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz", + "integrity": "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz", + "integrity": "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz", + "integrity": "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz", + "integrity": "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz", + "integrity": "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz", + "integrity": "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz", + "integrity": "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz", + "integrity": "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", + "integrity": "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz", + "integrity": "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.1.tgz", + "integrity": "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.1", + "@tailwindcss/oxide": "4.2.1", + "tailwindcss": "4.2.1" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/dom-speech-recognition": { + "version": "0.0.1", "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz", "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==", "license": "MIT" @@ -3307,25 +3218,6 @@ "undici-types": "~6.19.2" } }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "license": "MIT" - }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "license": "MIT", - "optional": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT" - }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -3336,6 +3228,7 @@ "version": "19.0.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -3361,15 +3254,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*" - } - }, "node_modules/@types/showdown": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.6.tgz", @@ -3444,21 +3328,6 @@ "algoliasearch": ">= 3.1 < 6" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, "node_modules/bowser": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", @@ -3498,15 +3367,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001695", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", @@ -3528,6 +3388,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -3552,12 +3424,6 @@ "node": "^12.20.0 || >=14" } }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, "node_modules/copy-to-clipboard": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", @@ -3567,31 +3433,6 @@ "toggle-selection": "^1.0.6" } }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, "node_modules/css-in-js-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", @@ -3633,6 +3474,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3646,14 +3488,14 @@ } } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" } }, "node_modules/electron-to-chromium": { @@ -3663,13 +3505,18 @@ "dev": true, "license": "ISC" }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/enhanced-resolve": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", + "dev": true, "license": "MIT", "dependencies": { - "is-arrayish": "^0.2.1" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" } }, "node_modules/error-stack-parser": { @@ -3732,18 +3579,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3783,12 +3618,6 @@ "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==", "license": "MIT" }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "license": "MIT" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3804,15 +3633,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3827,6 +3647,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -3838,18 +3659,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/hogan.js": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", @@ -3862,21 +3671,6 @@ "hulk": "bin/hulk" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/htm": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/htm/-/htm-3.1.1.tgz", @@ -3889,22 +3683,6 @@ "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", "license": "BSD-3-Clause" }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/inline-style-prefixer": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz", @@ -3946,27 +3724,6 @@ "algoliasearch": ">= 3.1 < 6" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-what": { "version": "4.1.16", "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", @@ -3985,6 +3742,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-cookie": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", @@ -4001,6 +3768,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -4009,12 +3777,6 @@ "node": ">=6" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4050,11 +3812,266 @@ "undici": "^5.21.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" + "node_modules/lightningcss": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", + "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz", + "integrity": "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz", + "integrity": "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz", + "integrity": "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz", + "integrity": "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz", + "integrity": "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz", + "integrity": "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz", + "integrity": "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz", + "integrity": "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz", + "integrity": "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz", + "integrity": "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.31.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz", + "integrity": "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, "node_modules/loose-envify": { "version": "1.4.0", @@ -4078,17 +4095,23 @@ "yallist": "^3.0.2" } }, - "node_modules/markdown-to-jsx": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.3.tgz", - "integrity": "sha512-o35IhJDFP6Fv60zPy+hbvZSQMmgvSGdK5j8NRZ7FeZMY+Bgqw+dSg7SC1ZEzC26++CiOUCqkbq96/c3j/FfTEQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 10" - }, + "node_modules/lucide-react": { + "version": "0.577.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.577.0.tgz", + "integrity": "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==", + "license": "ISC", "peerDependencies": { - "react": ">= 0.14.0" + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/mdn-data": { @@ -4111,25 +4134,9 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, - "node_modules/mui-markdown": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/mui-markdown/-/mui-markdown-1.2.5.tgz", - "integrity": "sha512-zgLSXxYgHmUkUZ6mp2aM8C1vcoAsCyQLyvvaiSf8AAutNnAXhA8tlBiiGg8hOvX77VQs1A1dssbOyT/W2ytonA==", - "license": "MIT", - "optionalDependencies": { - "prism-react-renderer": "^2.0.3" - }, - "peerDependencies": { - "@emotion/react": "^11.10.8", - "@emotion/styled": "^11.10.8", - "@mui/material": "^5.12.2 || ^6.0.0", - "markdown-to-jsx": "^7.3.0", - "react": ">= 17.0.2", - "react-dom": ">= 17.0.2" - } - }, "node_modules/nano-css": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz", @@ -4212,55 +4219,11 @@ "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==", "license": "MIT" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/postcss": { @@ -4302,20 +4265,6 @@ "url": "https://opencollective.com/preact" } }, - "node_modules/prism-react-renderer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz", - "integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/prismjs": "^1.26.0", - "clsx": "^2.0.0" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4507,12 +4456,6 @@ "react": ">=16.8.0" } }, - "node_modules/react-is": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -4564,22 +4507,6 @@ "react": "^16.3.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/react-universal-interface": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", @@ -4627,35 +4554,6 @@ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", "license": "MIT" }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -4781,15 +4679,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4851,22 +4740,35 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "license": "MIT" }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", + "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", + "dev": true, "license": "MIT" }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/throttle-debounce": { diff --git a/package.json b/package.json index 6c49685f..655ddb36 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,12 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.438.0", - "@emotion/react": "latest", - "@emotion/styled": "latest", - "@mui/icons-material": "latest", - "@mui/material": "latest", "algoliasearch": "^4.20.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "is-what": "^4.1.16", "libpkgx": "^0.15.1", - "mui-markdown": "^1.1.9", + "lucide-react": "^0.577.0", "react": "latest", "react-dom": "latest", "react-helmet": "^6.1.0", @@ -27,15 +25,18 @@ "react-router-dom": "^6.18.0", "react-use": "^17.4.0", "showdown": "^2.1.0", + "tailwind-merge": "^3.5.0", "yaml": "^2.3.3" }, "devDependencies": { + "@tailwindcss/vite": "^4.2.1", "@types/node": "^20.6.2", "@types/react": "latest", "@types/react-dom": "latest", "@types/react-helmet": "^6.1.11", "@types/showdown": "^2.0.3", "@vitejs/plugin-react": "latest", + "tailwindcss": "^4.2.1", "typescript": "latest", "vite": "latest" }, diff --git a/src/assets/app.css b/src/assets/app.css new file mode 100644 index 00000000..7d2e980f --- /dev/null +++ b/src/assets/app.css @@ -0,0 +1,168 @@ +@import "tailwindcss"; + +/* ===== pkgx Design Tokens (CSS Variables) ===== */ +@theme { + --color-primary: #4156E1; + --color-secondary: #F26212; + --color-background: #0D1117; + --color-surface: #0D1117; + --color-text-primary: #EDF2EF; + --color-text-secondary: rgba(237, 242, 239, 0.7); + --color-border: rgba(149, 178, 184, 0.3); + --color-error: #f44336; + --color-success: #22c55e; + --color-pkgx-purple: #4156E1; + --color-pkgx-orange: #F26212; + --color-pkgx-teal: #74FAD1; +} + +/* ===== Base Styles ===== */ + +html, body { + background: var(--color-background); + color: var(--color-text-primary); + font-family: 'Roboto', sans-serif; +} + +::selection { + background: rgba(68, 79, 226, 0.35); +} + +/* ===== Terminal Component ===== */ +[data-terminal]:before { + user-select: none; + content: ''; + position: absolute; + top: 12px; + left: 12px; + display: inline-block; + width: 12px; + height: 12px; + border-radius: 50%; + background: rgb(255, 95, 86); + box-shadow: 20px 0 0 rgb(255, 189, 46), 40px 0 0 rgb(39, 201, 63); +} + +[data-terminal] { + background: radial-gradient(circle, #070C14 0.1%, rgb(13, 17, 23) 100%) no-repeat; + position: relative; + box-shadow: 0 0 150px 10px rgb(65, 86, 225, 0.1); +} + +/* ===== Glow/Halo Effect ===== */ +.halo { + box-shadow: 0 0 150px 10px rgb(65, 86, 225, 0.1); +} + +/* ===== Text Gradient ===== */ +.text-gradient { + background: linear-gradient(to right, #4156E1 0%, #F26412 75%, #EDF2EF 130%); + background-clip: text; + color: transparent; +} + +/* ===== Inline Code ===== */ +code { + border: 0.5px solid rgba(149, 178, 184, 0.3); + background-color: rgba(149, 178, 184, 0.067); + border-radius: 2px; + padding: 0 0.2em; + margin: 0 -0.1em; + display: inline-block; + line-height: 1.3; + color: var(--color-text-primary); +} + +/* ===== Card Styles ===== */ +.card { + border-radius: 0.5rem; + border: 1px solid rgba(149, 178, 184, 0.3); + background-color: #0D1117; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.card:hover { + border-color: rgba(149, 178, 184, 0.5); +} + +.card-raised { + border-radius: 0.5rem; + border: 1px solid rgba(149, 178, 184, 0.3); + background-color: #0D1117; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.card-outlined { + border-radius: 0.5rem; + border: 1px solid rgba(149, 178, 184, 0.3); + background-color: #0D1117; +} + +/* ===== Custom Scrollbar ===== */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: rgba(149, 178, 184, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(149, 178, 184, 0.5); +} + +/* ===== Font Face ===== */ +@font-face { + font-family: 'shader'; + src: url('https://pkgx.sh/fonts/Shader_W_Lt.woff2') format('woff2'), + url('https://pkgx.sh/fonts/Shader_W_Lt.woff') format('woff'); + font-weight: 300; + font-style: normal; +} + +/* ===== Animations ===== */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in { + animation: fadeIn 0.3s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.4s ease-out; +} + +/* ===== Link Styles ===== */ +a { + color: inherit; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* ===== Utility: List Reset ===== */ +.list-none-reset { + list-style-type: none; + padding-left: 0; + margin-left: 0; +} + +/* ===== Editor List Fix ===== */ +.editors li { + margin-left: -20px; +} diff --git a/src/assets/main.css b/src/assets/main.css deleted file mode 100644 index 63965f84..00000000 --- a/src/assets/main.css +++ /dev/null @@ -1,66 +0,0 @@ -html, body { - background: #0D1117 -} - -::selection { - background: rgba(68, 79, 226, 0.35); -} - -[data-terminal]:before { - user-select: none; - content: ''; - position: absolute; - top: 12px; - left: 12px; - display: inline-block; - width: 12px; - height: 12px; - border-radius: 50%; - /* A little hack to display the window buttons in one pseudo element. */ - background: rgb(255, 95, 86); - -webkit-box-shadow: 20px 0 0 rgb(255, 189, 46), 40px 0 0 rgb(39, 201, 63); - box-shadow: 20px 0 0 rgb(255, 189, 46), 40px 0 0 rgb(39, 201, 63); -} - -[data-terminal] { - background: radial-gradient(circle, #070C14 0.1%, rgb(13, 17, 23) 100%) no-repeat; - position: relative; - box-shadow: 0 0 150px 10px rgb(65, 86, 225, 0.1); -} - -.halo { - box-shadow: 0 0 150px 10px rgb(65, 86, 225, 0.1); -} - -.text-gradient, .text-gradient-white { - background: linear-gradient(to right, #4156E1 0%, #F26412 75%, #EDF2EF 130%); - background-clip: text; - color: transparent; - font-size: 48px; -} - -code { - border: 0.5px solid rgba(149, 178, 184, 0.3); - background-color: rgba(149, 178, 184, 0.067); - border-radius: 2px; - padding: 0 0.2em; - margin: 0 -0.1em; - display: inline-block; - line-height: 1.3 -} - -.MuiPaper-root code { - color: #EDF2EF; -} - -.editors li { - margin-left: -20px; -} - -@font-face { - font-family: 'shader'; - src: url('https://pkgx.sh/fonts/Shader_W_Lt.woff2') format('woff2'), - url('https://pkgx.sh/fonts/Shader_W_Lt.woff') format('woff'); - font-weight: 300; - font-style: normal; -} diff --git a/src/components/Discord.tsx b/src/components/Discord.tsx index ce38d61a..4cf7fc25 100644 --- a/src/components/Discord.tsx +++ b/src/components/Discord.tsx @@ -1,10 +1,13 @@ import discord from "../assets/wordmarks/discord.svg"; -import { IconButton, Box } from "@mui/material"; export default function Discord() { - //FIXME hardcoding the size sucks - - return - - -} \ No newline at end of file + return ( + + Discord + + ); +} diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index ef03c961..9fd0cc8b 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,92 +1,104 @@ -import Grid from '@mui/material/Grid2'; -import { Link, LinkProps, Typography, useTheme, useMediaQuery, Box, Button, Stack } from "@mui/material"; -import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; -import ArrowOutwardIcon from '@mui/icons-material/CallMade'; +import { ArrowRight, ArrowUpRight } from "lucide-react"; +import { useIsMobile } from "../utils/useIsMobile"; import tea from "../assets/wordmarks/tea.svg"; import logo from "../assets/pkgx.svg"; - export default function Footer() { - const year = new Date().getFullYear() - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); + const year = new Date().getFullYear(); + const isxs = useIsMobile(); - const ul_style = { - listStyleType: 'none', - paddingLeft: 0, - marginLeft: 0, - fontSize: 14, - marginTop: 10 - } - const link_props: LinkProps = { - color: 'text.secondary', - underline: 'none', - } - const c = - ©{year} PKGX INC. All Rights Reserved. - ; + const linkClass = "text-[rgba(237,242,239,0.7)] hover:text-[#EDF2EF] no-underline transition-colors text-sm"; - const icon = + const copyright = ( +

+ ©{year} PKGX INC. All Rights Reserved. +

+ ); - return - - - - pkgx is a core contributor to the tea protocol - - - + return ( +
+ {/* Tea Partnership Banner */} +
+ tea +

pkgx is a core contributor to the tea protocol

+ + Learn More + +
- - - - {!isxs && c} - - - - Product - -
    -
  • pkgx
  • -
  • oss.app
  • -
  • mash
  • -
  • docs
  • -
  • pkgs
  • -
-
- - - Company - -
    -
  • Home
  • -
  • Privacy Policy
  • -
  • Terms of Use
  • -
  • Blog
  • -
  • Press Kit
  • -
  • Contact{icon}
  • -
-
- - - Community - -
    -
  • GitHub{icon}
  • -
  • 𝕏{icon}
  • -
  • Discord{icon}
  • -
  • irc:#pkgx{icon}
  • -
-
- {isxs && - {c} - } -
- -} + {/* Footer Grid */} +
+ {/* Logo Column */} +
+ pkgx + {!isxs && copyright} +
+ + {/* Product */} +
+
Product
+ +
+ + {/* Company */} + + + {/* Community */} +
+
Community
+ +
-function Li({ children }: { children: React.ReactNode }) { - return
  • {children}
  • + {isxs &&
    {copyright}
    } +
    +
    + ); } diff --git a/src/components/HeroTypography.tsx b/src/components/HeroTypography.tsx index b2bba6a0..eb0219b5 100644 --- a/src/components/HeroTypography.tsx +++ b/src/components/HeroTypography.tsx @@ -1,16 +1,20 @@ -import { Typography, styled } from "@mui/material"; +import { cn } from "../utils/cn"; -const StyledTypography = styled(Typography)(({ theme }) => ({ - textAlign: "center", - fontWeight: 300, - textTransform: 'uppercase', - fontSize: 80, - fontFamily: 'shader, Roboto, sans-serif', - [theme.breakpoints.down("md")]: { - fontSize: 40, - }, -})); - -export default function HeroTypography({ children, ...props }: React.ComponentProps) { - return {children} +export default function HeroTypography({ + children, + className, + ...props +}: React.HTMLAttributes) { + return ( +

    + {children} +

    + ); } diff --git a/src/components/HomebrewBadge.tsx b/src/components/HomebrewBadge.tsx index c76155af..a364aed3 100644 --- a/src/components/HomebrewBadge.tsx +++ b/src/components/HomebrewBadge.tsx @@ -1,57 +1,29 @@ -import { Box, Tooltip, useTheme, useMediaQuery } from "@mui/material"; +import { useIsMobile } from "../utils/useIsMobile"; -/** - * HomebrewBadge — lightweight badge indicating pkgx's Homebrew heritage. - * Uses a styled Box instead of MUI Chip to minimize bundle impact. - * - * Accessibility: focusable, tooltip, role="status", aria-label. - * Responsive: smaller text on mobile. - */ export default function HomebrewBadge() { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down("md")); + const isxs = useIsMobile(); return ( - - - - {isxs ? "By Homebrew's creator" : "From the creator of Homebrew"} - - + + {isxs ? "By Homebrew's creator" : "From the creator of Homebrew"} + ); } diff --git a/src/components/Masthead.tsx b/src/components/Masthead.tsx index 3d94499d..c33c057b 100644 --- a/src/components/Masthead.tsx +++ b/src/components/Masthead.tsx @@ -1,18 +1,27 @@ -import { Stack, Link, Box, useTheme, useMediaQuery } from "@mui/material" -import logo from "../assets/wordmarks/pkgx.svg" -import Search from "./Search" +import { useIsMobile } from "../utils/useIsMobile"; +import logo from "../assets/wordmarks/pkgx.svg"; -export default function Masthead({ children, left }: { children?: React.ReactNode, left?: React.ReactNode }) { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); +export default function Masthead({ + children, + left, +}: { + children?: React.ReactNode; + left?: React.ReactNode; +}) { + const isxs = useIsMobile(); - return - - - - {left} - - {children} - {/*TODO isxs ? undefined : */} - + return ( +
    + + pkgx + + {left} +
    + {children} +
    + ); } diff --git a/src/components/Search.tsx b/src/components/Search.tsx index 29c8fce1..3e3bb149 100644 --- a/src/components/Search.tsx +++ b/src/components/Search.tsx @@ -1,7 +1,8 @@ -import { Fade, Grow, InputAdornment, List, ListItem, ListItemButton, ListItemText, Paper, Popper, TextField, Typography, useMediaQuery, useTheme, Divider, Box, Chip, Stack } from '@mui/material'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from "react"; +import { useIsMobile } from "../utils/useIsMobile"; +import { cn } from "../utils/cn"; -const RECENT_SEARCHES_KEY = 'pkgx_recent_searches'; +const RECENT_SEARCHES_KEY = "pkgx_recent_searches"; const MAX_RECENT = 5; const MAX_RESULTS = 15; @@ -23,7 +24,7 @@ function getRecentSearches(): string[] { function saveRecentSearch(query: string) { try { - const existing = getRecentSearches().filter(s => s !== query); + const existing = getRecentSearches().filter((s) => s !== query); existing.unshift(query); localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(existing.slice(0, MAX_RECENT))); } catch { @@ -35,9 +36,9 @@ let pkgCache: PkgEntry[] | null = null; async function loadPkgIndex(): Promise { if (pkgCache) return pkgCache; - const rsp = await fetch('https://pkgx.dev/pkgs/index.json'); + const rsp = await fetch("https://pkgx.dev/pkgs/index.json"); if (!rsp.ok) throw new Error(rsp.statusText); - pkgCache = await rsp.json() as PkgEntry[]; + pkgCache = (await rsp.json()) as PkgEntry[]; return pkgCache; } @@ -45,47 +46,45 @@ function searchPackages(query: string, packages: PkgEntry[]): PkgEntry[] { const q = query.toLowerCase().trim(); if (!q) return []; - // Score-based matching for relevance ranking - const scored = packages.map(pkg => { - const project = pkg.project.toLowerCase(); - const name = (pkg.name || '').toLowerCase(); - const desc = (pkg.description || '').toLowerCase(); + const scored = packages + .map((pkg) => { + const project = pkg.project.toLowerCase(); + const name = (pkg.name || "").toLowerCase(); + const desc = (pkg.description || "").toLowerCase(); - let score = 0; + let score = 0; + if (project === q || name === q) score += 100; + else if (project.startsWith(q) || name.startsWith(q)) score += 50; + else if ( + project.split("/").pop()?.startsWith(q) || + project.split(".").some((seg) => seg.startsWith(q)) + ) + score += 30; + else if (project.includes(q) || name.includes(q)) score += 20; + else if (desc.includes(q)) score += 5; + else return null; - // Exact match on project or name - if (project === q || name === q) score += 100; - // Starts with query - else if (project.startsWith(q) || name.startsWith(q)) score += 50; - // Last segment of project matches (e.g., "yarn" matches "classic.yarnpkg.com") - else if (project.split('/').pop()?.startsWith(q) || project.split('.').some(seg => seg.startsWith(q))) score += 30; - // Contains query - else if (project.includes(q) || name.includes(q)) score += 20; - // Description contains query - else if (desc.includes(q)) score += 5; - // No match - else return null; - - return { pkg, score }; - }).filter(Boolean) as { pkg: PkgEntry; score: number }[]; + return { pkg, score }; + }) + .filter(Boolean) as { pkg: PkgEntry; score: number }[]; scored.sort((a, b) => b.score - a.score); - return scored.slice(0, MAX_RESULTS).map(s => s.pkg); + return scored.slice(0, MAX_RESULTS).map((s) => s.pkg); } export default function Search() { const inputRef = useRef(null); + const popperRef = useRef(null); const [isopen, setopen] = useState(false); - const [query, setQuery] = useState(''); + const [query, setQuery] = useState(""); const [results, setResults] = useState([]); const [packages, setPackages] = useState([]); const [selectedIndex, setSelectedIndex] = useState(-1); const [recentSearches, setRecentSearches] = useState(getRecentSearches()); - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); - const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; - const shortcut_txt = isMac ? '⌘K' : 'Ctrl+K'; + const isxs = useIsMobile(); + const isMac = typeof navigator !== "undefined" && navigator.platform.toUpperCase().indexOf("MAC") >= 0; + const shortcut_txt = isMac ? "⌘K" : "Ctrl+K"; // Load package index on mount useEffect(() => { @@ -95,7 +94,7 @@ export default function Search() { // Cmd+K handler useEffect(() => { const handler = (event: KeyboardEvent) => { - if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { + if (event.key === "k" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); if (document.activeElement !== inputRef.current) { inputRef.current?.focus(); @@ -104,46 +103,63 @@ export default function Search() { } } }; - document.addEventListener('keydown', handler); - return () => document.removeEventListener('keydown', handler); + document.addEventListener("keydown", handler); + return () => document.removeEventListener("keydown", handler); + }, []); + + // Click outside to close + useEffect(() => { + const handler = (e: MouseEvent) => { + if ( + popperRef.current && + !popperRef.current.contains(e.target as Node) && + e.target !== inputRef.current + ) { + setopen(false); + } + }; + document.addEventListener("mousedown", handler); + return () => document.removeEventListener("mousedown", handler); }, []); // Keyboard navigation useEffect(() => { const handler = (event: KeyboardEvent) => { if (!isopen) return; - const items = query ? results : []; - if (event.key === 'ArrowDown') { + if (event.key === "ArrowDown") { event.preventDefault(); - setSelectedIndex(i => Math.min(i + 1, items.length - 1)); - } else if (event.key === 'ArrowUp') { + setSelectedIndex((i) => Math.min(i + 1, items.length - 1)); + } else if (event.key === "ArrowUp") { event.preventDefault(); - setSelectedIndex(i => Math.max(i - 1, -1)); - } else if (event.key === 'Enter' && selectedIndex >= 0 && selectedIndex < items.length) { + setSelectedIndex((i) => Math.max(i - 1, -1)); + } else if (event.key === "Enter" && selectedIndex >= 0 && selectedIndex < items.length) { event.preventDefault(); const pkg = items[selectedIndex]; saveRecentSearch(query); window.location.href = `/pkgs/${pkg.project}/`; - } else if (event.key === 'Escape') { + } else if (event.key === "Escape") { inputRef.current?.blur(); setopen(false); } }; - document.addEventListener('keydown', handler); - return () => document.removeEventListener('keydown', handler); + document.addEventListener("keydown", handler); + return () => document.removeEventListener("keydown", handler); }, [isopen, query, results, selectedIndex]); - const handleSearch = useCallback((value: string) => { - setQuery(value); - setSelectedIndex(-1); - if (value.trim()) { - setResults(searchPackages(value, packages)); - } else { - setResults([]); - } - }, [packages]); + const handleSearch = useCallback( + (value: string) => { + setQuery(value); + setSelectedIndex(-1); + if (value.trim()) { + setResults(searchPackages(value, packages)); + } else { + setResults([]); + } + }, + [packages] + ); const handleResultClick = (project: string) => { if (query) saveRecentSearch(query); @@ -156,117 +172,136 @@ export default function Search() { inputRef.current?.focus(); }; - return <> - setopen(true)} - onChange={e => handleSearch(e.target.value)} - inputRef={inputRef} - InputProps={isxs ? undefined : { - endAdornment: {shortcut_txt}, - }} - /> - - - + return ( +
    +
    + setopen(true)} + onChange={(e) => handleSearch(e.target.value)} + className={cn( + "bg-transparent border border-[rgba(149,178,184,0.3)] rounded px-3 py-1.5 text-sm", + "text-[#EDF2EF] placeholder:text-[rgba(237,242,239,0.5)]", + "focus:outline-none focus:border-[#4156E1] focus:ring-1 focus:ring-[#4156E1]", + "transition-colors w-48 md:w-56" + )} + /> + {!isxs && ( + + {shortcut_txt} + + )} +
    + + {isopen && ( +
    {query.trim() ? ( - - ) : isopen && recentSearches.length > 0 ? ( - + + ) : recentSearches.length > 0 ? ( + ) : null} - - - - ; +
    + )} +
    + ); } -function SearchResults({ results, selectedIndex, onClick }: { +function SearchResults({ + results, + selectedIndex, + onClick, +}: { results: PkgEntry[]; selectedIndex: number; onClick: (project: string) => void; }) { if (results.length === 0) { return ( - - No packages found - +
    +

    No packages found

    +
    ); } return ( - + ); } -function RecentSearches({ searches, onSelect }: { searches: string[]; onSelect: (term: string) => void }) { +function RecentSearches({ + searches, + onSelect, +}: { + searches: string[]; + onSelect: (term: string) => void; +}) { return ( - - Recent searches - - {searches.map(term => ( - - onSelect(term)}> - - - +
    +

    Recent searches

    +
      + {searches.map((term) => ( +
    • + +
    • ))} - - +
    +
    ); } diff --git a/src/components/Stars.tsx b/src/components/Stars.tsx index 753c1ca8..00f8ac20 100644 --- a/src/components/Stars.tsx +++ b/src/components/Stars.tsx @@ -1,13 +1,8 @@ -import { Stack, IconButton, Box, Tooltip, Typography, useTheme, useMediaQuery } from "@mui/material"; import { useAsync } from "react-use"; import { useState, useEffect, useRef, useCallback } from "react"; +import { useIsMobile } from "../utils/useIsMobile"; import github from "../assets/wordmarks/github.svg"; -/** - * Animated star counter that counts from 0 to the actual value. - * Uses requestAnimationFrame for smooth 60fps animation. - * Respects prefers-reduced-motion: skips animation and shows final value instantly. - */ function useAnimatedCounter(target: number | undefined, durationMs = 1600): string { const [display, setDisplay] = useState(""); const prefersReducedMotion = useRef(false); @@ -28,7 +23,6 @@ function useAnimatedCounter(target: number | undefined, durationMs = 1600): stri return; } - // Respect reduced motion preference if (prefersReducedMotion.current) { setDisplay(formatNumber(target)); return; @@ -36,17 +30,13 @@ function useAnimatedCounter(target: number | undefined, durationMs = 1600): stri let rafId: number; let startTime: number | null = null; - const startValue = 0; const animate = (timestamp: number) => { if (startTime === null) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / durationMs, 1); - - // Ease-out cubic for satisfying deceleration const eased = 1 - Math.pow(1 - progress, 3); - const current = Math.round(startValue + (target - startValue) * eased); - + const current = Math.round(target * eased); setDisplay(formatNumber(current)); if (progress < 1) { @@ -55,23 +45,24 @@ function useAnimatedCounter(target: number | undefined, durationMs = 1600): stri }; rafId = requestAnimationFrame(animate); - - return () => { - if (rafId) cancelAnimationFrame(rafId); - }; + return () => { if (rafId) cancelAnimationFrame(rafId); }; }, [target, durationMs, formatNumber]); return display; } -export default function Stars({ href, hideCountIfMobile }: { href?: string; hideCountIfMobile?: boolean }) { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down("md")); +export default function Stars({ + href, + hideCountIfMobile, +}: { + href?: string; + hideCountIfMobile?: boolean; +}) { + const isxs = useIsMobile(); const { value: stars } = useAsync(async () => { const response = await fetch("/stars.json"); const data = await response.json(); - // Handle both number and string formats const raw = typeof data === "object" && data !== null ? (data.total ?? data.stars ?? data) : data; return typeof raw === "string" ? parseInt(raw.replace(/,/g, ""), 10) : Number(raw); }, []); @@ -80,32 +71,24 @@ export default function Stars({ href, hideCountIfMobile }: { href?: string; hide const shouldHide = hideCountIfMobile && isxs; return ( - - + - - + GitHub + {!shouldHide && ( - - - {animatedStars || "\u00A0"} - - + + {animatedStars || "\u00A0"} + )} - +
    ); } diff --git a/src/components/Terminal.tsx b/src/components/Terminal.tsx index b1e296ae..b44d8d0d 100644 --- a/src/components/Terminal.tsx +++ b/src/components/Terminal.tsx @@ -1,47 +1,62 @@ -import { useMediaQuery, Box, Card, Typography, useTheme } from "@mui/material"; +import { useIsMobile } from "../utils/useIsMobile"; +import { cn } from "../utils/cn"; -export default function Terminal({ children, width, mb, mt }: { children: React.ReactNode, width?: string, mb?: number, mt?: number }) { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); +export default function Terminal({ + children, + width, + mb, + mt, +}: { + children: React.ReactNode; + width?: string; + mb?: number; + mt?: number; +}) { + const isxs = useIsMobile(); + const hasStoplights = width === undefined; - const stoplights = width === undefined ? true : undefined - const sx = {p: isxs ? 2 : 4} as any - if (stoplights) sx.pt = 6 - - return - - {children} - - + return ( +
    +
    + {children} +
    +
    + ); } export function Dim({ children }: { children: React.ReactNode }) { - return {children} + return {children}; } export function Purple({ children }: { children: React.ReactNode }) { - return {children} + return ( + {children} + ); } -export function Orange({children}: {children: React.ReactNode}) { - return - {children} - +export function Orange({ children }: { children: React.ReactNode }) { + return ( + {children} + ); } export function Prompt() { - return $ + return $; } diff --git a/src/mash.pkgx.sh.tsx b/src/mash.pkgx.sh.tsx index e7ecfe8e..e6108e14 100644 --- a/src/mash.pkgx.sh.tsx +++ b/src/mash.pkgx.sh.tsx @@ -1,63 +1,69 @@ -import { ThemeProvider } from '@mui/material/styles'; -import Grid from '@mui/material/Grid2'; -import { Button, CssBaseline, Stack, Typography, useMediaQuery, useTheme } from '@mui/material'; -import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; -import * as ReactDOM from 'react-dom/client'; +import { Route, BrowserRouter as Router, Routes } from "react-router-dom"; +import React from "react"; +import * as ReactDOM from "react-dom/client"; +import { useIsMobile } from "./utils/useIsMobile"; import Masthead from "./components/Masthead"; -import Listing from './mash.pkgx.sh/Listing'; -import Script from './mash.pkgx.sh/Script'; +import Listing from "./mash.pkgx.sh/Listing"; +import Script from "./mash.pkgx.sh/Script"; import Footer from "./components/Footer"; -import Stars from './components/Stars'; -import Hero from './mash.pkgx.sh/Hero'; -import theme from './utils/theme'; -import './assets/main.css'; -import React from "react"; -import Discord from './components/Discord'; +import Stars from "./components/Stars"; +import Hero from "./mash.pkgx.sh/Hero"; +import Discord from "./components/Discord"; +import "./assets/app.css"; function Body() { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); + const isxs = useIsMobile(); return ( - +
    - - +
    +
    - } /> - } /> + } /> + } /> - - +
    +
    - - +
    +
    - - ) +
    + ); } function MyMasthead() { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); + const isxs = useIsMobile(); - return MASH}> - {!isxs && <> - - - } - - - + return ( + MASH}> + {!isxs && ( + <> + + docs + + + pkgs + + + )} + + + + ); } -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - - - , + +
    ); diff --git a/src/mash.pkgx.sh/Hero.tsx b/src/mash.pkgx.sh/Hero.tsx index dd28a441..360367e0 100644 --- a/src/mash.pkgx.sh/Hero.tsx +++ b/src/mash.pkgx.sh/Hero.tsx @@ -1,64 +1,56 @@ -import { Button, Card, CardContent, Link, Stack, Typography, useMediaQuery, useTheme } from '@mui/material'; -import ArrowOutwardIcon from '@mui/icons-material/CallMade'; -import { Orange } from '../components/Terminal'; +import { ArrowUpRight } from "lucide-react"; +import { Orange } from "../components/Terminal"; export default function Hero() { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); - - return - - - - mash—the package manager for scripts. - - + return ( +
    +
    +

    + mash—the package manager for scripts. +

    +

    Mash up millions of open source packages into monstrously powerful scripts. - - +

    +

    Bash is ancient. Write scripts in any language you want and trivially distribute them to the whole world. - - - We’re a community of thousands of passionate computer users who want to make the most of the fruits of open source software. - - - - - - - Get Started - - - - - - - - Submitting Scripts - - -

  • Fork
  • +

    +

    + We're a community of thousands of passionate computer users who want to make the most of the fruits of open source software. +

    +
    + +
    +

    Get Started

    + + Install Mash + +
    + +
    +

    Submitting Scripts

    +
      +
    1. Fork
    2. Push scripts
    3. Wait an hour
    4. - - +
    +

    No pull request required! We index the fork graph. - - - - - - - Improving This Website - - - Even this site is Open Source! - If you have ideas for improving it, why not give it a go? - github.com/pkgxdev/www - - - - +

    +
    + +
    +

    Improving This Website

    +

    + Even this site is Open Source! If you have ideas for improving it, why not give it a go?{" "} + + github.com/pkgxdev/www + +

    +
    +
    + ); } diff --git a/src/mash.pkgx.sh/Listing.tsx b/src/mash.pkgx.sh/Listing.tsx index 5dcc6da6..897e9258 100644 --- a/src/mash.pkgx.sh/Listing.tsx +++ b/src/mash.pkgx.sh/Listing.tsx @@ -1,29 +1,38 @@ -import { Alert, Skeleton, Stack } from '@mui/material'; -import { ScriptComponent, Script } from './Script' -import { useAsync } from 'react-use'; +import { ScriptComponent, Script } from "./Script"; +import { useAsync } from "react-use"; export default function Landing() { const data = useAsync(async () => { - const rsp = await fetch('https://pkgxdev.github.io/mash/index.json') - const data = await rsp.json() - return (data.scripts as Script[]).filter(({description}) => description) - }) + const rsp = await fetch("https://pkgxdev.github.io/mash/index.json"); + const data = await rsp.json(); + return (data.scripts as Script[]).filter(({ description }) => description); + }); - return - {body()} - + return ( +
    + {body()} +
    + ); function body() { if (data.loading) { - return <> - - - - + return ( + <> +
    +
    +
    + + ); } else if (data.error) { - return {data.error.message} + return ( +
    + {data.error.message} +
    + ); } else { - return data.value!.map(script => ) + return data.value!.map((script) => ( + + )); } } } diff --git a/src/mash.pkgx.sh/Script.tsx b/src/mash.pkgx.sh/Script.tsx index ea310e98..9b382835 100644 --- a/src/mash.pkgx.sh/Script.tsx +++ b/src/mash.pkgx.sh/Script.tsx @@ -1,86 +1,107 @@ -import { Alert, Avatar, Button, Card, CardActionArea, CardContent, Chip, Skeleton, Stack, Typography } from '@mui/material'; -import ArrowOutwardIcon from '@mui/icons-material/CallMade'; -import EastIcon from '@mui/icons-material/East'; -import { useLocation } from 'react-router-dom'; -import Markdown from '../components/Markdown'; -import Terminal from '../components/Terminal'; -import ScriptDetail from './ScriptDetail'; -import { useAsync } from 'react-use'; +import { ArrowUpRight, ArrowRight } from "lucide-react"; +import { useLocation } from "react-router-dom"; +import Markdown from "../components/Markdown"; +import Terminal from "../components/Terminal"; +import ScriptDetail from "./ScriptDetail"; +import { useAsync } from "react-use"; export interface Script { - fullname: string - birthtime: string - description?: string - avatar: string - url: string - cmd: string - README?: string - category?: string + fullname: string; + birthtime: string; + description?: string; + avatar: string; + url: string; + cmd: string; + README?: string; + category?: string; } export default function ScriptRoute() { - const path = useLocation().pathname + const path = useLocation().pathname; const data = useAsync(async () => { - const rsp = await fetch('https://pkgxdev.github.io/mash/index.json') - const data = await rsp.json() - return data.scripts.find((s: any) => `/${s.fullname}` === path) as Script - }) + const rsp = await fetch("https://pkgxdev.github.io/mash/index.json"); + const data = await rsp.json(); + return data.scripts.find((s: any) => `/${s.fullname}` === path) as Script; + }); if (data.loading) { - return + return
    ; } else if (data.error || !data.value) { - return {data.error?.message ?? 'Script not found'} + return ( +
    + {data.error?.message ?? "Script not found"} +
    + ); } else { - return + return ; } } -export function ScriptComponent({fullname, birthtime, description, avatar, url, cmd, category}: Script) { - const username = fullname.split('/')[0] +export function ScriptComponent({ + fullname, + birthtime, + description, + avatar, + url, + cmd, + category, +}: Script) { + const username = fullname.split("/")[0]; - return - - - - {fullname} - {timeAgo(birthtime)} - {category && } - + return ( +
    +
    + {username} + {fullname} + {timeAgo(birthtime)} + {category && ( + + {category} + + )} +
    {description && } {cmd} - - - - - - + +
    + ); } function timeAgo(date: Date | string) { const now = new Date().getTime(); const diffInSeconds = Math.round((now - new Date(date).getTime()) / 1000); - const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: 'auto' }); + const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: "auto" }); - // Define the time thresholds in seconds for different units const minute = 60; const hour = minute * 60; const day = hour * 24; const week = day * 7; - // Calculate the difference and determine the unit - if (diffInSeconds < minute) { - return rtf.format(-diffInSeconds, 'second'); - } else if (diffInSeconds < hour) { - return rtf.format(-Math.round(diffInSeconds / minute), 'minute'); - } else if (diffInSeconds < day) { - return rtf.format(-Math.round(diffInSeconds / hour), 'hour'); - } else if (diffInSeconds < week) { - return rtf.format(-Math.round(diffInSeconds / day), 'day'); - } else { - // For differences larger than a week, you could continue with months and years - // or decide to show the full date. - return rtf.format(-Math.round(diffInSeconds / week), 'week'); - } + if (diffInSeconds < minute) return rtf.format(-diffInSeconds, "second"); + else if (diffInSeconds < hour) return rtf.format(-Math.round(diffInSeconds / minute), "minute"); + else if (diffInSeconds < day) return rtf.format(-Math.round(diffInSeconds / hour), "hour"); + else if (diffInSeconds < week) return rtf.format(-Math.round(diffInSeconds / day), "day"); + else return rtf.format(-Math.round(diffInSeconds / week), "week"); } diff --git a/src/mash.pkgx.sh/ScriptDetail.tsx b/src/mash.pkgx.sh/ScriptDetail.tsx index dabb9c1e..2bc2731b 100644 --- a/src/mash.pkgx.sh/ScriptDetail.tsx +++ b/src/mash.pkgx.sh/ScriptDetail.tsx @@ -1,52 +1,74 @@ -import { Alert, Avatar, Button, Card, CardContent, Skeleton, Stack, Typography } from '@mui/material'; -import ArrowOutwardIcon from '@mui/icons-material/CallMade'; -import Terminal from '../components/Terminal'; -import Markdown from '../components/Markdown'; -import { useAsync } from 'react-use'; +import { ArrowUpRight } from "lucide-react"; +import Terminal from "../components/Terminal"; +import Markdown from "../components/Markdown"; +import { useAsync } from "react-use"; export interface Script { - fullname: string - birthtime: string - description?: string - avatar: string - url: string - README?: string - cmd: string + fullname: string; + birthtime: string; + description?: string; + avatar: string; + url: string; + README?: string; + cmd: string; } -export default function ScriptComponent({fullname, birthtime, cmd, README: description, avatar, url}: Script) { - const {loading, error, value: content} = useAsync(async () => { - const rsp = await fetch(`https://pkgxdev.github.io/mash/u/${fullname}`) - return await rsp.text() - }) +export default function ScriptComponent({ + fullname, + birthtime, + cmd, + README: description, + avatar, + url, +}: Script) { + const { + loading, + error, + value: content, + } = useAsync(async () => { + const rsp = await fetch(`https://pkgxdev.github.io/mash/u/${fullname}`); + return await rsp.text(); + }); - const username = fullname.split('/')[0] + const username = fullname.split("/")[0]; - return - - - - {fullname} - {timeAgo(birthtime)} - - {description - ? - : {cmd} - } + return ( +
    +
    + {username} + {fullname} + {timeAgo(birthtime)} +
    + {description ? : {cmd}} {excerpt()} - - - - - + +
    + ); function excerpt() { if (loading) { - return + return
    ; } else if (error) { - return {error.message} + return ( +
    + {error.message} +
    + ); } else { - return {content} + return {content}; } } } @@ -54,26 +76,19 @@ export default function ScriptComponent({fullname, birthtime, cmd, README: descr function timeAgo(date: Date | string) { const now = new Date().getTime(); const diffInSeconds = Math.round((now - new Date(date).getTime()) / 1000); - const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: 'auto' }); + const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: "auto" }); - // Define the time thresholds in seconds for different units const minute = 60; const hour = minute * 60; const day = hour * 24; const week = day * 7; - // Calculate the difference and determine the unit - if (diffInSeconds < minute) { - return rtf.format(-diffInSeconds, 'second'); - } else if (diffInSeconds < hour) { - return rtf.format(-Math.round(diffInSeconds / minute), 'minute'); - } else if (diffInSeconds < day) { - return rtf.format(-Math.round(diffInSeconds / hour), 'hour'); - } else if (diffInSeconds < week) { - return rtf.format(-Math.round(diffInSeconds / day), 'day'); - } else { - // For differences larger than a week, you could continue with months and years - // or decide to show the full date. - return rtf.format(-Math.round(diffInSeconds / week), 'week'); - } + if (diffInSeconds < minute) return rtf.format(-diffInSeconds, "second"); + else if (diffInSeconds < hour) + return rtf.format(-Math.round(diffInSeconds / minute), "minute"); + else if (diffInSeconds < day) + return rtf.format(-Math.round(diffInSeconds / hour), "hour"); + else if (diffInSeconds < week) + return rtf.format(-Math.round(diffInSeconds / day), "day"); + else return rtf.format(-Math.round(diffInSeconds / week), "week"); } diff --git a/src/pkgx.app.tsx b/src/pkgx.app.tsx index 95b11350..35c2cdf9 100644 --- a/src/pkgx.app.tsx +++ b/src/pkgx.app.tsx @@ -1,104 +1,95 @@ -import React from "react"; -import { ThemeProvider } from '@mui/material/styles'; -import Grid from '@mui/material/Grid2'; -import { Box, Button, Card, CardContent, CssBaseline, Dialog, DialogTitle, Stack, Typography, useMediaQuery, useTheme } from '@mui/material'; -import HeroTypography from './components/HeroTypography'; -import * as ReactDOM from 'react-dom/client'; -import gui from "./assets/gui.png"; -import theme from './utils/theme'; -import Masthead from './components/Masthead'; +import React, { useState } from "react"; +import * as ReactDOM from "react-dom/client"; +import { useIsMobile } from "./utils/useIsMobile"; +import HeroTypography from "./components/HeroTypography"; +import Masthead from "./components/Masthead"; import Footer from "./components/Footer"; -import './assets/main.css'; +import gui from "./assets/gui.png"; +import { cn } from "./utils/cn"; +import "./assets/app.css"; -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - - - , + + ); - function Body() { - const theme = useTheme(); - const isxs = useMediaQuery(theme.breakpoints.down('md')); - - const imgsz = {width: '100%', height: '100%'} + const isxs = useIsMobile(); - return - + return ( +
    + - - - Open Source is a Treasure Trove - - - What jewel will you discover today? - - +
    + Open Source is a Treasure Trove +

    What jewel will you discover today?

    +
    - - - +
    + pkgx app +
    - + - - - - - - One Click Installs - - - Say goodbye to the days of scavenging through cluttered docs. oss.app enables you to query our expansive pkgdb and install your desired version of any package with one click. OpenAI, deno, youtube-dl, and hundreds more… all available to you within seconds. - - - - - - - - - Complementing pkgx - - - We believe command line interfaces and graphical user interfaces are complements and should not necessarily share the same features. - - - Our cli is precise and powerful where our gui is optimized for discovery and batch operations. - - - - - +
    +
    +

    One Click Installs

    +

    + Say goodbye to the days of scavenging through cluttered docs. oss.app enables you to query our expansive pkgdb and install your desired version of any package with one click. +

    +
    +
    +

    + Complementing pkgx +

    +

    + We believe command line interfaces and graphical user interfaces are complements and should not necessarily share the same features. +

    +

    + Our cli is precise and powerful where our gui is optimized for discovery and batch operations. +

    +
    +
    -
    - +
    +
    + ); } function Download() { - const [open, setOpen] = React.useState(false); - const handleOpen = () => setOpen(true); - const handleClose = () => setOpen(false); + const [open, setOpen] = useState(false); return ( - - - - Which Platform? - - - - - - +
    + + + {open && ( +
    setOpen(false)}> +
    e.stopPropagation()}> +

    Which Platform?

    + +
    +
    + )} +
    ); } diff --git a/src/pkgx.dev.tsx b/src/pkgx.dev.tsx index 9bcec329..fee4b55f 100644 --- a/src/pkgx.dev.tsx +++ b/src/pkgx.dev.tsx @@ -1,64 +1,67 @@ -import { CssBaseline, Button, Stack, useTheme, ThemeProvider, useMediaQuery } from '@mui/material'; -import { Route, BrowserRouter as Router, Routes, useLocation } from 'react-router-dom'; -import PackageShowcase from './pkgx.dev/PackageShowcase'; -import PackageListing from './pkgx.dev/PackageListing'; -import PrivacyPolicy from './pkgx.dev/PrivacyPolicy'; -import TermsOfUse from './pkgx.dev/TermsOfUse'; -import * as ReactDOM from 'react-dom/client'; -import Masthead from './components/Masthead'; -import HomeFeed from './pkgx.dev/HomeFeed'; +import { Route, BrowserRouter as Router, Routes, useLocation } from "react-router-dom"; +import PackageShowcase from "./pkgx.dev/PackageShowcase"; +import PackageListing from "./pkgx.dev/PackageListing"; +import PrivacyPolicy from "./pkgx.dev/PrivacyPolicy"; +import TermsOfUse from "./pkgx.dev/TermsOfUse"; +import TeaProtocol from "./pkgx.dev/TeaProtocol"; +import CoinListLandingPage from "./pkgx.dev/CoinListLandingPage"; +import * as ReactDOM from "react-dom/client"; +import Masthead from "./components/Masthead"; +import HomeFeed from "./pkgx.dev/HomeFeed"; import Footer from "./components/Footer"; -import Search from './components/Search'; -import Stars from './components/Stars'; -import theme from './utils/theme'; -import React, { } from "react"; -import './assets/main.css'; -import Discord from './components/Discord'; -import TeaProtocol from './pkgx.dev/TeaProtocol'; -import CoinListLandingPage from './pkgx.dev/CoinListLandingPage'; +import Search from "./components/Search"; +import Stars from "./components/Stars"; +import Discord from "./components/Discord"; +import { useIsMobile } from "./utils/useIsMobile"; +import React from "react"; +import "./assets/app.css"; - -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -