├── .nvmrc ├── public ├── pfp │ ├── 1.png │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.png │ ├── chart.jpg │ ├── levels.jpg │ ├── wayne.jpg │ └── levelspost.jpg ├── unfurl.png ├── wallet.png ├── bsv.svg ├── icon.svg ├── github.svg └── logo.svg ├── src ├── app │ ├── favicon.ico │ ├── social │ │ ├── layout.tsx │ │ └── page.tsx │ ├── layout.tsx │ ├── globals.css │ ├── chart.tsx │ ├── page.tsx │ └── header.tsx ├── components │ ├── ui │ │ ├── toaster.tsx │ │ ├── connect-button.tsx │ │ ├── avatar.tsx │ │ ├── link-preview.tsx │ │ ├── dropdown-menu.tsx │ │ ├── post-composer.tsx │ │ ├── sidebar.tsx │ │ └── social-card.tsx │ ├── providers.tsx │ ├── loading.tsx │ ├── icons │ │ └── gitbook-icon.tsx │ ├── theme-provider.tsx │ └── layout.tsx ├── utils │ ├── constants.tsx │ ├── hooks │ │ ├── useExchangeRate.tsx │ │ ├── useWindowSize.tsx │ │ ├── useChainInfo.tsx │ │ ├── useEventListener.tsx │ │ └── useLockHistory.tsx │ └── format-number.tsx ├── types │ ├── number-abbreviate.d.ts │ ├── lock-history.ts │ └── yours.d.ts ├── hooks │ └── use-toast.ts └── lib │ ├── link-preview.ts │ └── utils.ts ├── postcss.config.js ├── next.config.mjs ├── .prettierrc ├── README.md ├── tailwind.config.ts ├── .gitignore ├── tsconfig.json ├── package.json └── pnpm-lock.yaml /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | 3 | -------------------------------------------------------------------------------- /public/pfp/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/1.png -------------------------------------------------------------------------------- /public/pfp/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/2.jpg -------------------------------------------------------------------------------- /public/pfp/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/3.jpg -------------------------------------------------------------------------------- /public/pfp/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/4.jpg -------------------------------------------------------------------------------- /public/pfp/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/5.png -------------------------------------------------------------------------------- /public/unfurl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/unfurl.png -------------------------------------------------------------------------------- /public/wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/wallet.png -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /public/pfp/chart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/chart.jpg -------------------------------------------------------------------------------- /public/pfp/levels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/levels.jpg -------------------------------------------------------------------------------- /public/pfp/wayne.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/wayne.jpg -------------------------------------------------------------------------------- /public/pfp/levelspost.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yours-org/frontend/HEAD/public/pfp/levelspost.jpg -------------------------------------------------------------------------------- /src/components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | export function Toaster() { 4 | return null 5 | } 6 | 7 | 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/constants.tsx: -------------------------------------------------------------------------------- 1 | //export const API_URL = 'http://localhost:9007' 2 | export const API_URL = 'https://lock.yours.org'; 3 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactCompiler: true 4 | } 5 | 6 | export default nextConfig 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "parser": "typescript", 4 | "useTabs": true, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "semi": false 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/types/number-abbreviate.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'number-abbreviate' { 2 | export default class NumberAbbreviate { 3 | constructor(suffixes?: string[]) 4 | abbreviate(value: number, decimals?: number): string 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/components/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ReactNode } from 'react' 4 | import { WalletProvider } from '@/components/layout' 5 | 6 | export function Providers({ children }: { children: ReactNode }) { 7 | return {children} 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/types/lock-history.ts: -------------------------------------------------------------------------------- 1 | export type LockHistoryEntry = { 2 | time: number 3 | height: number | string 4 | sum: string 5 | sats: string 6 | } 7 | 8 | export type UnlockHistoryEntry = { 9 | height: number | string 10 | sum: string 11 | sats: string 12 | time?: number 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/hooks/useExchangeRate.tsx: -------------------------------------------------------------------------------- 1 | import useSwr from 'swr' 2 | // @ts-ignore 3 | const fetcher = (...args) => fetch(...args).then((res) => res.json()) 4 | 5 | export default function useExchangeRate() { 6 | const { data, isLoading } = useSwr( 7 | 'https://cloud-functions.twetch.app/api/exchange-rate', 8 | fetcher 9 | ) 10 | return { exchangeRate: data?.price, isLoading } 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | First, run the development server: 4 | 5 | ```bash 6 | npm run dev 7 | # or 8 | yarn dev 9 | # or 10 | pnpm dev 11 | # or 12 | bun dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config: Config = { 4 | content: ['./src/**/*.{js,ts,jsx,tsx,mdx,css}'], 5 | theme: { 6 | extend: { 7 | backgroundImage: { 8 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 9 | 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' 10 | } 11 | } 12 | }, 13 | plugins: [] 14 | } 15 | export default config 16 | -------------------------------------------------------------------------------- /src/app/social/layout.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { ReactNode } from 'react' 4 | import { ThemeProvider } from '@/components/theme-provider' 5 | import { AppLayout } from '@/components/layout' 6 | import { Toaster } from '@/components/ui/toaster' 7 | 8 | export default function SocialLayout({ children }: { children: ReactNode }) { 9 | return ( 10 | 11 | {children} 12 | 13 | 14 | ) 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/hooks/use-toast.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useCallback } from 'react' 4 | 5 | interface ToastOptions { 6 | title?: string 7 | description?: string 8 | variant?: 'default' | 'success' | 'destructive' 9 | } 10 | 11 | export const useToast = () => { 12 | const toast = useCallback(({ title, description, variant }: ToastOptions) => { 13 | console.info(`[toast:${variant ?? 'default'}] ${title ?? ''}`, description ?? '') 14 | }, []) 15 | 16 | return { toast } 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /src/components/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return ( 3 | 8 | 16 | 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/format-number.tsx: -------------------------------------------------------------------------------- 1 | import NumAbbr from 'number-abbreviate' 2 | 3 | const numAbbr = new NumAbbr(['k', 'm', 'b', 't', 'q']) 4 | 5 | export default function numberWithCommas( 6 | x: number | string | null | undefined 7 | ): string | undefined { 8 | if (!x) { 9 | return 10 | } 11 | 12 | const numericValue = typeof x === 'number' ? x : parseFloat(x) 13 | 14 | if (!Number.isNaN(numericValue) && numericValue > 100000000) { 15 | return numAbbr.abbreviate(numericValue, 2).toUpperCase() 16 | } 17 | 18 | const [value, decimals] = x.toString().split('.') 19 | 20 | return `${value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}${decimals ? `.${decimals}` : ''}` 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/hooks/useWindowSize.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import useEventListener from './useEventListener' 3 | 4 | function useWindowSize(initialWidth?: number, initialHeight?: number) { 5 | const [windowSize, setWindowSize] = useState({ 6 | width: typeof window !== 'undefined' ? window.innerWidth : initialWidth, 7 | height: typeof window !== 'undefined' ? window.innerHeight : initialHeight 8 | }) 9 | 10 | useEventListener('resize', () => { 11 | const width = window.innerWidth 12 | const height = window.innerHeight 13 | requestAnimationFrame(() => { 14 | setTimeout(() => { 15 | setWindowSize({ 16 | width, 17 | height 18 | }) 19 | }, 0) 20 | }) 21 | }) 22 | 23 | return windowSize 24 | } 25 | 26 | export default useWindowSize 27 | -------------------------------------------------------------------------------- /src/types/yours.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface YoursProviderAddresses { 3 | bsvAddress?: string 4 | identityAddress?: string 5 | ordAddress?: string 6 | } 7 | 8 | interface YoursSocialProfile { 9 | displayName?: string 10 | avatar?: string 11 | username?: string 12 | handle?: string 13 | profileUrl?: string 14 | socialUrl?: string 15 | } 16 | 17 | interface YoursProvider { 18 | connect: () => Promise 19 | disconnect?: () => Promise 20 | getSocialProfile?: () => Promise 21 | getAddresses?: () => Promise 22 | } 23 | 24 | interface Window { 25 | yours?: YoursProvider 26 | } 27 | 28 | interface WindowEventMap { 29 | 'yours#initialized': Event 30 | } 31 | } 32 | 33 | export {} 34 | -------------------------------------------------------------------------------- /src/lib/link-preview.ts: -------------------------------------------------------------------------------- 1 | export interface LinkMetadata { 2 | url: string 3 | title?: string 4 | description?: string 5 | image?: string 6 | favicon?: string 7 | isX?: boolean 8 | } 9 | 10 | const urlRegex = 11 | /(https?:\/\/(?:www\.)?[^\s/$.?#].[^\s]*)/gi 12 | 13 | export function extractUrls(text: string) { 14 | if (!text) return [] 15 | return text.match(urlRegex) ?? [] 16 | } 17 | 18 | export async function fetchLinkMetadata(url: string): Promise { 19 | if (!url) return null 20 | 21 | // Placeholder metadata until a real endpoint is connected. 22 | return { 23 | url, 24 | title: 'Link preview title', 25 | description: 'Link preview description placeholder content.', 26 | image: '/unfurl.png', 27 | favicon: `https://www.google.com/s2/favicons?domain=${encodeURIComponent(url)}` 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "dom", 5 | "dom.iterable", 6 | "esnext" 7 | ], 8 | "allowJs": true, 9 | "target": "ES6", 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "module": "esnext", 15 | "moduleResolution": "bundler", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "react-jsx", 19 | "incremental": true, 20 | "plugins": [ 21 | { 22 | "name": "next" 23 | } 24 | ], 25 | "paths": { 26 | "@/*": [ 27 | "./src/*" 28 | ] 29 | } 30 | }, 31 | "include": [ 32 | "next-env.d.ts", 33 | "**/*.ts", 34 | "**/*.tsx", 35 | ".next/types/**/*.ts", 36 | ".next/dev/types/**/*.ts" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /src/utils/hooks/useChainInfo.tsx: -------------------------------------------------------------------------------- 1 | import useLockHistory from '@/utils/hooks/useLockHistory' 2 | import useSwr from 'swr' 3 | // @ts-ignore 4 | const fetcher = (...args) => fetch(...args).then((res) => res.json()) 5 | 6 | export default function useChainInfo() { 7 | const { data: lockHistoryData } = useLockHistory() 8 | const { data, isLoading } = useSwr( 9 | 'https://api.whatsonchain.com/v1/bsv/main/chain/info', 10 | fetcher, 11 | { 12 | refreshInterval: 30000 13 | } 14 | ) 15 | 16 | const tip = data?.blocks || 0 17 | const lastHistoryHeight = lockHistoryData?.slice(-1)?.[0]?.height 18 | const historyTip = 19 | lastHistoryHeight == null 20 | ? 0 21 | : typeof lastHistoryHeight === 'number' 22 | ? lastHistoryHeight 23 | : parseInt(lastHistoryHeight, 10) 24 | 25 | console.log({ tip, historyTip, blockDiff: tip - historyTip }) 26 | 27 | return { data, isLoading } 28 | } 29 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import Header from './header' 3 | import { Inter } from 'next/font/google' 4 | import './globals.css' 5 | import { Providers } from '@/components/providers' 6 | 7 | const inter = Inter({ subsets: ['latin'] }) 8 | 9 | export const metadata: Metadata = { 10 | title: 'Yours', 11 | description: 'Yours', 12 | openGraph: { 13 | title: 'Yours', 14 | description: 'Yours', 15 | url: 'https://yours.org', 16 | images: [ 17 | { 18 | url: 'https://yours.org/unfurl.png' 19 | } 20 | ] 21 | } 22 | } 23 | 24 | export default function RootLayout({ 25 | children 26 | }: Readonly<{ 27 | children: React.ReactNode 28 | }>) { 29 | return ( 30 | 31 | 32 | 33 |
34 | {children} 35 | 36 | 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-icons": "^1.3.0", 13 | "axios": "^1.13.2", 14 | "classnames": "^2.5.1", 15 | "framer-motion": "^12.23.24", 16 | "lightweight-charts": "^4.1.3", 17 | "lucide-react": "^0.468.0", 18 | "next": "16.0.9", 19 | "number-abbreviate": "^2.0.0", 20 | "prettier": "^2.8.8", 21 | "react": "^18", 22 | "react-dom": "^18", 23 | "swr": "^2.3.6" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^20", 27 | "@types/react": "^18", 28 | "@types/react-dom": "^18", 29 | "autoprefixer": "^10.0.1", 30 | "babel-plugin-react-compiler": "^1.0.0", 31 | "postcss": "^8", 32 | "tailwindcss": "^3.3.0", 33 | "typescript": "^5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --yours-background: #050507; 7 | --yours-surface: rgba(16, 20, 28, 0.88); 8 | --yours-border: rgba(255, 255, 255, 0.08); 9 | --yours-card: rgba(17, 23, 32, 0.72); 10 | --yours-glow: rgba(40, 121, 255, 0.16); 11 | } 12 | 13 | body { 14 | min-height: 100vh; 15 | background: radial-gradient(120% 120% at 50% 0%, rgba(76, 201, 240, 0.14) 0%, transparent 55%), 16 | radial-gradient(110% 110% at 90% 12%, rgba(108, 233, 166, 0.24) 0%, transparent 60%), 17 | linear-gradient(180deg, #050507 0%, #03020d 100%); 18 | color: #f5f7ff; 19 | font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; 20 | } 21 | 22 | @layer utilities { 23 | .text-balance { 24 | text-wrap: balance; 25 | } 26 | } 27 | 28 | @keyframes mobile-menu-enter { 29 | from { 30 | transform: translateX(100%); 31 | opacity: 0; 32 | } 33 | to { 34 | transform: translateX(0); 35 | opacity: 1; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/ui/connect-button.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ButtonHTMLAttributes } from 'react' 4 | import { cn } from '@/lib/utils' 5 | import { useWallet } from '@/components/layout' 6 | 7 | interface ConnectWalletButtonProps extends ButtonHTMLAttributes { 8 | label?: string 9 | } 10 | 11 | export function ConnectWalletButton({ label = 'Connect Wallet', className, onClick, ...props }: ConnectWalletButtonProps) { 12 | const { connect } = useWallet() 13 | 14 | return ( 15 | 31 | ) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/utils/hooks/useEventListener.tsx: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect, useRef } from 'react' 2 | 3 | function useEventListener( 4 | eventName: keyof WindowEventMap, 5 | handler: (event: Event) => void, 6 | element?: RefObject 7 | ) { 8 | const savedHandler = useRef<(event: Event) => void>() 9 | 10 | useEffect(() => { 11 | const targetElement: T | Window = element?.current || window 12 | if (!(targetElement && targetElement.addEventListener)) { 13 | return 14 | } 15 | 16 | if (savedHandler.current !== handler) { 17 | savedHandler.current = handler 18 | } 19 | 20 | const eventListener = (event: Event) => { 21 | // eslint-disable-next-line no-extra-boolean-cast 22 | if (!!savedHandler?.current) { 23 | savedHandler.current(event) 24 | } 25 | } 26 | 27 | targetElement.addEventListener(eventName, eventListener) 28 | 29 | return () => { 30 | targetElement.removeEventListener(eventName, eventListener) 31 | } 32 | }, [eventName, element, handler]) 33 | } 34 | 35 | export default useEventListener 36 | -------------------------------------------------------------------------------- /src/components/icons/gitbook-icon.tsx: -------------------------------------------------------------------------------- 1 | type GitbookIconProps = { 2 | className?: string 3 | } 4 | 5 | export function GitbookIcon({ className }: GitbookIconProps) { 6 | return ( 7 | 16 | ) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react' 2 | 3 | type ClassValue = string | number | null | undefined | false 4 | 5 | export function cn(...classes: ClassValue[]) { 6 | return classes.filter(Boolean).join(' ') 7 | } 8 | 9 | export function formatTimeAgo(dateInput: string | number | Date) { 10 | const date = dateInput instanceof Date ? dateInput : new Date(dateInput) 11 | const diffInSeconds = Math.floor((Date.now() - date.getTime()) / 1000) 12 | 13 | if (Number.isNaN(diffInSeconds)) { 14 | return '' 15 | } 16 | 17 | const intervals: [number, string][] = [ 18 | [60, 's'], 19 | [60, 'm'], 20 | [24, 'h'], 21 | [7, 'd'], 22 | [4.34524, 'w'], 23 | [12, 'mo'], 24 | [Number.POSITIVE_INFINITY, 'y'] 25 | ] 26 | 27 | let remaining = diffInSeconds 28 | 29 | for (const [amount, label] of intervals) { 30 | if (remaining < amount) { 31 | const value = Math.max(1, Math.floor(remaining)) 32 | return `${value}${label}` 33 | } 34 | remaining /= amount 35 | } 36 | 37 | return '' 38 | } 39 | 40 | export function formatUrlForDisplay(url?: string | null) { 41 | if (!url) { 42 | return null 43 | } 44 | 45 | try { 46 | const parsed = new URL(url) 47 | const trimmedPath = parsed.pathname.replace(/\/$/, '') 48 | const path = 49 | trimmedPath && trimmedPath !== '/' ? trimmedPath : '' 50 | return `${parsed.hostname}${path}` 51 | } catch { 52 | return url 53 | } 54 | } 55 | 56 | export type ChildrenProp = { children?: ReactNode } 57 | 58 | -------------------------------------------------------------------------------- /src/utils/hooks/useLockHistory.tsx: -------------------------------------------------------------------------------- 1 | import { API_URL } from '@/utils/constants' 2 | import useSwr from 'swr' 3 | import type { LockHistoryEntry, UnlockHistoryEntry } from '@/types/lock-history' 4 | // @ts-ignore 5 | const fetcher = (...args) => fetch(...args).then((res) => res.json()) 6 | 7 | type MempoolResponse = { 8 | sats?: number 9 | } 10 | 11 | type UseLockHistoryResult = { 12 | data: LockHistoryEntry[] | null 13 | unlockData: UnlockHistoryEntry[] | null 14 | isLoading: boolean 15 | isUnlockLoading: boolean 16 | mempoolData: MempoolResponse | null 17 | } 18 | 19 | export default function useLockHistory(): UseLockHistoryResult { 20 | const { data, isLoading } = useSwr(`${API_URL}/lock-history`, fetcher, { 21 | refreshInterval: 5000 22 | }) 23 | const { data: unlockData, isLoading: isUnlockLoading } = useSwr( 24 | `${API_URL}/unlock-history`, 25 | fetcher, 26 | { refreshInterval: 5000 } 27 | ) 28 | const { data: mempoolData } = useSwr( 29 | `${API_URL}/mempool`, 30 | fetcher, 31 | { 32 | refreshInterval: 2000 33 | } 34 | ) 35 | 36 | const mempoolSats = mempoolData?.sats || 0 37 | 38 | return { 39 | data: data 40 | ? data.map((e, i) => { 41 | if (i === data.length - 1) { 42 | return { 43 | ...e, 44 | sum: `${parseInt(e.sum, 10) + mempoolSats}`, 45 | sats: `${parseInt(e.sats, 10) + mempoolSats}` 46 | } 47 | } 48 | 49 | return e 50 | }) 51 | : null, 52 | unlockData: unlockData ?? null, 53 | isLoading, 54 | isUnlockLoading, 55 | mempoolData: mempoolData ?? null 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/bsv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext, useContext, useEffect, useMemo, useState } from 'react' 4 | 5 | type Theme = 'light' | 'dark' 6 | 7 | interface ThemeContextValue { 8 | theme: Theme 9 | setTheme: (theme: Theme) => void 10 | } 11 | 12 | const ThemeContext = createContext(undefined) 13 | 14 | interface ThemeProviderProps { 15 | children: React.ReactNode 16 | defaultTheme?: Theme 17 | } 18 | 19 | export function ThemeProvider({ children, defaultTheme = 'light' }: ThemeProviderProps) { 20 | const [theme, setThemeState] = useState(defaultTheme) 21 | 22 | useEffect(() => { 23 | const stored = window.localStorage.getItem('yours-theme') as Theme | null 24 | if (stored) { 25 | setThemeState(stored) 26 | return 27 | } 28 | 29 | const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches 30 | setThemeState(prefersDark ? 'dark' : defaultTheme) 31 | }, [defaultTheme]) 32 | 33 | useEffect(() => { 34 | const root = document.documentElement 35 | root.classList.remove('light', 'dark') 36 | root.classList.add(theme) 37 | window.localStorage.setItem('yours-theme', theme) 38 | }, [theme]) 39 | 40 | const setTheme = (value: Theme) => { 41 | setThemeState(value) 42 | } 43 | 44 | const value = useMemo(() => ({ theme, setTheme }), [theme]) 45 | 46 | return {children} 47 | } 48 | 49 | export const useTheme = () => { 50 | const context = useContext(ThemeContext) 51 | if (!context) { 52 | throw new Error('useTheme must be used within ThemeProvider') 53 | } 54 | return context 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Image from 'next/image' 4 | import { useEffect, useMemo, useState } from 'react' 5 | import { cn } from '@/lib/utils' 6 | 7 | interface AvatarProps { 8 | src?: string | null 9 | alt?: string 10 | size?: number 11 | className?: string 12 | } 13 | 14 | const palette = [ 15 | '#6366F1', 16 | '#0EA5E9', 17 | '#22C55E', 18 | '#F97316', 19 | '#F59E0B', 20 | '#EC4899', 21 | '#14B8A6', 22 | '#8B5CF6' 23 | ] 24 | 25 | export function Avatar({ src, alt = 'User avatar', size = 40, className }: AvatarProps) { 26 | const dimension = `${size}px` 27 | const [hasError, setHasError] = useState(false) 28 | const shouldShowImage = typeof src === 'string' && src.length > 0 && !hasError 29 | 30 | useEffect(() => { 31 | setHasError(false) 32 | }, [src]) 33 | 34 | const initials = useMemo(() => { 35 | const label = (alt || '').trim() 36 | if (!label) return 'U' 37 | const words = label.split(' ') 38 | if (words.length === 1) { 39 | return words[0].slice(0, 2).toUpperCase() 40 | } 41 | return (words[0][0] + words[1][0]).toUpperCase() 42 | }, [alt]) 43 | 44 | const background = useMemo(() => { 45 | const seed = src ?? alt ?? initials 46 | const total = seed 47 | .split('') 48 | .reduce((acc, char) => acc + char.charCodeAt(0), 0) 49 | return palette[total % palette.length] 50 | }, [src, alt, initials]) 51 | 52 | return ( 53 |
65 | {shouldShowImage && src ? ( 66 | {alt} setHasError(true)} 73 | /> 74 | ) : ( 75 | initials 76 | )} 77 |
78 | ) 79 | } 80 | -------------------------------------------------------------------------------- /src/components/ui/link-preview.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { LinkMetadata } from '@/lib/link-preview' 4 | import { cn } from '@/lib/utils' 5 | 6 | interface LinkPreviewProps { 7 | metadata: LinkMetadata 8 | onClose?: () => void 9 | showCloseButton?: boolean 10 | className?: string 11 | } 12 | 13 | export function LinkPreview({ metadata, onClose, showCloseButton, className }: LinkPreviewProps) { 14 | if (!metadata?.url) return null 15 | 16 | const { url, title, description, image, favicon } = metadata 17 | 18 | return ( 19 | 28 | {image && ( 29 |
30 | {title 36 |
37 | )} 38 |
39 | {favicon && ( 40 |
41 | Site icon 42 |
43 | )} 44 |
45 |

46 | {title ?? url} 47 |

48 | {description && ( 49 |

{description}

50 | )} 51 |

{new URL(url).hostname}

52 |
53 | {showCloseButton && onClose && ( 54 | 64 | )} 65 |
66 |
67 | ) 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /public/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /public/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { 4 | createContext, 5 | useContext, 6 | useEffect, 7 | useRef, 8 | useState, 9 | ReactNode, 10 | cloneElement, 11 | isValidElement, 12 | HTMLAttributes, 13 | forwardRef 14 | } from 'react' 15 | import { cn } from '@/lib/utils' 16 | 17 | interface DropdownContextValue { 18 | open: boolean 19 | setOpen: (value: boolean) => void 20 | menuRef: React.MutableRefObject 21 | } 22 | 23 | const DropdownMenuContext = createContext(undefined) 24 | 25 | const useDropdownContext = () => { 26 | const context = useContext(DropdownMenuContext) 27 | if (!context) { 28 | throw new Error('Dropdown components must be used within ') 29 | } 30 | return context 31 | } 32 | 33 | interface DropdownMenuProps { 34 | children: ReactNode 35 | } 36 | 37 | export function DropdownMenu({ children }: DropdownMenuProps) { 38 | const [open, setOpen] = useState(false) 39 | const menuRef = useRef(null) 40 | 41 | useEffect(() => { 42 | if (!open) return 43 | 44 | const handleClick = (event: MouseEvent) => { 45 | if (!menuRef.current) return 46 | if (!menuRef.current.contains(event.target as Node)) { 47 | setOpen(false) 48 | } 49 | } 50 | 51 | const handleEscape = (event: KeyboardEvent) => { 52 | if (event.key === 'Escape') { 53 | setOpen(false) 54 | } 55 | } 56 | 57 | document.addEventListener('mousedown', handleClick) 58 | document.addEventListener('keydown', handleEscape) 59 | 60 | return () => { 61 | document.removeEventListener('mousedown', handleClick) 62 | document.removeEventListener('keydown', handleEscape) 63 | } 64 | }, [open]) 65 | 66 | return ( 67 | 68 |
{children}
69 |
70 | ) 71 | } 72 | 73 | interface DropdownMenuTriggerProps { 74 | children: ReactNode 75 | asChild?: boolean 76 | } 77 | 78 | export const DropdownMenuTrigger = ({ children, asChild }: DropdownMenuTriggerProps) => { 79 | const { open, setOpen } = useDropdownContext() 80 | 81 | const handleToggle = () => setOpen(!open) 82 | 83 | if (asChild && isValidElement(children)) { 84 | const child = children as React.ReactElement<{ onClick?: React.MouseEventHandler }> 85 | return cloneElement(child, { 86 | onClick: (event: React.MouseEvent) => { 87 | child.props.onClick?.(event) 88 | if (!event.defaultPrevented) { 89 | handleToggle() 90 | } 91 | } 92 | }) 93 | } 94 | 95 | return ( 96 | 99 | ) 100 | } 101 | 102 | interface DropdownMenuContentProps extends HTMLAttributes { 103 | children: ReactNode 104 | align?: 'start' | 'center' | 'end' 105 | } 106 | 107 | export const DropdownMenuContent = forwardRef( 108 | ({ children, className, align = 'start', ...props }, ref) => { 109 | const { open, menuRef } = useDropdownContext() 110 | 111 | if (!open) return null 112 | 113 | const alignmentStyles = 114 | align === 'end' 115 | ? 'right-0' 116 | : align === 'center' 117 | ? 'left-1/2 -translate-x-1/2' 118 | : 'left-0' 119 | 120 | return ( 121 |
{ 123 | if (typeof ref === 'function') { 124 | ref(node) 125 | } else if (ref) { 126 | ref.current = node 127 | } 128 | menuRef.current = node 129 | }} 130 | className={cn( 131 | 'absolute z-50 mt-2 min-w-[180px] overflow-hidden rounded-xl border border-neutral-200 bg-white shadow-lg dark:border-neutral-800 dark:bg-neutral-900', 132 | alignmentStyles, 133 | className 134 | )} 135 | {...props} 136 | > 137 | {children} 138 |
139 | ) 140 | } 141 | ) 142 | 143 | DropdownMenuContent.displayName = 'DropdownMenuContent' 144 | 145 | interface DropdownMenuItemProps extends HTMLAttributes { 146 | children: ReactNode 147 | } 148 | 149 | export const DropdownMenuItem = forwardRef( 150 | ({ children, className, onClick, ...props }, ref) => { 151 | const { setOpen } = useDropdownContext() 152 | 153 | return ( 154 | 171 | ) 172 | } 173 | ) 174 | 175 | DropdownMenuItem.displayName = 'DropdownMenuItem' 176 | 177 | 178 | -------------------------------------------------------------------------------- /src/components/ui/post-composer.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useEffect, useRef, useState } from 'react' 4 | import { AnimatePresence, motion } from 'framer-motion' 5 | import type { Variants } from 'framer-motion' 6 | import { Image as ImageIcon, Send } from 'lucide-react' 7 | import { cn } from '@/lib/utils' 8 | import { Avatar } from '@/components/ui/avatar' 9 | import { extractUrls, fetchLinkMetadata, LinkMetadata } from '@/lib/link-preview' 10 | import { LinkPreview } from '@/components/ui/link-preview' 11 | 12 | interface PostComposerProps { 13 | avatar?: string | null 14 | onSubmit?: (text: string, metadata?: LinkMetadata | null) => void 15 | className?: string 16 | } 17 | 18 | const expandAnimation: Variants = { 19 | initial: { height: 40, opacity: 0 }, 20 | animate: { 21 | height: 'auto', 22 | opacity: 1, 23 | transition: { 24 | type: 'spring', 25 | stiffness: 700, 26 | damping: 30 27 | } 28 | }, 29 | exit: { 30 | height: 40, 31 | opacity: 0, 32 | transition: { 33 | type: 'spring', 34 | stiffness: 700, 35 | damping: 30 36 | } 37 | } 38 | } 39 | 40 | export function PostComposer({ avatar, onSubmit, className }: PostComposerProps) { 41 | const [isExpanded, setIsExpanded] = useState(false) 42 | const [isFocused, setIsFocused] = useState(false) 43 | const [text, setText] = useState('') 44 | const [linkPreview, setLinkPreview] = useState(null) 45 | const [isLoadingPreview, setIsLoadingPreview] = useState(false) 46 | 47 | const composerRef = useRef(null) 48 | const textareaRef = useRef(null) 49 | 50 | useEffect(() => { 51 | if (isExpanded && textareaRef.current) { 52 | textareaRef.current.focus() 53 | } 54 | }, [isExpanded]) 55 | 56 | useEffect(() => { 57 | const handleClickOutside = (event: MouseEvent) => { 58 | if (!composerRef.current) return 59 | if (!composerRef.current.contains(event.target as Node) && !text) { 60 | setIsExpanded(false) 61 | setIsFocused(false) 62 | } 63 | } 64 | 65 | document.addEventListener('mousedown', handleClickOutside) 66 | return () => document.removeEventListener('mousedown', handleClickOutside) 67 | }, [text]) 68 | 69 | useEffect(() => { 70 | let isMounted = true 71 | const urls = extractUrls(text) 72 | 73 | if (!urls.length) { 74 | setLinkPreview(null) 75 | return 76 | } 77 | 78 | setIsLoadingPreview(true) 79 | 80 | const timer = setTimeout(async () => { 81 | const metadata = await fetchLinkMetadata(urls[0]) 82 | if (isMounted) { 83 | setLinkPreview(metadata) 84 | setIsLoadingPreview(false) 85 | } 86 | }, 400) 87 | 88 | return () => { 89 | isMounted = false 90 | clearTimeout(timer) 91 | } 92 | }, [text]) 93 | 94 | const handleSubmit = () => { 95 | const trimmed = text.trim() 96 | if (!trimmed) return 97 | 98 | onSubmit?.(trimmed, linkPreview) 99 | setText('') 100 | setLinkPreview(null) 101 | setIsExpanded(false) 102 | setIsFocused(false) 103 | } 104 | 105 | return ( 106 | { 110 | setIsExpanded(true) 111 | setIsFocused(true) 112 | }} 113 | className={cn( 114 | 'w-full max-w-2xl rounded-3xl bg-neutral-100 p-4 shadow-sm dark:border dark:border-neutral-800 dark:bg-neutral-900', 115 | className 116 | )} 117 | > 118 |
119 | 120 |
121 | 122 | 123 | setText(event.target.value)} 128 | onFocus={() => setIsFocused(true)} 129 | className={cn( 130 | 'w-full resize-none border-none bg-transparent text-sm text-neutral-800 outline-none transition-all placeholder:text-neutral-500 dark:text-neutral-200 dark:placeholder:text-neutral-500', 131 | isExpanded ? 'min-h-[120px] py-1' : 'h-10 py-2' 132 | )} 133 | /> 134 | 135 | {linkPreview && ( 136 | 137 | setLinkPreview(null)} 140 | showCloseButton 141 | /> 142 | 143 | )} 144 | 145 | {isExpanded && ( 146 | 147 |
148 | 155 | {isLoadingPreview && Loading preview…} 156 |
157 | 172 |
173 | )} 174 |
175 |
176 |
177 |
178 |
179 | ) 180 | } 181 | 182 | 183 | -------------------------------------------------------------------------------- /src/components/ui/sidebar.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import Link from 'next/link' 4 | import Image from 'next/image' 5 | import React, { 6 | createContext, 7 | useContext, 8 | useMemo, 9 | useState, 10 | useCallback, 11 | ReactNode, 12 | MouseEvent 13 | } from 'react' 14 | import { AnimatePresence, motion } from 'framer-motion' 15 | import { Menu, X } from 'lucide-react' 16 | import { cn } from '@/lib/utils' 17 | 18 | interface SidebarContextValue { 19 | open: boolean 20 | setOpen: (value: boolean) => void 21 | animate: boolean 22 | } 23 | 24 | const SidebarContext = createContext(undefined) 25 | 26 | interface SidebarProviderProps { 27 | children: ReactNode 28 | open?: boolean 29 | setOpen?: (value: boolean) => void 30 | animate?: boolean 31 | } 32 | 33 | export const SidebarProvider = ({ 34 | children, 35 | open: controlledOpen, 36 | setOpen: controlledSetOpen, 37 | animate = true 38 | }: SidebarProviderProps) => { 39 | const [uncontrolledOpen, setUncontrolledOpen] = useState(false) 40 | 41 | const open = controlledOpen ?? uncontrolledOpen 42 | const setOpen = useCallback( 43 | (value: boolean) => { 44 | if (controlledSetOpen) { 45 | controlledSetOpen(value) 46 | } else { 47 | setUncontrolledOpen(value) 48 | } 49 | }, 50 | [controlledSetOpen] 51 | ) 52 | 53 | const value = useMemo( 54 | () => ({ 55 | open, 56 | setOpen, 57 | animate 58 | }), 59 | [open, setOpen, animate] 60 | ) 61 | 62 | return {children} 63 | } 64 | 65 | export const useSidebar = () => { 66 | const context = useContext(SidebarContext) 67 | if (!context) { 68 | throw new Error('useSidebar must be used within a SidebarProvider') 69 | } 70 | return context 71 | } 72 | 73 | interface SidebarProps extends SidebarProviderProps { 74 | children: ReactNode 75 | } 76 | 77 | export const Sidebar = ({ children, open, setOpen, animate }: SidebarProps) => { 78 | return ( 79 | 80 | {children} 81 | 82 | ) 83 | } 84 | 85 | export const SidebarBody = (props: React.ComponentProps) => { 86 | const { className, children, ...rest } = props 87 | const resolvedChildren = children as React.ReactNode 88 | 89 | return ( 90 | <> 91 | 92 | {resolvedChildren} 93 | 94 | 95 | {resolvedChildren} 96 | 97 | 98 | ) 99 | } 100 | 101 | export const DesktopSidebar = ({ 102 | className, 103 | children, 104 | ...props 105 | }: React.ComponentProps) => { 106 | const { open, setOpen, animate } = useSidebar() 107 | 108 | return ( 109 | 123 | ) 124 | } 125 | 126 | export const MobileSidebar = ({ 127 | className, 128 | children, 129 | ...props 130 | }: React.ComponentProps<'div'>) => { 131 | const { open, setOpen } = useSidebar() 132 | 133 | return ( 134 |
138 |
139 | 140 | Yours Logo 147 | Yours 148 | 149 | setOpen(!open)} 152 | /> 153 |
154 | 155 | 156 | {open && ( 157 | 167 | 174 | {children} 175 | 176 | )} 177 | 178 |
179 | ) 180 | } 181 | 182 | interface Links { 183 | label: string 184 | href: string 185 | icon: ReactNode 186 | target?: string 187 | rel?: string 188 | onClick?: (event: MouseEvent) => void 189 | } 190 | 191 | interface SidebarLinkProps extends Omit, 'href'> { 192 | link: Links 193 | className?: string 194 | } 195 | 196 | export const SidebarLink = ({ link, className, ...rest }: SidebarLinkProps) => { 197 | const { open, animate, setOpen } = useSidebar() 198 | 199 | const handleClick = (event: MouseEvent) => { 200 | if (typeof window !== 'undefined' && window.innerWidth < 768) { 201 | setOpen(false) 202 | } 203 | 204 | link.onClick?.(event) 205 | } 206 | 207 | return ( 208 | 220 | 221 | {link.icon} 222 | 223 | 230 | {link.label} 231 | 232 | 233 | ) 234 | } 235 | 236 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/components/ui/social-card.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState } from 'react' 4 | import Link from 'next/link' 5 | import { AnimatePresence, motion } from 'framer-motion' 6 | import { 7 | Bookmark, 8 | Heart, 9 | MessageCircle, 10 | MoreHorizontal, 11 | Share2, 12 | Trash2 13 | } from 'lucide-react' 14 | import { cn } from '@/lib/utils' 15 | import { Avatar } from '@/components/ui/avatar' 16 | import { LinkPreview } from '@/components/ui/link-preview' 17 | import { LinkMetadata } from '@/lib/link-preview' 18 | import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/ui/dropdown-menu' 19 | import { useToast } from '@/hooks/use-toast' 20 | 21 | interface AuthorInfo { 22 | name?: string 23 | username?: string 24 | avatar?: string 25 | timeAgo?: string 26 | address?: string 27 | } 28 | 29 | interface ContentInfo { 30 | text?: string 31 | link?: LinkMetadata 32 | } 33 | 34 | interface EngagementInfo { 35 | likes?: number 36 | comments?: number 37 | shares?: number 38 | isLiked?: boolean 39 | isBookmarked?: boolean 40 | } 41 | 42 | interface SocialCardProps { 43 | id?: string 44 | author?: AuthorInfo 45 | content?: ContentInfo 46 | engagement?: EngagementInfo 47 | onLike?: () => void 48 | onComment?: () => void 49 | onShare?: () => void 50 | onBookmark?: () => void 51 | onDelete?: () => void 52 | userAddress?: string 53 | className?: string 54 | } 55 | 56 | export function SocialCard({ 57 | id, 58 | author, 59 | content, 60 | engagement, 61 | onLike, 62 | onComment, 63 | onShare, 64 | onBookmark, 65 | onDelete, 66 | userAddress, 67 | className 68 | }: SocialCardProps) { 69 | const { toast } = useToast() 70 | const [isDeleting, setIsDeleting] = useState(false) 71 | const [isLiked, setIsLiked] = useState(Boolean(engagement?.isLiked)) 72 | const [isBookmarked, setIsBookmarked] = useState(Boolean(engagement?.isBookmarked)) 73 | const [likes, setLikes] = useState(engagement?.likes ?? 0) 74 | 75 | const handleLike = () => { 76 | setIsLiked((prev) => !prev) 77 | setLikes((prev) => (isLiked ? Math.max(0, prev - 1) : prev + 1)) 78 | onLike?.() 79 | } 80 | 81 | const handleBookmark = () => { 82 | setIsBookmarked((prev) => !prev) 83 | onBookmark?.() 84 | } 85 | 86 | const handleDelete = () => { 87 | setIsDeleting(true) 88 | toast({ 89 | variant: 'success', 90 | title: 'Post deleted', 91 | description: 'This is a placeholder action.' 92 | }) 93 | setTimeout(() => { 94 | onDelete?.() 95 | }, 250) 96 | } 97 | 98 | const handleComment = () => onComment?.() 99 | const handleShare = () => onShare?.() 100 | 101 | return ( 102 | 103 | {!isDeleting && ( 104 | 113 |
114 |
115 |
116 | 117 | 122 | 123 |
124 | 125 | 126 | {author?.name ?? 'Yours User'} 127 | 128 | 129 | @{author?.username ?? 'username'} 130 | 131 | 132 | {author?.timeAgo ?? 'moments ago'} 133 |
134 |
135 | {userAddress && author?.address && userAddress === author.address ? ( 136 | 137 | 138 | 144 | 145 | 146 | { 148 | event.preventDefault() 149 | handleDelete() 150 | }} 151 | className="text-red-600 dark:text-red-400" 152 | > 153 | 154 | Delete post 155 | 156 | 157 | 158 | ) : null} 159 |
160 | 161 |
162 | {content?.text && ( 163 |

{content.text}

164 | )} 165 | {content?.link && } 166 |
167 | 168 |
169 |
170 | 181 | 182 | 190 | 191 | 199 |
200 | 201 | 213 |
214 |
215 |
216 | )} 217 |
218 | ) 219 | } 220 | 221 | 222 | -------------------------------------------------------------------------------- /src/components/layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createContext, useCallback, useContext, useMemo, useState } from 'react' 4 | import type { MouseEvent as ReactMouseEvent } from 'react' 5 | import Link from 'next/link' 6 | import Image from 'next/image' 7 | import { motion } from 'framer-motion' 8 | import { 9 | LayoutDashboard, 10 | LogOut, 11 | Moon, 12 | Settings, 13 | Sun, 14 | UserCog, 15 | Users, 16 | Wallet 17 | } from 'lucide-react' 18 | import { cn } from '@/lib/utils' 19 | import { Sidebar, SidebarBody, SidebarLink } from '@/components/ui/sidebar' 20 | import { Avatar } from '@/components/ui/avatar' 21 | import { useTheme } from '@/components/theme-provider' 22 | 23 | interface WalletUser { 24 | id: string 25 | address: string 26 | name: string 27 | username: string 28 | avatar: string 29 | addresses: { 30 | bsvAddress: string 31 | identityAddress?: string 32 | ordAddress?: string 33 | } 34 | } 35 | 36 | interface WalletContextValue { 37 | isConnected: boolean 38 | address: string | null 39 | user: WalletUser | null 40 | connect: () => Promise 41 | disconnect: () => Promise 42 | } 43 | 44 | const WalletContext = createContext(undefined) 45 | 46 | export const useWallet = () => { 47 | const context = useContext(WalletContext) 48 | if (!context) { 49 | throw new Error('useWallet must be used within WalletProvider') 50 | } 51 | return context 52 | } 53 | 54 | export function WalletProvider({ children }: { children: React.ReactNode }) { 55 | const value = useWalletProviderValue() 56 | return {children} 57 | } 58 | 59 | function useWalletProviderValue(): WalletContextValue { 60 | const [isConnected, setIsConnected] = useState(false) 61 | const [user, setUser] = useState(null) 62 | 63 | const connect = useCallback(async () => { 64 | if (typeof window === 'undefined' || !window.yours) { 65 | console.warn('Yours wallet provider not detected in this environment.') 66 | return 67 | } 68 | 69 | try { 70 | const provider = window.yours 71 | const publicKey = await provider.connect() 72 | 73 | const [profile, addresses] = await Promise.all([ 74 | provider.getSocialProfile?.().catch((error) => { 75 | console.warn('Unable to fetch Yours social profile.', error) 76 | return undefined 77 | }), 78 | provider.getAddresses?.().catch((error) => { 79 | console.warn('Unable to fetch Yours addresses.', error) 80 | return undefined 81 | }) 82 | ]) 83 | 84 | const bsvAddress = addresses?.bsvAddress 85 | if (!bsvAddress) { 86 | throw new Error('Yours provider did not return a BSV address.') 87 | } 88 | 89 | const usernameSource = 90 | profile?.username ?? 91 | profile?.handle ?? 92 | addresses?.identityAddress ?? 93 | publicKey ?? 94 | bsvAddress 95 | 96 | const resolvedUsername = usernameSource || 'yours-user' 97 | const fallbackAvatar = `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(resolvedUsername)}` 98 | 99 | setUser({ 100 | id: publicKey ?? bsvAddress, 101 | address: bsvAddress, 102 | name: profile?.displayName ?? 'Anonymous', 103 | username: resolvedUsername, 104 | avatar: profile?.avatar ?? fallbackAvatar, 105 | addresses: { 106 | bsvAddress, 107 | identityAddress: addresses?.identityAddress, 108 | ordAddress: addresses?.ordAddress 109 | } 110 | }) 111 | setIsConnected(true) 112 | } catch (error) { 113 | console.error('Failed to connect to Yours wallet.', error) 114 | setIsConnected(false) 115 | setUser(null) 116 | } 117 | }, []) 118 | 119 | const disconnect = useCallback(async () => { 120 | if (typeof window !== 'undefined') { 121 | try { 122 | await window.yours?.disconnect?.() 123 | } catch (error) { 124 | console.warn('Failed to disconnect from Yours wallet.', error) 125 | } 126 | } 127 | 128 | setIsConnected(false) 129 | setUser(null) 130 | }, []) 131 | 132 | return useMemo( 133 | () => ({ 134 | isConnected, 135 | address: user?.address ?? null, 136 | user, 137 | connect, 138 | disconnect 139 | }), 140 | [isConnected, user, connect, disconnect] 141 | ) 142 | } 143 | 144 | export function AppLayout({ children }: { children: React.ReactNode }) { 145 | const { theme, setTheme } = useTheme() 146 | const [isSidebarOpen, setIsSidebarOpen] = useState(false) 147 | const { isConnected, user, connect, disconnect } = useWallet() 148 | 149 | const formattedAddress = user?.address 150 | ? `${user.address.slice(0, 6)}...${user.address.slice(-4)}` 151 | : 'Connect Wallet' 152 | 153 | const navigationLinks = [ 154 | { 155 | label: 'Home', 156 | href: '/', 157 | icon: 158 | }, 159 | { 160 | label: 'Social', 161 | href: '/social', 162 | icon: 163 | }, 164 | { 165 | label: 'Profile', 166 | href: user ? `/profile/${user.username}` : '#', 167 | icon: 168 | }, 169 | { 170 | label: 'Settings', 171 | href: '#', 172 | icon: 173 | }, 174 | { 175 | label: theme === 'dark' ? 'Switch to Light' : 'Switch to Dark', 176 | href: '#', 177 | onClick: () => setTheme(theme === 'dark' ? 'light' : 'dark'), 178 | icon: 179 | theme === 'dark' ? : 180 | }, 181 | { 182 | label: 'Sign out', 183 | href: '#', 184 | onClick: (event: ReactMouseEvent) => { 185 | event.preventDefault() 186 | void disconnect() 187 | }, 188 | icon: 189 | } 190 | ] 191 | 192 | return ( 193 |
194 | 195 | 196 |
197 | {isSidebarOpen ? : } 198 | 207 |
208 |
209 | 224 | ) 225 | }} 226 | className={cn(!isConnected && 'opacity-80')} 227 | /> 228 | {!isConnected && ( 229 | 237 | )} 238 |
239 |
240 |
241 |
242 |
243 | {children} 244 |
245 |
246 |
247 | ) 248 | } 249 | 250 | const Logo = () => ( 251 | 252 | Yours Logo 259 | 260 | Yours 261 | 262 | 263 | ) 264 | 265 | const LogoIcon = () => ( 266 | 267 | Yours Logo 268 | 269 | ) 270 | -------------------------------------------------------------------------------- /src/app/social/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useEffect, useMemo, useState } from 'react' 4 | import { PostComposer } from '@/components/ui/post-composer' 5 | import { SocialCard } from '@/components/ui/social-card' 6 | import { ConnectWalletButton } from '@/components/ui/connect-button' 7 | import { formatTimeAgo } from '@/lib/utils' 8 | import { LinkMetadata } from '@/lib/link-preview' 9 | import { useWallet } from '@/components/layout' 10 | 11 | interface Author { 12 | id: string 13 | address: string 14 | name: string 15 | username: string 16 | avatar: string 17 | } 18 | 19 | interface Post { 20 | id: string 21 | text: string 22 | createdAt: string 23 | author: Author 24 | linkPreview?: LinkMetadata 25 | engagement: { 26 | likes: number 27 | comments: number 28 | shares: number 29 | } 30 | } 31 | 32 | const placeholderAuthors: Author[] = [ 33 | { 34 | id: '0', 35 | address: '1placeholder0001', 36 | name: 'Captain Caca', 37 | username: 'captaincaca', 38 | avatar: '/pfp/1.png' 39 | }, 40 | { 41 | id: '1', 42 | address: '1placeholder0002', 43 | name: 'Gilda Gasblast', 44 | username: 'gildagasblast', 45 | avatar: '/pfp/4.jpg' 46 | }, 47 | { 48 | id: '2', 49 | address: '1placeholder0003', 50 | name: 'Duke Doodie', 51 | username: 'doodieduke', 52 | avatar: '/pfp/5.png' 53 | }, 54 | { 55 | id: '3', 56 | address: '1placeholder0004', 57 | name: 'Lady Logjam', 58 | username: 'ladylogjam', 59 | avatar: '/pfp/2.jpg' 60 | }, 61 | { 62 | id: '4', 63 | address: '1placeholder0005', 64 | name: 'Baron von Blowout', 65 | username: 'baronblowout', 66 | avatar: '/pfp/3.jpg' 67 | }, 68 | { 69 | id: '5', 70 | address: '1placeholder0006', 71 | name: 'Professor Pootson', 72 | username: 'professorpootson', 73 | avatar: '/pfp/wayne.jpg' 74 | } 75 | ] 76 | 77 | const placeholderPosts: Post[] = [ 78 | { 79 | id: 'p-1', 80 | text: "Captain's log: shipped a smart bidet that salutes every time it flushes. Crew morale is high, methane levels even higher. 💩🚽", 81 | createdAt: new Date(Date.now() - 1000 * 60 * 5).toISOString(), 82 | author: placeholderAuthors[0], 83 | linkPreview: { 84 | url: 'https://yours.org', 85 | title: 'Flush Fleet Roadmap', 86 | description: 'Keeping every deployment shipshape, shipshank, and freshly shanked.', 87 | image: '/pfp/levelspost.jpg', 88 | favicon: 'https://www.google.com/s2/favicons?domain=yours.org' 89 | }, 90 | engagement: { 91 | likes: 12, 92 | comments: 3, 93 | shares: 4 94 | } 95 | }, 96 | { 97 | id: 'p-2', 98 | text: "QA sprint wrapped! New feature auto-detects silent-but-deadlies and plays a victory trombone. User delight measured in decibels… and tears.", 99 | createdAt: new Date(Date.now() - 1000 * 60 * 30).toISOString(), 100 | author: placeholderAuthors[1], 101 | engagement: { 102 | likes: 32, 103 | comments: 6, 104 | shares: 9 105 | } 106 | }, 107 | { 108 | id: 'p-3', 109 | text: 'Gas fee update: we now offset every toot-saction with a planted whoopee cushion. Sustainable silliness is the only path forward.', 110 | createdAt: new Date(Date.now() - 1000 * 60 * 90).toISOString(), 111 | author: placeholderAuthors[2], 112 | engagement: { 113 | likes: 54, 114 | comments: 11, 115 | shares: 20 116 | } 117 | }, 118 | { 119 | id: 'p-4', 120 | text: 'Lady Logjam here—implemented a flush-to-earn model. Users stack sats every time they drop a performance review in porcelain.', 121 | createdAt: new Date(Date.now() - 1000 * 60 * 150).toISOString(), 122 | author: placeholderAuthors[3], 123 | linkPreview: { 124 | url: 'https://yours.org/reports', 125 | title: 'Flushonomics Whitepaper', 126 | description: 'A 30-page thesis on liquidity events, both figurative and… aromatic.', 127 | image: '/pfp/chart.jpg', 128 | favicon: 'https://www.google.com/s2/favicons?domain=yours.org' 129 | }, 130 | engagement: { 131 | likes: 21, 132 | comments: 5, 133 | shares: 7 134 | } 135 | }, 136 | { 137 | id: 'p-5', 138 | text: 'Baron von Blowout just forked the whoopee chain. New consensus: proof-of-guffaw keeps the gas flowing and the devs glowing.', 139 | createdAt: new Date(Date.now() - 1000 * 60 * 210).toISOString(), 140 | author: placeholderAuthors[4], 141 | engagement: { 142 | likes: 47, 143 | comments: 8, 144 | shares: 16 145 | } 146 | }, 147 | { 148 | id: 'p-6', 149 | text: 'Professor Pootson shipped an AI that predicts office crop-dusts and sends pre-emptive candle deploys. Accuracy: 96%, APR: aromatic.', 150 | createdAt: new Date(Date.now() - 1000 * 60 * 300).toISOString(), 151 | author: placeholderAuthors[5], 152 | engagement: { 153 | likes: 39, 154 | comments: 4, 155 | shares: 12 156 | } 157 | } 158 | ] 159 | 160 | export default function SocialPage() { 161 | const { isConnected, user, connect } = useWallet() 162 | const [posts, setPosts] = useState([]) 163 | const [isLoading, setIsLoading] = useState(true) 164 | 165 | useEffect(() => { 166 | const timeout = setTimeout(() => { 167 | setPosts(placeholderPosts) 168 | setIsLoading(false) 169 | }, 600) 170 | 171 | return () => clearTimeout(timeout) 172 | }, []) 173 | 174 | const activeAuthor = useMemo(() => { 175 | if (user) return user 176 | return placeholderAuthors[0] 177 | }, [user]) 178 | 179 | const handleCreatePost = (text: string, metadata?: LinkMetadata | null) => { 180 | const newPost: Post = { 181 | id: `p-${Date.now()}`, 182 | text, 183 | createdAt: new Date().toISOString(), 184 | author: { 185 | id: activeAuthor.id, 186 | address: activeAuthor.address, 187 | name: activeAuthor.name, 188 | username: activeAuthor.username, 189 | avatar: activeAuthor.avatar 190 | }, 191 | linkPreview: metadata || undefined, 192 | engagement: { 193 | likes: 0, 194 | comments: 0, 195 | shares: 0 196 | } 197 | } 198 | 199 | setPosts((prev) => [newPost, ...prev]) 200 | } 201 | 202 | const handleDeletePost = (postId: string) => { 203 | setPosts((prev) => prev.filter((post) => post.id !== postId)) 204 | } 205 | 206 | if (isLoading) { 207 | return ( 208 |
209 |
210 |
211 | ) 212 | } 213 | 214 | return ( 215 |
216 |
217 | {isConnected ? ( 218 | 219 | ) : ( 220 |
221 |

Connect to start posting placeholder updates.

222 |
223 | 224 |
225 |
226 | )} 227 |
228 | 229 |
230 | {posts.length ? ( 231 | posts.map((post, index) => ( 232 | 239 | )) 240 | ) : ( 241 |
242 | No posts yet. Share the first update about your build! 243 |
244 | )} 245 |
246 |
247 | ) 248 | } 249 | 250 | function SocialCardWrapper({ 251 | post, 252 | isFirst, 253 | onDelete, 254 | currentAddress 255 | }: { 256 | post: Post 257 | isFirst: boolean 258 | onDelete: (id: string) => void 259 | currentAddress: string | null 260 | }) { 261 | return ( 262 | {}} 277 | onComment={() => {}} 278 | onShare={() => {}} 279 | onBookmark={() => {}} 280 | onDelete={() => onDelete(post.id)} 281 | userAddress={currentAddress ?? undefined} 282 | className={isFirst ? 'ring-1 ring-neutral-200 dark:ring-neutral-800' : undefined} 283 | /> 284 | ) 285 | } 286 | 287 | 288 | -------------------------------------------------------------------------------- /src/app/chart.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { createChart, ColorType, UTCTimestamp } from 'lightweight-charts' 4 | // @ts-ignore 5 | import React, { useEffect, useRef } from 'react' 6 | import formatNumber from '@/utils/format-number' 7 | import useExchangeRate from '@/utils/hooks/useExchangeRate' 8 | import classNames from 'classnames' 9 | import type { LockHistoryEntry, UnlockHistoryEntry } from '@/types/lock-history' 10 | 11 | const INTERVAL_MILLISECONDS = { 12 | '10m': 10 * 60 * 1000, 13 | '1h': 60 * 60 * 1000, 14 | '4h': 60 * 60 * 1000 * 4, 15 | '6h': 60 * 60 * 1000 * 6, 16 | '12h': 60 * 60 * 1000 * 12, 17 | '1D': 24 * 60 * 60 * 1000 18 | } as const 19 | 20 | type IntervalKey = keyof typeof INTERVAL_MILLISECONDS 21 | 22 | const TABS: IntervalKey[] = ['1h', '4h', '6h', '12h', '1D'] 23 | 24 | const toHeightNumber = (value: number | string) => 25 | typeof value === 'number' ? value : parseInt(value, 10) 26 | 27 | function groupData(data: LockHistoryEntry[], interval: IntervalKey) { 28 | if (!Array.isArray(data) || data.length === 0) { 29 | return {} 30 | } 31 | 32 | const groupedData: Record = {} 33 | const intervalMilliseconds = INTERVAL_MILLISECONDS 34 | 35 | data.forEach((item) => { 36 | const date = new Date(item.time * 1000) 37 | // Round down the date to the nearest interval 38 | const roundedDate = new Date( 39 | Math.floor(date.getTime() / intervalMilliseconds[interval]) * intervalMilliseconds[interval] 40 | ) 41 | 42 | // Use the rounded date as a key for grouping 43 | const key = roundedDate.toISOString() 44 | if (!groupedData[key]) { 45 | groupedData[key] = [] 46 | } 47 | groupedData[key].push(item) 48 | }) 49 | 50 | return groupedData 51 | } 52 | 53 | export default function Chart(props: { 54 | height: number 55 | data: LockHistoryEntry[] | null | undefined 56 | unlockData: UnlockHistoryEntry[] | null | undefined 57 | mempoolData: Record | null | undefined 58 | }) { 59 | const { data, unlockData, mempoolData, height } = props 60 | const { exchangeRate } = useExchangeRate() 61 | const [selectedTab, setSelectedTab] = React.useState('1D') 62 | 63 | const { dayAgoTvl, tvl, percentChange, ready } = React.useMemo(() => { 64 | const safeData = Array.isArray(data) ? data : [] 65 | const safeUnlockData = Array.isArray(unlockData) ? unlockData : [] 66 | 67 | if (safeData.length === 0) { 68 | return { 69 | tvl: 0, 70 | percentChange: 0, 71 | dayAgoTvl: 0, 72 | ready: false 73 | } 74 | } 75 | 76 | if (safeUnlockData.length === 0) { 77 | return { 78 | tvl: 0, 79 | percentChange: 0, 80 | dayAgoTvl: 0, 81 | ready: false 82 | } 83 | } 84 | 85 | const lastLock = safeData.slice(-1)[0] 86 | const filteredUnlocks = safeUnlockData.filter( 87 | (e) => toHeightNumber(e.height) <= toHeightNumber(lastLock.height) 88 | ) 89 | const lastUnlock = filteredUnlocks.slice(-1)[0] 90 | 91 | const lastLockHeight = toHeightNumber(lastLock.height) 92 | 93 | if (!lastUnlock) { 94 | return { 95 | tvl: 0, 96 | percentChange: 0, 97 | dayAgoTvl: 0, 98 | ready: false 99 | } 100 | } 101 | 102 | const dayAgoLock = safeData.filter((e) => toHeightNumber(e.height) < lastLockHeight - 144).slice(-1)[0] 103 | if (!dayAgoLock) { 104 | const tvl = (parseInt(lastLock.sum, 10) - parseInt(lastUnlock.sum, 10)) / 1e8 105 | return { 106 | tvl, 107 | percentChange: 0, 108 | dayAgoTvl: tvl, 109 | ready: true 110 | } 111 | } 112 | 113 | const dayAgoUnlockCandidates = safeUnlockData.filter( 114 | (e) => toHeightNumber(e.height) <= toHeightNumber(dayAgoLock.height) 115 | ) 116 | const dayAgoUnlock = dayAgoUnlockCandidates.slice(-1)[0] 117 | 118 | if (!dayAgoUnlock) { 119 | const tvl = (parseInt(lastLock.sum, 10) - parseInt(lastUnlock.sum, 10)) / 1e8 120 | return { 121 | tvl, 122 | percentChange: 0, 123 | dayAgoTvl: tvl, 124 | ready: true 125 | } 126 | } 127 | 128 | const tvl = (parseInt(lastLock.sum, 10) - parseInt(lastUnlock.sum, 10)) / 1e8 129 | const dayAgoTvl = (parseInt(dayAgoLock.sum, 10) - parseInt(dayAgoUnlock.sum, 10)) / 1e8 130 | 131 | const percentChange = 132 | dayAgoTvl === 0 ? 0 : ((tvl - dayAgoTvl) / dayAgoTvl) * 100 133 | 134 | return { tvl, percentChange, dayAgoTvl, ready: true } 135 | }, [data, unlockData, mempoolData]) 136 | 137 | const totalCirculatingSupply = 19600000 // 19.6M 138 | 139 | const ref = useRef(null) 140 | 141 | useEffect(() => { 142 | if (!ref.current) { 143 | return 144 | } 145 | 146 | const safeData = Array.isArray(data) ? data : [] 147 | const safeUnlockData = Array.isArray(unlockData) ? unlockData : [] 148 | const groups = groupData(safeData, selectedTab) 149 | 150 | const parsedData = Object.keys(groups) 151 | .map((key) => { 152 | const values = groups[key].map((e) => parseInt(e.sum)) 153 | const maxHeight = Math.max(...groups[key].map((e) => toHeightNumber(e.height))) 154 | const lastUnlock = safeUnlockData 155 | .filter((e) => toHeightNumber(e.height) <= maxHeight) 156 | .reduce((a, e) => a + parseInt(e.sats), 0) 157 | 158 | return { 159 | time: groups[key][0].time as UTCTimestamp, 160 | value: (Math.max(...values) - lastUnlock) / 1e8 161 | } 162 | }) 163 | .sort((a, b) => a?.time - b?.time) 164 | 165 | const volumeData = Object.keys(groups) 166 | .map((key) => { 167 | const values = groups[key].map((e) => parseInt(e.sum)) 168 | const heights = groups[key].map((e) => toHeightNumber(e.height)) 169 | const maxHeight = Math.max(...heights) 170 | const minHeight = Math.min(...heights) 171 | const unlocks = safeUnlockData.filter((e) => { 172 | const height = toHeightNumber(e.height) 173 | return height >= minHeight && height <= maxHeight 174 | }) 175 | 176 | const lockVolume = groups[key].map((e) => parseInt(e.sats)).reduce((a, e) => a + e, 0) 177 | const unlockVolume = unlocks.map((e) => parseInt(e.sats)).reduce((a, e) => a + e, 0) 178 | 179 | return { 180 | time: groups[key][0].time as UTCTimestamp, 181 | value: (lockVolume + unlockVolume) / 1e8, 182 | color: lockVolume > unlockVolume ? 'rgba(0, 150, 136, 0.8)' : 'rgba(255,82,82, 0.8)' 183 | } 184 | }) 185 | .sort((a, b) => a?.time - b?.time) 186 | 187 | const colors = { 188 | backgroundColor: 'black', 189 | lineColor: '#2962FF', 190 | textColor: 'white', 191 | areaTopColor: '#2962FF', 192 | areaBottomColor: 'rgba(41, 98, 255, 0.28)' 193 | } 194 | const chart = createChart(ref.current, { 195 | layout: { 196 | background: { type: ColorType.Solid, color: colors.backgroundColor }, 197 | textColor: colors.textColor 198 | }, 199 | grid: { 200 | vertLines: { 201 | visible: false 202 | }, 203 | horzLines: { 204 | visible: false 205 | } 206 | }, 207 | rightPriceScale: { 208 | borderVisible: false, 209 | textColor: 'black' 210 | }, 211 | timeScale: { 212 | borderVisible: false, 213 | timeVisible: true 214 | }, 215 | //localization: { 216 | //priceFormatter: (p) => { 217 | //return `${p} BSV` 218 | //} 219 | //}, 220 | 221 | width: ref.current.clientWidth, 222 | height: height 223 | }) 224 | const handleResize = () => { 225 | chart.applyOptions({ width: ref.current?.clientWidth || 0 }) 226 | } 227 | chart.timeScale().fitContent() 228 | 229 | // Locks 230 | const newSeries = chart.addAreaSeries({ 231 | lineColor: '#34D399', 232 | topColor: 'rgba(52, 211, 153, 0.9)', 233 | bottomColor: 'rgba(161, 255, 139, 0.04)' 234 | }) 235 | newSeries.setData(parsedData) 236 | //newSeries.setMarkers([ 237 | //{ 238 | //time: '2024-02-07', 239 | //position: 'inBar', 240 | //color: 'white', 241 | //shape: 'circle', 242 | //text: 'yours.org launch' 243 | //} 244 | //]) 245 | 246 | // Lock Volume 247 | const volumeSeries = chart.addHistogramSeries({ 248 | color: '#26a69a', 249 | priceFormat: { 250 | type: 'volume' 251 | }, 252 | priceScaleId: '' 253 | }) 254 | volumeSeries.setData(volumeData) 255 | 256 | window.addEventListener('resize', handleResize) 257 | 258 | return () => { 259 | window.removeEventListener('resize', handleResize) 260 | chart.remove() 261 | } 262 | }, [data, height, unlockData, selectedTab]) 263 | 264 | const renderTab = React.useCallback( 265 | (e: IntervalKey) => { 266 | return ( 267 |
setSelectedTab(e)} 277 | > 278 | {e} 279 |
280 | ) 281 | }, 282 | [selectedTab] 283 | ) 284 | 285 | return ( 286 |
287 |
288 |
289 |

290 | Locked BSV 291 |

292 |
293 |
294 |
295 | 296 |

297 | {formatNumber(tvl.toFixed(2))} 298 |

299 |
300 |

0, 303 | ['text-red-400']: percentChange < 0, 304 | ['text-white/60']: percentChange === 0 305 | })} 306 | > 307 | {ready ? ( 308 | <> 309 | {formatNumber(((dayAgoTvl * percentChange) / 100).toFixed(2))} ( 310 | {percentChange.toFixed(2)}%) 311 | 312 | ) : ( 313 | 'Updating…' 314 | )} 315 |

316 |
317 |
318 |

319 | ${formatNumber((tvl * exchangeRate).toFixed(2))} 320 |

321 |

0, 324 | ['text-red-400']: percentChange < 0, 325 | ['text-white/60']: percentChange === 0 326 | })} 327 | > 328 | {ready 329 | ? `$${formatNumber((((dayAgoTvl * percentChange) / 100) * exchangeRate).toFixed(2))}` 330 | : '—'} 331 |

332 |
333 |
334 |
335 |
336 |

337 | Share of circulating supply 338 |

339 |

340 | {ready ? ((tvl / totalCirculatingSupply) * 100).toFixed(2) : '—'}% 341 |

342 |

Measured against 19.6M BSV total supply.

343 |
344 |
{TABS.map(renderTab)}
345 |
346 |
347 |
348 | ) 349 | } 350 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | import Link from 'next/link' 5 | import Image from 'next/image' 6 | import { ArrowUpRight, Chrome, Network, Shield, Sparkles, Twitter } from 'lucide-react' 7 | import { GitHubLogoIcon, RocketIcon } from '@radix-ui/react-icons' 8 | import { GitbookIcon } from '@/components/icons/gitbook-icon' 9 | 10 | const statHighlights = [ 11 | { 12 | value: '2k+', 13 | label: 'Organic downloads', 14 | description: 'Trusted by thousands of builders to store keys and sign BSV transactions securely.' 15 | }, 16 | { 17 | value: 'Non-custodial', 18 | label: 'Key management', 19 | description: 'Private keys stay encrypted on device with deterministic derivation for every app.' 20 | }, 21 | { 22 | value: 'Provider API', 23 | label: 'GitBook docs', 24 | description: 'Deep integration guide for dApps, event subscriptions, ordinals, and MNEE USD.' 25 | } 26 | ] 27 | 28 | const resourceCards = [ 29 | { 30 | title: 'Provider API GitBook', 31 | description: 32 | 'Detect the Yours provider, request user permissions, and ship end-to-end web3 experiences with 1Sat Ordinals, BSV20s, and MNEE USD.', 33 | href: 'https://yours-wallet.gitbook.io/provider-api/', 34 | icon: Sparkles, 35 | ctaLabel: 'Open documentation' 36 | }, 37 | { 38 | title: 'React OnChain', 39 | description: 40 | 'World’s first React app deployed entirely on-chain. Explore immutable hosting, versioning, and how Yours Wallet signs on-chain interactions.', 41 | href: 'https://app.reactonchain.com/content/0fbc6c40adb34111cf8d90ef5e92a0260f6a0dd6b0fa803ba8066783a3ba58aa_0', 42 | icon: Network, 43 | ctaLabel: 'View deployment' 44 | }, 45 | { 46 | title: 'Panda Wallet Sample App', 47 | description: 48 | 'Cloneable starter that demonstrates provider detection, request flows, and transaction broadcasting in a production-ready UI.', 49 | href: 'https://panda-wallet-sample-app.vercel.app/', 50 | icon: RocketIcon, 51 | ctaLabel: 'Launch sample' 52 | } 53 | ] 54 | 55 | export default function Home() { 56 | const currentYear = new Date().getFullYear() 57 | 58 | return ( 59 | <> 60 |
61 |
65 |
66 |

67 | Powered by Yours 68 |

69 |

70 | Open source wallet infrastructure for BSV developers and users. 71 |

72 |

73 | Yours Wallet ships a non-custodial, programmable wallet with real-time lock analytics, deep 74 | documentation, and production tooling so builders can launch Bitcoin-centric experiences faster. 75 |

76 | 96 |
97 |
98 |
99 |
100 | Yours Wallet interface 108 |
109 |
110 |
111 | 112 |
116 |
117 | {statHighlights.map(({ value, label, description }) => ( 118 |
122 |

{value}

123 |

124 | {label} 125 |

126 |

{description}

127 |
128 | ))} 129 |
130 |
131 | 132 |
136 |
137 |

Builder toolkit

138 |

139 | End-to-end resources to launch and scale. 140 |

141 |

142 | Everything from documentation and on-chain deployments to live sample applications—curated to keep 143 | your roadmap moving. 144 |

145 |
146 | 147 |
148 | {resourceCards.map(({ title, description, href, icon: Icon, ctaLabel }) => ( 149 |
153 |
154 |
155 | 156 |
157 | 158 | Resource 159 | 160 |
161 |

{title}

162 |

{description}

163 | 169 | 170 | {ctaLabel} 171 | 172 |
173 | ))} 174 |
175 |
176 | 177 |
181 |
182 |

183 | Let’s ship the next thousand-wallet milestone together. 184 |

185 |

186 | Tell us where you’re headed and the Yours team will help map the wallet, analytics, and partnership 187 | motion to get you there faster. 188 |

189 | 195 | 196 | Follow us on Twitter 197 | 198 |
199 |
200 |
201 | 273 | 274 | ) 275 | } 276 | -------------------------------------------------------------------------------- /src/app/header.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | import Link from 'next/link' 5 | import Image from 'next/image' 6 | import { Chrome, Menu, Twitter, X } from 'lucide-react' 7 | import { GitHubLogoIcon } from '@radix-ui/react-icons' 8 | import { GitbookIcon } from '@/components/icons/gitbook-icon' 9 | import { usePathname } from 'next/navigation' 10 | import { ConnectWalletButton } from '@/components/ui/connect-button' 11 | import { useWallet } from '@/components/layout' 12 | 13 | const navLinks = [ 14 | { href: '#overview', label: 'Overview' }, 15 | { href: '#stats', label: 'Network' }, 16 | { href: '#toolkit', label: 'Tooling' }, 17 | { href: '#ecosystem', label: 'Ecosystem' } 18 | ] 19 | 20 | export default function Header() { 21 | const pathname = usePathname() 22 | const [isScrolled, setIsScrolled] = React.useState(false) 23 | const [isMenuOpen, setIsMenuOpen] = React.useState(false) 24 | const { isConnected, address, user, disconnect } = useWallet() 25 | const [hasWalletProvider, setHasWalletProvider] = React.useState(false) 26 | 27 | React.useEffect(() => { 28 | const handleScroll = () => { 29 | setIsScrolled(window.scrollY > 10) 30 | } 31 | 32 | handleScroll() 33 | window.addEventListener('scroll', handleScroll) 34 | return () => window.removeEventListener('scroll', handleScroll) 35 | }, []) 36 | 37 | React.useEffect(() => { 38 | if (typeof document === 'undefined') return 39 | 40 | if (!isMenuOpen) { 41 | document.body.style.overflow = '' 42 | return 43 | } 44 | 45 | const previousOverflow = document.body.style.overflow 46 | document.body.style.overflow = 'hidden' 47 | 48 | return () => { 49 | document.body.style.overflow = previousOverflow 50 | } 51 | }, [isMenuOpen]) 52 | 53 | React.useEffect(() => { 54 | const handleKeyDown = (event: KeyboardEvent) => { 55 | if (event.key === 'Escape') { 56 | setIsMenuOpen(false) 57 | } 58 | } 59 | 60 | window.addEventListener('keydown', handleKeyDown) 61 | return () => window.removeEventListener('keydown', handleKeyDown) 62 | }, []) 63 | 64 | React.useEffect(() => { 65 | if (typeof window === 'undefined') return 66 | 67 | const updateAvailability = () => { 68 | setHasWalletProvider(Boolean(window.yours)) 69 | } 70 | 71 | updateAvailability() 72 | window.addEventListener('yours#initialized', updateAvailability) 73 | return () => window.removeEventListener('yours#initialized', updateAvailability) 74 | }, []) 75 | 76 | const handleNavClick = (event: React.MouseEvent, href: string) => { 77 | event.preventDefault() 78 | 79 | const targetElement = document.querySelector(href) as HTMLElement | null 80 | if (targetElement) { 81 | const headerOffset = 72 82 | const elementPosition = targetElement.getBoundingClientRect().top + window.scrollY 83 | const offsetPosition = elementPosition - headerOffset 84 | 85 | window.scrollTo({ 86 | top: offsetPosition >= 0 ? offsetPosition : 0, 87 | behavior: 'smooth' 88 | }) 89 | } 90 | 91 | setIsMenuOpen(false) 92 | } 93 | 94 | if (pathname?.startsWith('/social')) { 95 | return null 96 | } 97 | 98 | const formattedAddress = React.useMemo(() => { 99 | if (!address) return 'Wallet Connected' 100 | return `${address.slice(0, 6)}...${address.slice(-4)}` 101 | }, [address]) 102 | 103 | const renderDesktopWalletCTA = () => { 104 | if (!hasWalletProvider) return null 105 | 106 | if (isConnected) { 107 | return ( 108 | 126 | ) 127 | } 128 | 129 | return ( 130 | 134 | ) 135 | } 136 | 137 | const renderMobileWalletCTA = () => { 138 | if (!hasWalletProvider) return null 139 | 140 | if (isConnected) { 141 | return ( 142 | 161 | ) 162 | } 163 | 164 | return ( 165 | setIsMenuOpen(false)} 168 | className="w-full justify-center border border-white/20 bg-white/10 px-4 py-2 text-sm font-semibold text-white transition hover:border-white/40 hover:bg-white/20" 169 | /> 170 | ) 171 | } 172 | 173 | return ( 174 | <> 175 |
182 |
183 | 184 | Yours Wallet 192 | 193 | 194 | 206 | 207 |
208 |
209 | 216 | 217 | 218 | 225 | 226 | 227 | 234 | 235 | 236 | 242 | 243 | Download Extension 244 | 245 | {renderDesktopWalletCTA()} 246 |
247 | 256 |
257 |
258 |
259 | {isMenuOpen && ( 260 |
261 |
setIsMenuOpen(false)} 264 | /> 265 |
269 |
270 | 299 | 307 |
308 | 320 |
321 | {renderMobileWalletCTA()} 322 | 328 | 329 | Download Extension 330 | 331 |
332 |
333 |
334 | )} 335 | 336 | ) 337 | } 338 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@radix-ui/react-icons': 12 | specifier: ^1.3.0 13 | version: 1.3.2(react@18.3.1) 14 | axios: 15 | specifier: ^1.13.2 16 | version: 1.13.2 17 | classnames: 18 | specifier: ^2.5.1 19 | version: 2.5.1 20 | framer-motion: 21 | specifier: ^12.23.24 22 | version: 12.23.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 23 | lightweight-charts: 24 | specifier: ^4.1.3 25 | version: 4.2.3 26 | lucide-react: 27 | specifier: ^0.468.0 28 | version: 0.468.0(react@18.3.1) 29 | next: 30 | specifier: 16.0.9 31 | version: 16.0.9(babel-plugin-react-compiler@1.0.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) 32 | number-abbreviate: 33 | specifier: ^2.0.0 34 | version: 2.0.0 35 | prettier: 36 | specifier: ^2.8.8 37 | version: 2.8.8 38 | react: 39 | specifier: ^18 40 | version: 18.3.1 41 | react-dom: 42 | specifier: ^18 43 | version: 18.3.1(react@18.3.1) 44 | swr: 45 | specifier: ^2.3.6 46 | version: 2.3.6(react@18.3.1) 47 | devDependencies: 48 | '@types/node': 49 | specifier: ^20 50 | version: 20.19.24 51 | '@types/react': 52 | specifier: ^18 53 | version: 18.3.26 54 | '@types/react-dom': 55 | specifier: ^18 56 | version: 18.3.7(@types/react@18.3.26) 57 | autoprefixer: 58 | specifier: ^10.0.1 59 | version: 10.4.22(postcss@8.5.6) 60 | babel-plugin-react-compiler: 61 | specifier: ^1.0.0 62 | version: 1.0.0 63 | postcss: 64 | specifier: ^8 65 | version: 8.5.6 66 | tailwindcss: 67 | specifier: ^3.3.0 68 | version: 3.4.18 69 | typescript: 70 | specifier: ^5 71 | version: 5.9.3 72 | 73 | packages: 74 | 75 | '@alloc/quick-lru@5.2.0': 76 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 77 | engines: {node: '>=10'} 78 | 79 | '@babel/helper-string-parser@7.27.1': 80 | resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 81 | engines: {node: '>=6.9.0'} 82 | 83 | '@babel/helper-validator-identifier@7.28.5': 84 | resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 85 | engines: {node: '>=6.9.0'} 86 | 87 | '@babel/types@7.28.5': 88 | resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} 89 | engines: {node: '>=6.9.0'} 90 | 91 | '@emnapi/runtime@1.7.0': 92 | resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} 93 | 94 | '@img/colour@1.0.0': 95 | resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} 96 | engines: {node: '>=18'} 97 | 98 | '@img/sharp-darwin-arm64@0.34.5': 99 | resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} 100 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 101 | cpu: [arm64] 102 | os: [darwin] 103 | 104 | '@img/sharp-darwin-x64@0.34.5': 105 | resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} 106 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 107 | cpu: [x64] 108 | os: [darwin] 109 | 110 | '@img/sharp-libvips-darwin-arm64@1.2.4': 111 | resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} 112 | cpu: [arm64] 113 | os: [darwin] 114 | 115 | '@img/sharp-libvips-darwin-x64@1.2.4': 116 | resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} 117 | cpu: [x64] 118 | os: [darwin] 119 | 120 | '@img/sharp-libvips-linux-arm64@1.2.4': 121 | resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} 122 | cpu: [arm64] 123 | os: [linux] 124 | 125 | '@img/sharp-libvips-linux-arm@1.2.4': 126 | resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} 127 | cpu: [arm] 128 | os: [linux] 129 | 130 | '@img/sharp-libvips-linux-ppc64@1.2.4': 131 | resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} 132 | cpu: [ppc64] 133 | os: [linux] 134 | 135 | '@img/sharp-libvips-linux-riscv64@1.2.4': 136 | resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} 137 | cpu: [riscv64] 138 | os: [linux] 139 | 140 | '@img/sharp-libvips-linux-s390x@1.2.4': 141 | resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} 142 | cpu: [s390x] 143 | os: [linux] 144 | 145 | '@img/sharp-libvips-linux-x64@1.2.4': 146 | resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} 147 | cpu: [x64] 148 | os: [linux] 149 | 150 | '@img/sharp-libvips-linuxmusl-arm64@1.2.4': 151 | resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} 152 | cpu: [arm64] 153 | os: [linux] 154 | 155 | '@img/sharp-libvips-linuxmusl-x64@1.2.4': 156 | resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} 157 | cpu: [x64] 158 | os: [linux] 159 | 160 | '@img/sharp-linux-arm64@0.34.5': 161 | resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} 162 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 163 | cpu: [arm64] 164 | os: [linux] 165 | 166 | '@img/sharp-linux-arm@0.34.5': 167 | resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} 168 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 169 | cpu: [arm] 170 | os: [linux] 171 | 172 | '@img/sharp-linux-ppc64@0.34.5': 173 | resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} 174 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 175 | cpu: [ppc64] 176 | os: [linux] 177 | 178 | '@img/sharp-linux-riscv64@0.34.5': 179 | resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} 180 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 181 | cpu: [riscv64] 182 | os: [linux] 183 | 184 | '@img/sharp-linux-s390x@0.34.5': 185 | resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} 186 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 187 | cpu: [s390x] 188 | os: [linux] 189 | 190 | '@img/sharp-linux-x64@0.34.5': 191 | resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} 192 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 193 | cpu: [x64] 194 | os: [linux] 195 | 196 | '@img/sharp-linuxmusl-arm64@0.34.5': 197 | resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} 198 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 199 | cpu: [arm64] 200 | os: [linux] 201 | 202 | '@img/sharp-linuxmusl-x64@0.34.5': 203 | resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} 204 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 205 | cpu: [x64] 206 | os: [linux] 207 | 208 | '@img/sharp-wasm32@0.34.5': 209 | resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} 210 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 211 | cpu: [wasm32] 212 | 213 | '@img/sharp-win32-arm64@0.34.5': 214 | resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} 215 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 216 | cpu: [arm64] 217 | os: [win32] 218 | 219 | '@img/sharp-win32-ia32@0.34.5': 220 | resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} 221 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 222 | cpu: [ia32] 223 | os: [win32] 224 | 225 | '@img/sharp-win32-x64@0.34.5': 226 | resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} 227 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 228 | cpu: [x64] 229 | os: [win32] 230 | 231 | '@isaacs/cliui@8.0.2': 232 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 233 | engines: {node: '>=12'} 234 | 235 | '@jridgewell/gen-mapping@0.3.13': 236 | resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} 237 | 238 | '@jridgewell/resolve-uri@3.1.2': 239 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 240 | engines: {node: '>=6.0.0'} 241 | 242 | '@jridgewell/sourcemap-codec@1.5.5': 243 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} 244 | 245 | '@jridgewell/trace-mapping@0.3.31': 246 | resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 247 | 248 | '@next/env@16.0.9': 249 | resolution: {integrity: sha512-6284pl8c8n9PQidN63qjPVEu1uXXKjnmbmaLebOzIfTrSXdGiAPsIMRi4pk/+v/ezqweE1/B8bFqiAAfC6lMXg==} 250 | 251 | '@next/swc-darwin-arm64@16.0.9': 252 | resolution: {integrity: sha512-j06fWg/gPqiWjK+sEpCDsh5gX+Bdy9gnPYjFqMBvBEOIcCFy1/ecF6pY6XAce7WyCJAbBPVb+6GvpmUZKNq0oQ==} 253 | engines: {node: '>= 10'} 254 | cpu: [arm64] 255 | os: [darwin] 256 | 257 | '@next/swc-darwin-x64@16.0.9': 258 | resolution: {integrity: sha512-FRYYz5GSKUkfvDSjd5hgHME2LgYjfOLBmhRVltbs3oRNQQf9n5UTQMmIu/u5vpkjJFV4L2tqo8duGqDxdQOFwg==} 259 | engines: {node: '>= 10'} 260 | cpu: [x64] 261 | os: [darwin] 262 | 263 | '@next/swc-linux-arm64-gnu@16.0.9': 264 | resolution: {integrity: sha512-EI2klFVL8tOyEIX5J1gXXpm1YuChmDy4R+tHoNjkCHUmBJqXioYErX/O2go4pEhjxkAxHp2i8y5aJcRz2m5NqQ==} 265 | engines: {node: '>= 10'} 266 | cpu: [arm64] 267 | os: [linux] 268 | 269 | '@next/swc-linux-arm64-musl@16.0.9': 270 | resolution: {integrity: sha512-vq/5HeGvowhDPMrpp/KP4GjPVhIXnwNeDPF5D6XK6ta96UIt+C0HwJwuHYlwmn0SWyNANqx1Mp6qSVDXwbFKsw==} 271 | engines: {node: '>= 10'} 272 | cpu: [arm64] 273 | os: [linux] 274 | 275 | '@next/swc-linux-x64-gnu@16.0.9': 276 | resolution: {integrity: sha512-GlUdJwy2leA/HnyRYxJ1ZJLCJH+BxZfqV4E0iYLrJipDKxWejWpPtZUdccPmCfIEY9gNBO7bPfbG6IIgkt0qXg==} 277 | engines: {node: '>= 10'} 278 | cpu: [x64] 279 | os: [linux] 280 | 281 | '@next/swc-linux-x64-musl@16.0.9': 282 | resolution: {integrity: sha512-UCtOVx4N8AHF434VPwg4L0KkFLAd7pgJShzlX/hhv9+FDrT7/xCuVdlBsCXH7l9yCA/wHl3OqhMbIkgUluriWA==} 283 | engines: {node: '>= 10'} 284 | cpu: [x64] 285 | os: [linux] 286 | 287 | '@next/swc-win32-arm64-msvc@16.0.9': 288 | resolution: {integrity: sha512-tQjtDGtv63mV3n/cZ4TH8BgUvKTSFlrF06yT5DyRmgQuj5WEjBUDy0W3myIW5kTRYMPrLn42H3VfCNwBH6YYiA==} 289 | engines: {node: '>= 10'} 290 | cpu: [arm64] 291 | os: [win32] 292 | 293 | '@next/swc-win32-x64-msvc@16.0.9': 294 | resolution: {integrity: sha512-y9AGACHTBwnWFLq5B5Fiv3FEbXBusdPb60pgoerB04CV/pwjY1xQNdoTNxAv7eUhU2k1CKnkN4XWVuiK07uOqA==} 295 | engines: {node: '>= 10'} 296 | cpu: [x64] 297 | os: [win32] 298 | 299 | '@nodelib/fs.scandir@2.1.5': 300 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 301 | engines: {node: '>= 8'} 302 | 303 | '@nodelib/fs.stat@2.0.5': 304 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 305 | engines: {node: '>= 8'} 306 | 307 | '@nodelib/fs.walk@1.2.8': 308 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 309 | engines: {node: '>= 8'} 310 | 311 | '@pkgjs/parseargs@0.11.0': 312 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 313 | engines: {node: '>=14'} 314 | 315 | '@radix-ui/react-icons@1.3.2': 316 | resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} 317 | peerDependencies: 318 | react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc 319 | 320 | '@swc/helpers@0.5.15': 321 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} 322 | 323 | '@types/node@20.19.24': 324 | resolution: {integrity: sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==} 325 | 326 | '@types/prop-types@15.7.15': 327 | resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} 328 | 329 | '@types/react-dom@18.3.7': 330 | resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} 331 | peerDependencies: 332 | '@types/react': ^18.0.0 333 | 334 | '@types/react@18.3.26': 335 | resolution: {integrity: sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==} 336 | 337 | ansi-regex@5.0.1: 338 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 339 | engines: {node: '>=8'} 340 | 341 | ansi-regex@6.2.2: 342 | resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} 343 | engines: {node: '>=12'} 344 | 345 | ansi-styles@4.3.0: 346 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 347 | engines: {node: '>=8'} 348 | 349 | ansi-styles@6.2.3: 350 | resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} 351 | engines: {node: '>=12'} 352 | 353 | any-promise@1.3.0: 354 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 355 | 356 | anymatch@3.1.3: 357 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 358 | engines: {node: '>= 8'} 359 | 360 | arg@5.0.2: 361 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 362 | 363 | asynckit@0.4.0: 364 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 365 | 366 | autoprefixer@10.4.22: 367 | resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} 368 | engines: {node: ^10 || ^12 || >=14} 369 | hasBin: true 370 | peerDependencies: 371 | postcss: ^8.1.0 372 | 373 | axios@1.13.2: 374 | resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} 375 | 376 | babel-plugin-react-compiler@1.0.0: 377 | resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} 378 | 379 | balanced-match@1.0.2: 380 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 381 | 382 | baseline-browser-mapping@2.8.26: 383 | resolution: {integrity: sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q==} 384 | hasBin: true 385 | 386 | binary-extensions@2.3.0: 387 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 388 | engines: {node: '>=8'} 389 | 390 | brace-expansion@2.0.2: 391 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 392 | 393 | braces@3.0.3: 394 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 395 | engines: {node: '>=8'} 396 | 397 | browserslist@4.28.0: 398 | resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} 399 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 400 | hasBin: true 401 | 402 | call-bind-apply-helpers@1.0.2: 403 | resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 404 | engines: {node: '>= 0.4'} 405 | 406 | camelcase-css@2.0.1: 407 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 408 | engines: {node: '>= 6'} 409 | 410 | caniuse-lite@1.0.30001754: 411 | resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} 412 | 413 | chokidar@3.6.0: 414 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 415 | engines: {node: '>= 8.10.0'} 416 | 417 | classnames@2.5.1: 418 | resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} 419 | 420 | client-only@0.0.1: 421 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 422 | 423 | color-convert@2.0.1: 424 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 425 | engines: {node: '>=7.0.0'} 426 | 427 | color-name@1.1.4: 428 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 429 | 430 | combined-stream@1.0.8: 431 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 432 | engines: {node: '>= 0.8'} 433 | 434 | commander@4.1.1: 435 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 436 | engines: {node: '>= 6'} 437 | 438 | cross-spawn@7.0.6: 439 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 440 | engines: {node: '>= 8'} 441 | 442 | cssesc@3.0.0: 443 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 444 | engines: {node: '>=4'} 445 | hasBin: true 446 | 447 | csstype@3.1.3: 448 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 449 | 450 | delayed-stream@1.0.0: 451 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 452 | engines: {node: '>=0.4.0'} 453 | 454 | dequal@2.0.3: 455 | resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} 456 | engines: {node: '>=6'} 457 | 458 | detect-libc@2.1.2: 459 | resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 460 | engines: {node: '>=8'} 461 | 462 | didyoumean@1.2.2: 463 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 464 | 465 | dlv@1.1.3: 466 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 467 | 468 | dunder-proto@1.0.1: 469 | resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 470 | engines: {node: '>= 0.4'} 471 | 472 | eastasianwidth@0.2.0: 473 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 474 | 475 | electron-to-chromium@1.5.250: 476 | resolution: {integrity: sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==} 477 | 478 | emoji-regex@8.0.0: 479 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 480 | 481 | emoji-regex@9.2.2: 482 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 483 | 484 | es-define-property@1.0.1: 485 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 486 | engines: {node: '>= 0.4'} 487 | 488 | es-errors@1.3.0: 489 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 490 | engines: {node: '>= 0.4'} 491 | 492 | es-object-atoms@1.1.1: 493 | resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 494 | engines: {node: '>= 0.4'} 495 | 496 | es-set-tostringtag@2.1.0: 497 | resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} 498 | engines: {node: '>= 0.4'} 499 | 500 | escalade@3.2.0: 501 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 502 | engines: {node: '>=6'} 503 | 504 | fancy-canvas@2.1.0: 505 | resolution: {integrity: sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==} 506 | 507 | fast-glob@3.3.3: 508 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 509 | engines: {node: '>=8.6.0'} 510 | 511 | fastq@1.19.1: 512 | resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 513 | 514 | fill-range@7.1.1: 515 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 516 | engines: {node: '>=8'} 517 | 518 | follow-redirects@1.15.11: 519 | resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} 520 | engines: {node: '>=4.0'} 521 | peerDependencies: 522 | debug: '*' 523 | peerDependenciesMeta: 524 | debug: 525 | optional: true 526 | 527 | foreground-child@3.3.1: 528 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 529 | engines: {node: '>=14'} 530 | 531 | form-data@4.0.4: 532 | resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} 533 | engines: {node: '>= 6'} 534 | 535 | fraction.js@5.3.4: 536 | resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} 537 | 538 | framer-motion@12.23.24: 539 | resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} 540 | peerDependencies: 541 | '@emotion/is-prop-valid': '*' 542 | react: ^18.0.0 || ^19.0.0 543 | react-dom: ^18.0.0 || ^19.0.0 544 | peerDependenciesMeta: 545 | '@emotion/is-prop-valid': 546 | optional: true 547 | react: 548 | optional: true 549 | react-dom: 550 | optional: true 551 | 552 | fsevents@2.3.3: 553 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 554 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 555 | os: [darwin] 556 | 557 | function-bind@1.1.2: 558 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 559 | 560 | get-intrinsic@1.3.0: 561 | resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 562 | engines: {node: '>= 0.4'} 563 | 564 | get-proto@1.0.1: 565 | resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 566 | engines: {node: '>= 0.4'} 567 | 568 | glob-parent@5.1.2: 569 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 570 | engines: {node: '>= 6'} 571 | 572 | glob-parent@6.0.2: 573 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 574 | engines: {node: '>=10.13.0'} 575 | 576 | glob@10.4.5: 577 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 578 | hasBin: true 579 | 580 | gopd@1.2.0: 581 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 582 | engines: {node: '>= 0.4'} 583 | 584 | has-symbols@1.1.0: 585 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 586 | engines: {node: '>= 0.4'} 587 | 588 | has-tostringtag@1.0.2: 589 | resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 590 | engines: {node: '>= 0.4'} 591 | 592 | hasown@2.0.2: 593 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 594 | engines: {node: '>= 0.4'} 595 | 596 | is-binary-path@2.1.0: 597 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 598 | engines: {node: '>=8'} 599 | 600 | is-core-module@2.16.1: 601 | resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 602 | engines: {node: '>= 0.4'} 603 | 604 | is-extglob@2.1.1: 605 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 606 | engines: {node: '>=0.10.0'} 607 | 608 | is-fullwidth-code-point@3.0.0: 609 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 610 | engines: {node: '>=8'} 611 | 612 | is-glob@4.0.3: 613 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 614 | engines: {node: '>=0.10.0'} 615 | 616 | is-number@7.0.0: 617 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 618 | engines: {node: '>=0.12.0'} 619 | 620 | isexe@2.0.0: 621 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 622 | 623 | jackspeak@3.4.3: 624 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 625 | 626 | jiti@1.21.7: 627 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} 628 | hasBin: true 629 | 630 | js-tokens@4.0.0: 631 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 632 | 633 | lightweight-charts@4.2.3: 634 | resolution: {integrity: sha512-5kS/2hY3wNYNzhnS8Gb+GAS07DX8GPF2YVDnd2NMC85gJVQ6RLU6YrXNgNJ6eg0AnWPwCnvaGtYmGky3HiLQEw==} 635 | 636 | lilconfig@3.1.3: 637 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 638 | engines: {node: '>=14'} 639 | 640 | lines-and-columns@1.2.4: 641 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 642 | 643 | loose-envify@1.4.0: 644 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} 645 | hasBin: true 646 | 647 | lru-cache@10.4.3: 648 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 649 | 650 | lucide-react@0.468.0: 651 | resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==} 652 | peerDependencies: 653 | react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc 654 | 655 | math-intrinsics@1.1.0: 656 | resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 657 | engines: {node: '>= 0.4'} 658 | 659 | merge2@1.4.1: 660 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 661 | engines: {node: '>= 8'} 662 | 663 | micromatch@4.0.8: 664 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 665 | engines: {node: '>=8.6'} 666 | 667 | mime-db@1.52.0: 668 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 669 | engines: {node: '>= 0.6'} 670 | 671 | mime-types@2.1.35: 672 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 673 | engines: {node: '>= 0.6'} 674 | 675 | minimatch@9.0.5: 676 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 677 | engines: {node: '>=16 || 14 >=14.17'} 678 | 679 | minipass@7.1.2: 680 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 681 | engines: {node: '>=16 || 14 >=14.17'} 682 | 683 | motion-dom@12.23.23: 684 | resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} 685 | 686 | motion-utils@12.23.6: 687 | resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} 688 | 689 | mz@2.7.0: 690 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 691 | 692 | nanoid@3.3.11: 693 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 694 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 695 | hasBin: true 696 | 697 | next@16.0.9: 698 | resolution: {integrity: sha512-Xk5x/wEk6ADIAtQECLo1uyE5OagbQCiZ+gW4XEv24FjQ3O2PdSkvgsn22aaseSXC7xg84oONvQjFbSTX5YsMhQ==} 699 | engines: {node: '>=20.9.0'} 700 | hasBin: true 701 | peerDependencies: 702 | '@opentelemetry/api': ^1.1.0 703 | '@playwright/test': ^1.51.1 704 | babel-plugin-react-compiler: '*' 705 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 706 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 707 | sass: ^1.3.0 708 | peerDependenciesMeta: 709 | '@opentelemetry/api': 710 | optional: true 711 | '@playwright/test': 712 | optional: true 713 | babel-plugin-react-compiler: 714 | optional: true 715 | sass: 716 | optional: true 717 | 718 | node-releases@2.0.27: 719 | resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} 720 | 721 | normalize-path@3.0.0: 722 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 723 | engines: {node: '>=0.10.0'} 724 | 725 | normalize-range@0.1.2: 726 | resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} 727 | engines: {node: '>=0.10.0'} 728 | 729 | number-abbreviate@2.0.0: 730 | resolution: {integrity: sha512-A28RCggQAd0GPafgnijus1IV48TD8lbVt/J6xzkhcfk7jvXbWbh5aTaQsMjZjWAJ6LK6EoGf306RzJsdu12OqQ==} 731 | 732 | object-assign@4.1.1: 733 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 734 | engines: {node: '>=0.10.0'} 735 | 736 | object-hash@3.0.0: 737 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 738 | engines: {node: '>= 6'} 739 | 740 | package-json-from-dist@1.0.1: 741 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 742 | 743 | path-key@3.1.1: 744 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 745 | engines: {node: '>=8'} 746 | 747 | path-parse@1.0.7: 748 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 749 | 750 | path-scurry@1.11.1: 751 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 752 | engines: {node: '>=16 || 14 >=14.18'} 753 | 754 | picocolors@1.1.1: 755 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 756 | 757 | picomatch@2.3.1: 758 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 759 | engines: {node: '>=8.6'} 760 | 761 | pify@2.3.0: 762 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 763 | engines: {node: '>=0.10.0'} 764 | 765 | pirates@4.0.7: 766 | resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} 767 | engines: {node: '>= 6'} 768 | 769 | postcss-import@15.1.0: 770 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} 771 | engines: {node: '>=14.0.0'} 772 | peerDependencies: 773 | postcss: ^8.0.0 774 | 775 | postcss-js@4.1.0: 776 | resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} 777 | engines: {node: ^12 || ^14 || >= 16} 778 | peerDependencies: 779 | postcss: ^8.4.21 780 | 781 | postcss-load-config@6.0.1: 782 | resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} 783 | engines: {node: '>= 18'} 784 | peerDependencies: 785 | jiti: '>=1.21.0' 786 | postcss: '>=8.0.9' 787 | tsx: ^4.8.1 788 | yaml: ^2.4.2 789 | peerDependenciesMeta: 790 | jiti: 791 | optional: true 792 | postcss: 793 | optional: true 794 | tsx: 795 | optional: true 796 | yaml: 797 | optional: true 798 | 799 | postcss-nested@6.2.0: 800 | resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} 801 | engines: {node: '>=12.0'} 802 | peerDependencies: 803 | postcss: ^8.2.14 804 | 805 | postcss-selector-parser@6.1.2: 806 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} 807 | engines: {node: '>=4'} 808 | 809 | postcss-value-parser@4.2.0: 810 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 811 | 812 | postcss@8.4.31: 813 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 814 | engines: {node: ^10 || ^12 || >=14} 815 | 816 | postcss@8.5.6: 817 | resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} 818 | engines: {node: ^10 || ^12 || >=14} 819 | 820 | prettier@2.8.8: 821 | resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} 822 | engines: {node: '>=10.13.0'} 823 | hasBin: true 824 | 825 | proxy-from-env@1.1.0: 826 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} 827 | 828 | queue-microtask@1.2.3: 829 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 830 | 831 | react-dom@18.3.1: 832 | resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 833 | peerDependencies: 834 | react: ^18.3.1 835 | 836 | react@18.3.1: 837 | resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} 838 | engines: {node: '>=0.10.0'} 839 | 840 | read-cache@1.0.0: 841 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} 842 | 843 | readdirp@3.6.0: 844 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 845 | engines: {node: '>=8.10.0'} 846 | 847 | resolve@1.22.11: 848 | resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} 849 | engines: {node: '>= 0.4'} 850 | hasBin: true 851 | 852 | reusify@1.1.0: 853 | resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 854 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 855 | 856 | run-parallel@1.2.0: 857 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 858 | 859 | scheduler@0.23.2: 860 | resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} 861 | 862 | semver@7.7.3: 863 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 864 | engines: {node: '>=10'} 865 | hasBin: true 866 | 867 | sharp@0.34.5: 868 | resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} 869 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 870 | 871 | shebang-command@2.0.0: 872 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 873 | engines: {node: '>=8'} 874 | 875 | shebang-regex@3.0.0: 876 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 877 | engines: {node: '>=8'} 878 | 879 | signal-exit@4.1.0: 880 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 881 | engines: {node: '>=14'} 882 | 883 | source-map-js@1.2.1: 884 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 885 | engines: {node: '>=0.10.0'} 886 | 887 | string-width@4.2.3: 888 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 889 | engines: {node: '>=8'} 890 | 891 | string-width@5.1.2: 892 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 893 | engines: {node: '>=12'} 894 | 895 | strip-ansi@6.0.1: 896 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 897 | engines: {node: '>=8'} 898 | 899 | strip-ansi@7.1.2: 900 | resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} 901 | engines: {node: '>=12'} 902 | 903 | styled-jsx@5.1.6: 904 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 905 | engines: {node: '>= 12.0.0'} 906 | peerDependencies: 907 | '@babel/core': '*' 908 | babel-plugin-macros: '*' 909 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 910 | peerDependenciesMeta: 911 | '@babel/core': 912 | optional: true 913 | babel-plugin-macros: 914 | optional: true 915 | 916 | sucrase@3.35.0: 917 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 918 | engines: {node: '>=16 || 14 >=14.17'} 919 | hasBin: true 920 | 921 | supports-preserve-symlinks-flag@1.0.0: 922 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 923 | engines: {node: '>= 0.4'} 924 | 925 | swr@2.3.6: 926 | resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==} 927 | peerDependencies: 928 | react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 929 | 930 | tailwindcss@3.4.18: 931 | resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} 932 | engines: {node: '>=14.0.0'} 933 | hasBin: true 934 | 935 | thenify-all@1.6.0: 936 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 937 | engines: {node: '>=0.8'} 938 | 939 | thenify@3.3.1: 940 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 941 | 942 | to-regex-range@5.0.1: 943 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 944 | engines: {node: '>=8.0'} 945 | 946 | ts-interface-checker@0.1.13: 947 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 948 | 949 | tslib@2.8.1: 950 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 951 | 952 | typescript@5.9.3: 953 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 954 | engines: {node: '>=14.17'} 955 | hasBin: true 956 | 957 | undici-types@6.21.0: 958 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 959 | 960 | update-browserslist-db@1.1.4: 961 | resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} 962 | hasBin: true 963 | peerDependencies: 964 | browserslist: '>= 4.21.0' 965 | 966 | use-sync-external-store@1.6.0: 967 | resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} 968 | peerDependencies: 969 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 970 | 971 | util-deprecate@1.0.2: 972 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 973 | 974 | which@2.0.2: 975 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 976 | engines: {node: '>= 8'} 977 | hasBin: true 978 | 979 | wrap-ansi@7.0.0: 980 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 981 | engines: {node: '>=10'} 982 | 983 | wrap-ansi@8.1.0: 984 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 985 | engines: {node: '>=12'} 986 | 987 | snapshots: 988 | 989 | '@alloc/quick-lru@5.2.0': {} 990 | 991 | '@babel/helper-string-parser@7.27.1': {} 992 | 993 | '@babel/helper-validator-identifier@7.28.5': {} 994 | 995 | '@babel/types@7.28.5': 996 | dependencies: 997 | '@babel/helper-string-parser': 7.27.1 998 | '@babel/helper-validator-identifier': 7.28.5 999 | 1000 | '@emnapi/runtime@1.7.0': 1001 | dependencies: 1002 | tslib: 2.8.1 1003 | optional: true 1004 | 1005 | '@img/colour@1.0.0': 1006 | optional: true 1007 | 1008 | '@img/sharp-darwin-arm64@0.34.5': 1009 | optionalDependencies: 1010 | '@img/sharp-libvips-darwin-arm64': 1.2.4 1011 | optional: true 1012 | 1013 | '@img/sharp-darwin-x64@0.34.5': 1014 | optionalDependencies: 1015 | '@img/sharp-libvips-darwin-x64': 1.2.4 1016 | optional: true 1017 | 1018 | '@img/sharp-libvips-darwin-arm64@1.2.4': 1019 | optional: true 1020 | 1021 | '@img/sharp-libvips-darwin-x64@1.2.4': 1022 | optional: true 1023 | 1024 | '@img/sharp-libvips-linux-arm64@1.2.4': 1025 | optional: true 1026 | 1027 | '@img/sharp-libvips-linux-arm@1.2.4': 1028 | optional: true 1029 | 1030 | '@img/sharp-libvips-linux-ppc64@1.2.4': 1031 | optional: true 1032 | 1033 | '@img/sharp-libvips-linux-riscv64@1.2.4': 1034 | optional: true 1035 | 1036 | '@img/sharp-libvips-linux-s390x@1.2.4': 1037 | optional: true 1038 | 1039 | '@img/sharp-libvips-linux-x64@1.2.4': 1040 | optional: true 1041 | 1042 | '@img/sharp-libvips-linuxmusl-arm64@1.2.4': 1043 | optional: true 1044 | 1045 | '@img/sharp-libvips-linuxmusl-x64@1.2.4': 1046 | optional: true 1047 | 1048 | '@img/sharp-linux-arm64@0.34.5': 1049 | optionalDependencies: 1050 | '@img/sharp-libvips-linux-arm64': 1.2.4 1051 | optional: true 1052 | 1053 | '@img/sharp-linux-arm@0.34.5': 1054 | optionalDependencies: 1055 | '@img/sharp-libvips-linux-arm': 1.2.4 1056 | optional: true 1057 | 1058 | '@img/sharp-linux-ppc64@0.34.5': 1059 | optionalDependencies: 1060 | '@img/sharp-libvips-linux-ppc64': 1.2.4 1061 | optional: true 1062 | 1063 | '@img/sharp-linux-riscv64@0.34.5': 1064 | optionalDependencies: 1065 | '@img/sharp-libvips-linux-riscv64': 1.2.4 1066 | optional: true 1067 | 1068 | '@img/sharp-linux-s390x@0.34.5': 1069 | optionalDependencies: 1070 | '@img/sharp-libvips-linux-s390x': 1.2.4 1071 | optional: true 1072 | 1073 | '@img/sharp-linux-x64@0.34.5': 1074 | optionalDependencies: 1075 | '@img/sharp-libvips-linux-x64': 1.2.4 1076 | optional: true 1077 | 1078 | '@img/sharp-linuxmusl-arm64@0.34.5': 1079 | optionalDependencies: 1080 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 1081 | optional: true 1082 | 1083 | '@img/sharp-linuxmusl-x64@0.34.5': 1084 | optionalDependencies: 1085 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4 1086 | optional: true 1087 | 1088 | '@img/sharp-wasm32@0.34.5': 1089 | dependencies: 1090 | '@emnapi/runtime': 1.7.0 1091 | optional: true 1092 | 1093 | '@img/sharp-win32-arm64@0.34.5': 1094 | optional: true 1095 | 1096 | '@img/sharp-win32-ia32@0.34.5': 1097 | optional: true 1098 | 1099 | '@img/sharp-win32-x64@0.34.5': 1100 | optional: true 1101 | 1102 | '@isaacs/cliui@8.0.2': 1103 | dependencies: 1104 | string-width: 5.1.2 1105 | string-width-cjs: string-width@4.2.3 1106 | strip-ansi: 7.1.2 1107 | strip-ansi-cjs: strip-ansi@6.0.1 1108 | wrap-ansi: 8.1.0 1109 | wrap-ansi-cjs: wrap-ansi@7.0.0 1110 | 1111 | '@jridgewell/gen-mapping@0.3.13': 1112 | dependencies: 1113 | '@jridgewell/sourcemap-codec': 1.5.5 1114 | '@jridgewell/trace-mapping': 0.3.31 1115 | 1116 | '@jridgewell/resolve-uri@3.1.2': {} 1117 | 1118 | '@jridgewell/sourcemap-codec@1.5.5': {} 1119 | 1120 | '@jridgewell/trace-mapping@0.3.31': 1121 | dependencies: 1122 | '@jridgewell/resolve-uri': 3.1.2 1123 | '@jridgewell/sourcemap-codec': 1.5.5 1124 | 1125 | '@next/env@16.0.9': {} 1126 | 1127 | '@next/swc-darwin-arm64@16.0.9': 1128 | optional: true 1129 | 1130 | '@next/swc-darwin-x64@16.0.9': 1131 | optional: true 1132 | 1133 | '@next/swc-linux-arm64-gnu@16.0.9': 1134 | optional: true 1135 | 1136 | '@next/swc-linux-arm64-musl@16.0.9': 1137 | optional: true 1138 | 1139 | '@next/swc-linux-x64-gnu@16.0.9': 1140 | optional: true 1141 | 1142 | '@next/swc-linux-x64-musl@16.0.9': 1143 | optional: true 1144 | 1145 | '@next/swc-win32-arm64-msvc@16.0.9': 1146 | optional: true 1147 | 1148 | '@next/swc-win32-x64-msvc@16.0.9': 1149 | optional: true 1150 | 1151 | '@nodelib/fs.scandir@2.1.5': 1152 | dependencies: 1153 | '@nodelib/fs.stat': 2.0.5 1154 | run-parallel: 1.2.0 1155 | 1156 | '@nodelib/fs.stat@2.0.5': {} 1157 | 1158 | '@nodelib/fs.walk@1.2.8': 1159 | dependencies: 1160 | '@nodelib/fs.scandir': 2.1.5 1161 | fastq: 1.19.1 1162 | 1163 | '@pkgjs/parseargs@0.11.0': 1164 | optional: true 1165 | 1166 | '@radix-ui/react-icons@1.3.2(react@18.3.1)': 1167 | dependencies: 1168 | react: 18.3.1 1169 | 1170 | '@swc/helpers@0.5.15': 1171 | dependencies: 1172 | tslib: 2.8.1 1173 | 1174 | '@types/node@20.19.24': 1175 | dependencies: 1176 | undici-types: 6.21.0 1177 | 1178 | '@types/prop-types@15.7.15': {} 1179 | 1180 | '@types/react-dom@18.3.7(@types/react@18.3.26)': 1181 | dependencies: 1182 | '@types/react': 18.3.26 1183 | 1184 | '@types/react@18.3.26': 1185 | dependencies: 1186 | '@types/prop-types': 15.7.15 1187 | csstype: 3.1.3 1188 | 1189 | ansi-regex@5.0.1: {} 1190 | 1191 | ansi-regex@6.2.2: {} 1192 | 1193 | ansi-styles@4.3.0: 1194 | dependencies: 1195 | color-convert: 2.0.1 1196 | 1197 | ansi-styles@6.2.3: {} 1198 | 1199 | any-promise@1.3.0: {} 1200 | 1201 | anymatch@3.1.3: 1202 | dependencies: 1203 | normalize-path: 3.0.0 1204 | picomatch: 2.3.1 1205 | 1206 | arg@5.0.2: {} 1207 | 1208 | asynckit@0.4.0: {} 1209 | 1210 | autoprefixer@10.4.22(postcss@8.5.6): 1211 | dependencies: 1212 | browserslist: 4.28.0 1213 | caniuse-lite: 1.0.30001754 1214 | fraction.js: 5.3.4 1215 | normalize-range: 0.1.2 1216 | picocolors: 1.1.1 1217 | postcss: 8.5.6 1218 | postcss-value-parser: 4.2.0 1219 | 1220 | axios@1.13.2: 1221 | dependencies: 1222 | follow-redirects: 1.15.11 1223 | form-data: 4.0.4 1224 | proxy-from-env: 1.1.0 1225 | transitivePeerDependencies: 1226 | - debug 1227 | 1228 | babel-plugin-react-compiler@1.0.0: 1229 | dependencies: 1230 | '@babel/types': 7.28.5 1231 | 1232 | balanced-match@1.0.2: {} 1233 | 1234 | baseline-browser-mapping@2.8.26: {} 1235 | 1236 | binary-extensions@2.3.0: {} 1237 | 1238 | brace-expansion@2.0.2: 1239 | dependencies: 1240 | balanced-match: 1.0.2 1241 | 1242 | braces@3.0.3: 1243 | dependencies: 1244 | fill-range: 7.1.1 1245 | 1246 | browserslist@4.28.0: 1247 | dependencies: 1248 | baseline-browser-mapping: 2.8.26 1249 | caniuse-lite: 1.0.30001754 1250 | electron-to-chromium: 1.5.250 1251 | node-releases: 2.0.27 1252 | update-browserslist-db: 1.1.4(browserslist@4.28.0) 1253 | 1254 | call-bind-apply-helpers@1.0.2: 1255 | dependencies: 1256 | es-errors: 1.3.0 1257 | function-bind: 1.1.2 1258 | 1259 | camelcase-css@2.0.1: {} 1260 | 1261 | caniuse-lite@1.0.30001754: {} 1262 | 1263 | chokidar@3.6.0: 1264 | dependencies: 1265 | anymatch: 3.1.3 1266 | braces: 3.0.3 1267 | glob-parent: 5.1.2 1268 | is-binary-path: 2.1.0 1269 | is-glob: 4.0.3 1270 | normalize-path: 3.0.0 1271 | readdirp: 3.6.0 1272 | optionalDependencies: 1273 | fsevents: 2.3.3 1274 | 1275 | classnames@2.5.1: {} 1276 | 1277 | client-only@0.0.1: {} 1278 | 1279 | color-convert@2.0.1: 1280 | dependencies: 1281 | color-name: 1.1.4 1282 | 1283 | color-name@1.1.4: {} 1284 | 1285 | combined-stream@1.0.8: 1286 | dependencies: 1287 | delayed-stream: 1.0.0 1288 | 1289 | commander@4.1.1: {} 1290 | 1291 | cross-spawn@7.0.6: 1292 | dependencies: 1293 | path-key: 3.1.1 1294 | shebang-command: 2.0.0 1295 | which: 2.0.2 1296 | 1297 | cssesc@3.0.0: {} 1298 | 1299 | csstype@3.1.3: {} 1300 | 1301 | delayed-stream@1.0.0: {} 1302 | 1303 | dequal@2.0.3: {} 1304 | 1305 | detect-libc@2.1.2: 1306 | optional: true 1307 | 1308 | didyoumean@1.2.2: {} 1309 | 1310 | dlv@1.1.3: {} 1311 | 1312 | dunder-proto@1.0.1: 1313 | dependencies: 1314 | call-bind-apply-helpers: 1.0.2 1315 | es-errors: 1.3.0 1316 | gopd: 1.2.0 1317 | 1318 | eastasianwidth@0.2.0: {} 1319 | 1320 | electron-to-chromium@1.5.250: {} 1321 | 1322 | emoji-regex@8.0.0: {} 1323 | 1324 | emoji-regex@9.2.2: {} 1325 | 1326 | es-define-property@1.0.1: {} 1327 | 1328 | es-errors@1.3.0: {} 1329 | 1330 | es-object-atoms@1.1.1: 1331 | dependencies: 1332 | es-errors: 1.3.0 1333 | 1334 | es-set-tostringtag@2.1.0: 1335 | dependencies: 1336 | es-errors: 1.3.0 1337 | get-intrinsic: 1.3.0 1338 | has-tostringtag: 1.0.2 1339 | hasown: 2.0.2 1340 | 1341 | escalade@3.2.0: {} 1342 | 1343 | fancy-canvas@2.1.0: {} 1344 | 1345 | fast-glob@3.3.3: 1346 | dependencies: 1347 | '@nodelib/fs.stat': 2.0.5 1348 | '@nodelib/fs.walk': 1.2.8 1349 | glob-parent: 5.1.2 1350 | merge2: 1.4.1 1351 | micromatch: 4.0.8 1352 | 1353 | fastq@1.19.1: 1354 | dependencies: 1355 | reusify: 1.1.0 1356 | 1357 | fill-range@7.1.1: 1358 | dependencies: 1359 | to-regex-range: 5.0.1 1360 | 1361 | follow-redirects@1.15.11: {} 1362 | 1363 | foreground-child@3.3.1: 1364 | dependencies: 1365 | cross-spawn: 7.0.6 1366 | signal-exit: 4.1.0 1367 | 1368 | form-data@4.0.4: 1369 | dependencies: 1370 | asynckit: 0.4.0 1371 | combined-stream: 1.0.8 1372 | es-set-tostringtag: 2.1.0 1373 | hasown: 2.0.2 1374 | mime-types: 2.1.35 1375 | 1376 | fraction.js@5.3.4: {} 1377 | 1378 | framer-motion@12.23.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 1379 | dependencies: 1380 | motion-dom: 12.23.23 1381 | motion-utils: 12.23.6 1382 | tslib: 2.8.1 1383 | optionalDependencies: 1384 | react: 18.3.1 1385 | react-dom: 18.3.1(react@18.3.1) 1386 | 1387 | fsevents@2.3.3: 1388 | optional: true 1389 | 1390 | function-bind@1.1.2: {} 1391 | 1392 | get-intrinsic@1.3.0: 1393 | dependencies: 1394 | call-bind-apply-helpers: 1.0.2 1395 | es-define-property: 1.0.1 1396 | es-errors: 1.3.0 1397 | es-object-atoms: 1.1.1 1398 | function-bind: 1.1.2 1399 | get-proto: 1.0.1 1400 | gopd: 1.2.0 1401 | has-symbols: 1.1.0 1402 | hasown: 2.0.2 1403 | math-intrinsics: 1.1.0 1404 | 1405 | get-proto@1.0.1: 1406 | dependencies: 1407 | dunder-proto: 1.0.1 1408 | es-object-atoms: 1.1.1 1409 | 1410 | glob-parent@5.1.2: 1411 | dependencies: 1412 | is-glob: 4.0.3 1413 | 1414 | glob-parent@6.0.2: 1415 | dependencies: 1416 | is-glob: 4.0.3 1417 | 1418 | glob@10.4.5: 1419 | dependencies: 1420 | foreground-child: 3.3.1 1421 | jackspeak: 3.4.3 1422 | minimatch: 9.0.5 1423 | minipass: 7.1.2 1424 | package-json-from-dist: 1.0.1 1425 | path-scurry: 1.11.1 1426 | 1427 | gopd@1.2.0: {} 1428 | 1429 | has-symbols@1.1.0: {} 1430 | 1431 | has-tostringtag@1.0.2: 1432 | dependencies: 1433 | has-symbols: 1.1.0 1434 | 1435 | hasown@2.0.2: 1436 | dependencies: 1437 | function-bind: 1.1.2 1438 | 1439 | is-binary-path@2.1.0: 1440 | dependencies: 1441 | binary-extensions: 2.3.0 1442 | 1443 | is-core-module@2.16.1: 1444 | dependencies: 1445 | hasown: 2.0.2 1446 | 1447 | is-extglob@2.1.1: {} 1448 | 1449 | is-fullwidth-code-point@3.0.0: {} 1450 | 1451 | is-glob@4.0.3: 1452 | dependencies: 1453 | is-extglob: 2.1.1 1454 | 1455 | is-number@7.0.0: {} 1456 | 1457 | isexe@2.0.0: {} 1458 | 1459 | jackspeak@3.4.3: 1460 | dependencies: 1461 | '@isaacs/cliui': 8.0.2 1462 | optionalDependencies: 1463 | '@pkgjs/parseargs': 0.11.0 1464 | 1465 | jiti@1.21.7: {} 1466 | 1467 | js-tokens@4.0.0: {} 1468 | 1469 | lightweight-charts@4.2.3: 1470 | dependencies: 1471 | fancy-canvas: 2.1.0 1472 | 1473 | lilconfig@3.1.3: {} 1474 | 1475 | lines-and-columns@1.2.4: {} 1476 | 1477 | loose-envify@1.4.0: 1478 | dependencies: 1479 | js-tokens: 4.0.0 1480 | 1481 | lru-cache@10.4.3: {} 1482 | 1483 | lucide-react@0.468.0(react@18.3.1): 1484 | dependencies: 1485 | react: 18.3.1 1486 | 1487 | math-intrinsics@1.1.0: {} 1488 | 1489 | merge2@1.4.1: {} 1490 | 1491 | micromatch@4.0.8: 1492 | dependencies: 1493 | braces: 3.0.3 1494 | picomatch: 2.3.1 1495 | 1496 | mime-db@1.52.0: {} 1497 | 1498 | mime-types@2.1.35: 1499 | dependencies: 1500 | mime-db: 1.52.0 1501 | 1502 | minimatch@9.0.5: 1503 | dependencies: 1504 | brace-expansion: 2.0.2 1505 | 1506 | minipass@7.1.2: {} 1507 | 1508 | motion-dom@12.23.23: 1509 | dependencies: 1510 | motion-utils: 12.23.6 1511 | 1512 | motion-utils@12.23.6: {} 1513 | 1514 | mz@2.7.0: 1515 | dependencies: 1516 | any-promise: 1.3.0 1517 | object-assign: 4.1.1 1518 | thenify-all: 1.6.0 1519 | 1520 | nanoid@3.3.11: {} 1521 | 1522 | next@16.0.9(babel-plugin-react-compiler@1.0.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): 1523 | dependencies: 1524 | '@next/env': 16.0.9 1525 | '@swc/helpers': 0.5.15 1526 | caniuse-lite: 1.0.30001754 1527 | postcss: 8.4.31 1528 | react: 18.3.1 1529 | react-dom: 18.3.1(react@18.3.1) 1530 | styled-jsx: 5.1.6(react@18.3.1) 1531 | optionalDependencies: 1532 | '@next/swc-darwin-arm64': 16.0.9 1533 | '@next/swc-darwin-x64': 16.0.9 1534 | '@next/swc-linux-arm64-gnu': 16.0.9 1535 | '@next/swc-linux-arm64-musl': 16.0.9 1536 | '@next/swc-linux-x64-gnu': 16.0.9 1537 | '@next/swc-linux-x64-musl': 16.0.9 1538 | '@next/swc-win32-arm64-msvc': 16.0.9 1539 | '@next/swc-win32-x64-msvc': 16.0.9 1540 | babel-plugin-react-compiler: 1.0.0 1541 | sharp: 0.34.5 1542 | transitivePeerDependencies: 1543 | - '@babel/core' 1544 | - babel-plugin-macros 1545 | 1546 | node-releases@2.0.27: {} 1547 | 1548 | normalize-path@3.0.0: {} 1549 | 1550 | normalize-range@0.1.2: {} 1551 | 1552 | number-abbreviate@2.0.0: {} 1553 | 1554 | object-assign@4.1.1: {} 1555 | 1556 | object-hash@3.0.0: {} 1557 | 1558 | package-json-from-dist@1.0.1: {} 1559 | 1560 | path-key@3.1.1: {} 1561 | 1562 | path-parse@1.0.7: {} 1563 | 1564 | path-scurry@1.11.1: 1565 | dependencies: 1566 | lru-cache: 10.4.3 1567 | minipass: 7.1.2 1568 | 1569 | picocolors@1.1.1: {} 1570 | 1571 | picomatch@2.3.1: {} 1572 | 1573 | pify@2.3.0: {} 1574 | 1575 | pirates@4.0.7: {} 1576 | 1577 | postcss-import@15.1.0(postcss@8.5.6): 1578 | dependencies: 1579 | postcss: 8.5.6 1580 | postcss-value-parser: 4.2.0 1581 | read-cache: 1.0.0 1582 | resolve: 1.22.11 1583 | 1584 | postcss-js@4.1.0(postcss@8.5.6): 1585 | dependencies: 1586 | camelcase-css: 2.0.1 1587 | postcss: 8.5.6 1588 | 1589 | postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): 1590 | dependencies: 1591 | lilconfig: 3.1.3 1592 | optionalDependencies: 1593 | jiti: 1.21.7 1594 | postcss: 8.5.6 1595 | 1596 | postcss-nested@6.2.0(postcss@8.5.6): 1597 | dependencies: 1598 | postcss: 8.5.6 1599 | postcss-selector-parser: 6.1.2 1600 | 1601 | postcss-selector-parser@6.1.2: 1602 | dependencies: 1603 | cssesc: 3.0.0 1604 | util-deprecate: 1.0.2 1605 | 1606 | postcss-value-parser@4.2.0: {} 1607 | 1608 | postcss@8.4.31: 1609 | dependencies: 1610 | nanoid: 3.3.11 1611 | picocolors: 1.1.1 1612 | source-map-js: 1.2.1 1613 | 1614 | postcss@8.5.6: 1615 | dependencies: 1616 | nanoid: 3.3.11 1617 | picocolors: 1.1.1 1618 | source-map-js: 1.2.1 1619 | 1620 | prettier@2.8.8: {} 1621 | 1622 | proxy-from-env@1.1.0: {} 1623 | 1624 | queue-microtask@1.2.3: {} 1625 | 1626 | react-dom@18.3.1(react@18.3.1): 1627 | dependencies: 1628 | loose-envify: 1.4.0 1629 | react: 18.3.1 1630 | scheduler: 0.23.2 1631 | 1632 | react@18.3.1: 1633 | dependencies: 1634 | loose-envify: 1.4.0 1635 | 1636 | read-cache@1.0.0: 1637 | dependencies: 1638 | pify: 2.3.0 1639 | 1640 | readdirp@3.6.0: 1641 | dependencies: 1642 | picomatch: 2.3.1 1643 | 1644 | resolve@1.22.11: 1645 | dependencies: 1646 | is-core-module: 2.16.1 1647 | path-parse: 1.0.7 1648 | supports-preserve-symlinks-flag: 1.0.0 1649 | 1650 | reusify@1.1.0: {} 1651 | 1652 | run-parallel@1.2.0: 1653 | dependencies: 1654 | queue-microtask: 1.2.3 1655 | 1656 | scheduler@0.23.2: 1657 | dependencies: 1658 | loose-envify: 1.4.0 1659 | 1660 | semver@7.7.3: 1661 | optional: true 1662 | 1663 | sharp@0.34.5: 1664 | dependencies: 1665 | '@img/colour': 1.0.0 1666 | detect-libc: 2.1.2 1667 | semver: 7.7.3 1668 | optionalDependencies: 1669 | '@img/sharp-darwin-arm64': 0.34.5 1670 | '@img/sharp-darwin-x64': 0.34.5 1671 | '@img/sharp-libvips-darwin-arm64': 1.2.4 1672 | '@img/sharp-libvips-darwin-x64': 1.2.4 1673 | '@img/sharp-libvips-linux-arm': 1.2.4 1674 | '@img/sharp-libvips-linux-arm64': 1.2.4 1675 | '@img/sharp-libvips-linux-ppc64': 1.2.4 1676 | '@img/sharp-libvips-linux-riscv64': 1.2.4 1677 | '@img/sharp-libvips-linux-s390x': 1.2.4 1678 | '@img/sharp-libvips-linux-x64': 1.2.4 1679 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 1680 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4 1681 | '@img/sharp-linux-arm': 0.34.5 1682 | '@img/sharp-linux-arm64': 0.34.5 1683 | '@img/sharp-linux-ppc64': 0.34.5 1684 | '@img/sharp-linux-riscv64': 0.34.5 1685 | '@img/sharp-linux-s390x': 0.34.5 1686 | '@img/sharp-linux-x64': 0.34.5 1687 | '@img/sharp-linuxmusl-arm64': 0.34.5 1688 | '@img/sharp-linuxmusl-x64': 0.34.5 1689 | '@img/sharp-wasm32': 0.34.5 1690 | '@img/sharp-win32-arm64': 0.34.5 1691 | '@img/sharp-win32-ia32': 0.34.5 1692 | '@img/sharp-win32-x64': 0.34.5 1693 | optional: true 1694 | 1695 | shebang-command@2.0.0: 1696 | dependencies: 1697 | shebang-regex: 3.0.0 1698 | 1699 | shebang-regex@3.0.0: {} 1700 | 1701 | signal-exit@4.1.0: {} 1702 | 1703 | source-map-js@1.2.1: {} 1704 | 1705 | string-width@4.2.3: 1706 | dependencies: 1707 | emoji-regex: 8.0.0 1708 | is-fullwidth-code-point: 3.0.0 1709 | strip-ansi: 6.0.1 1710 | 1711 | string-width@5.1.2: 1712 | dependencies: 1713 | eastasianwidth: 0.2.0 1714 | emoji-regex: 9.2.2 1715 | strip-ansi: 7.1.2 1716 | 1717 | strip-ansi@6.0.1: 1718 | dependencies: 1719 | ansi-regex: 5.0.1 1720 | 1721 | strip-ansi@7.1.2: 1722 | dependencies: 1723 | ansi-regex: 6.2.2 1724 | 1725 | styled-jsx@5.1.6(react@18.3.1): 1726 | dependencies: 1727 | client-only: 0.0.1 1728 | react: 18.3.1 1729 | 1730 | sucrase@3.35.0: 1731 | dependencies: 1732 | '@jridgewell/gen-mapping': 0.3.13 1733 | commander: 4.1.1 1734 | glob: 10.4.5 1735 | lines-and-columns: 1.2.4 1736 | mz: 2.7.0 1737 | pirates: 4.0.7 1738 | ts-interface-checker: 0.1.13 1739 | 1740 | supports-preserve-symlinks-flag@1.0.0: {} 1741 | 1742 | swr@2.3.6(react@18.3.1): 1743 | dependencies: 1744 | dequal: 2.0.3 1745 | react: 18.3.1 1746 | use-sync-external-store: 1.6.0(react@18.3.1) 1747 | 1748 | tailwindcss@3.4.18: 1749 | dependencies: 1750 | '@alloc/quick-lru': 5.2.0 1751 | arg: 5.0.2 1752 | chokidar: 3.6.0 1753 | didyoumean: 1.2.2 1754 | dlv: 1.1.3 1755 | fast-glob: 3.3.3 1756 | glob-parent: 6.0.2 1757 | is-glob: 4.0.3 1758 | jiti: 1.21.7 1759 | lilconfig: 3.1.3 1760 | micromatch: 4.0.8 1761 | normalize-path: 3.0.0 1762 | object-hash: 3.0.0 1763 | picocolors: 1.1.1 1764 | postcss: 8.5.6 1765 | postcss-import: 15.1.0(postcss@8.5.6) 1766 | postcss-js: 4.1.0(postcss@8.5.6) 1767 | postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) 1768 | postcss-nested: 6.2.0(postcss@8.5.6) 1769 | postcss-selector-parser: 6.1.2 1770 | resolve: 1.22.11 1771 | sucrase: 3.35.0 1772 | transitivePeerDependencies: 1773 | - tsx 1774 | - yaml 1775 | 1776 | thenify-all@1.6.0: 1777 | dependencies: 1778 | thenify: 3.3.1 1779 | 1780 | thenify@3.3.1: 1781 | dependencies: 1782 | any-promise: 1.3.0 1783 | 1784 | to-regex-range@5.0.1: 1785 | dependencies: 1786 | is-number: 7.0.0 1787 | 1788 | ts-interface-checker@0.1.13: {} 1789 | 1790 | tslib@2.8.1: {} 1791 | 1792 | typescript@5.9.3: {} 1793 | 1794 | undici-types@6.21.0: {} 1795 | 1796 | update-browserslist-db@1.1.4(browserslist@4.28.0): 1797 | dependencies: 1798 | browserslist: 4.28.0 1799 | escalade: 3.2.0 1800 | picocolors: 1.1.1 1801 | 1802 | use-sync-external-store@1.6.0(react@18.3.1): 1803 | dependencies: 1804 | react: 18.3.1 1805 | 1806 | util-deprecate@1.0.2: {} 1807 | 1808 | which@2.0.2: 1809 | dependencies: 1810 | isexe: 2.0.0 1811 | 1812 | wrap-ansi@7.0.0: 1813 | dependencies: 1814 | ansi-styles: 4.3.0 1815 | string-width: 4.2.3 1816 | strip-ansi: 6.0.1 1817 | 1818 | wrap-ansi@8.1.0: 1819 | dependencies: 1820 | ansi-styles: 6.2.3 1821 | string-width: 5.1.2 1822 | strip-ansi: 7.1.2 1823 | --------------------------------------------------------------------------------