ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
16
fork

Configure Feed

Select the types of activity you want to include in your feed.

linting

byarielm.fyi 77208456 868c8779

verified
+2827 -670
+1
.gitignore
··· 6 6 .netlify/ 7 7 .claude/ 8 8 .deciduous/ 9 + .serena/ 9 10 node_modules/ 10 11 docs/ 11 12 dist/
+9
packages/web/.prettierignore
··· 1 + dist 2 + build 3 + .netlify 4 + node_modules 5 + coverage 6 + *.config.js 7 + *.config.ts 8 + pnpm-lock.yaml 9 + package-lock.json
+11
packages/web/.prettierrc.json
··· 1 + { 2 + "semi": true, 3 + "trailingComma": "es5", 4 + "singleQuote": false, 5 + "printWidth": 80, 6 + "tabWidth": 2, 7 + "useTabs": false, 8 + "arrowParens": "always", 9 + "endOfLine": "lf", 10 + "plugins": ["prettier-plugin-tailwindcss"] 11 + }
+119
packages/web/eslint.config.js
··· 1 + import js from '@eslint/js'; 2 + import tsPlugin from '@typescript-eslint/eslint-plugin'; 3 + import tsParser from '@typescript-eslint/parser'; 4 + import reactPlugin from 'eslint-plugin-react'; 5 + import reactHooksPlugin from 'eslint-plugin-react-hooks'; 6 + import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; 7 + import tailwindPlugin from 'eslint-plugin-tailwindcss'; 8 + 9 + export default [ 10 + // Ignore patterns 11 + { 12 + ignores: [ 13 + 'dist/**', 14 + 'build/**', 15 + '.netlify/**', 16 + 'node_modules/**', 17 + 'coverage/**', 18 + '*.config.js', 19 + '*.config.ts', 20 + ], 21 + }, 22 + 23 + // Base config for all files 24 + js.configs.recommended, 25 + 26 + // TypeScript files 27 + { 28 + files: ['**/*.ts', '**/*.tsx'], 29 + languageOptions: { 30 + parser: tsParser, 31 + parserOptions: { 32 + ecmaVersion: 'latest', 33 + sourceType: 'module', 34 + ecmaFeatures: { 35 + jsx: true, 36 + }, 37 + project: './tsconfig.json', 38 + }, 39 + globals: { 40 + // Browser globals 41 + window: 'readonly', 42 + document: 'readonly', 43 + console: 'readonly', 44 + fetch: 'readonly', 45 + crypto: 'readonly', 46 + localStorage: 'readonly', 47 + sessionStorage: 'readonly', 48 + navigator: 'readonly', 49 + location: 'readonly', 50 + // React 51 + React: 'readonly', 52 + JSX: 'readonly', 53 + }, 54 + }, 55 + plugins: { 56 + '@typescript-eslint': tsPlugin, 57 + 'react': reactPlugin, 58 + 'react-hooks': reactHooksPlugin, 59 + 'jsx-a11y': jsxA11yPlugin, 60 + 'tailwindcss': tailwindPlugin, 61 + }, 62 + settings: { 63 + react: { 64 + version: 'detect', 65 + }, 66 + tailwindcss: { 67 + callees: ['cn', 'cva', 'clsx'], 68 + config: 'tailwind.config.js', 69 + }, 70 + }, 71 + rules: { 72 + // TypeScript 73 + ...tsPlugin.configs.recommended.rules, 74 + '@typescript-eslint/no-unused-vars': [ 75 + 'warn', 76 + { 77 + argsIgnorePattern: '^_', 78 + varsIgnorePattern: '^_', 79 + }, 80 + ], 81 + '@typescript-eslint/no-explicit-any': 'warn', 82 + '@typescript-eslint/explicit-module-boundary-types': 'off', 83 + 84 + // React 85 + ...reactPlugin.configs.recommended.rules, 86 + ...reactPlugin.configs['jsx-runtime'].rules, 87 + 'react/react-in-jsx-scope': 'off', // Not needed in React 18+ 88 + 'react/prop-types': 'off', // Using TypeScript 89 + 'react/jsx-uses-react': 'off', 90 + 91 + // React Hooks 92 + ...reactHooksPlugin.configs.recommended.rules, 93 + 94 + // Accessibility (jsx-a11y) 95 + ...jsxA11yPlugin.configs.recommended.rules, 96 + 'jsx-a11y/anchor-is-valid': [ 97 + 'error', 98 + { 99 + components: ['Link'], 100 + specialLink: ['to'], 101 + }, 102 + ], 103 + 104 + // Tailwind CSS 105 + 'tailwindcss/classnames-order': 'warn', 106 + 'tailwindcss/enforces-negative-arbitrary-values': 'warn', 107 + 'tailwindcss/enforces-shorthand': 'warn', 108 + 'tailwindcss/no-arbitrary-value': 'off', // Allow arbitrary values 109 + 'tailwindcss/no-contradicting-classname': 'error', 110 + 'tailwindcss/no-custom-classname': [ 111 + 'warn', 112 + { 113 + whitelist: [], // Add custom classes here if needed 114 + }, 115 + ], 116 + 'tailwindcss/no-unnecessary-arbitrary-value': 'warn', 117 + }, 118 + }, 119 + ];
+17 -1
packages/web/package.json
··· 7 7 "dev": "vite --mode mock", 8 8 "dev:full": "vite", 9 9 "build": "vite build", 10 - "preview": "vite preview" 10 + "preview": "vite preview", 11 + "lint": "eslint . --ext .ts,.tsx --fix", 12 + "lint:check": "eslint . --ext .ts,.tsx", 13 + "lint:html": "eslint . --ext .ts,.tsx --rule 'jsx-a11y/*: error' --fix", 14 + "lint:tailwind": "eslint . --ext .ts,.tsx --rule 'tailwindcss/*: warn' --fix", 15 + "format": "prettier --write \"src/**/*.{ts,tsx,css}\"", 16 + "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"" 11 17 }, 12 18 "dependencies": { 13 19 "@atlast/shared": "workspace:*", ··· 26 32 "@types/jszip": "^3.4.0", 27 33 "@types/react": "^19.1.14", 28 34 "@types/react-dom": "^19.1.9", 35 + "@eslint/js": "^9.39.0", 36 + "@typescript-eslint/eslint-plugin": "^8.20.0", 37 + "@typescript-eslint/parser": "^8.20.0", 29 38 "@vitejs/plugin-react": "^4.2.1", 30 39 "autoprefixer": "^10.4.21", 40 + "eslint": "^9.19.0", 41 + "eslint-plugin-jsx-a11y": "^6.10.2", 42 + "eslint-plugin-react": "^7.37.2", 43 + "eslint-plugin-react-hooks": "^5.1.0", 44 + "eslint-plugin-tailwindcss": "^3.17.5", 31 45 "postcss": "^8.5.6", 46 + "prettier": "^3.4.2", 47 + "prettier-plugin-tailwindcss": "^0.6.11", 32 48 "tailwindcss": "^3.4.0", 33 49 "typescript": "^5.3.3", 34 50 "vite": "^5.4.0",
+69 -43
packages/web/src/App.tsx
··· 25 25 26 26 // Loading fallback component 27 27 const PageLoader: React.FC = () => ( 28 - <div className="p-6 max-w-md mx-auto mt-8"> 29 - <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-8 text-center space-y-4"> 30 - <div className="w-16 h-16 bg-firefly-banner dark:bg-firefly-banner-dark text-white rounded-2xl mx-auto flex items-center justify-center"> 31 - <ArrowRight className="w-8 h-8 text-white animate-pulse" /> 28 + <div className="mx-auto mt-8 max-w-md p-6"> 29 + <div className="space-y-4 rounded-2xl bg-white p-8 text-center shadow-lg dark:bg-gray-800"> 30 + <div className="mx-auto flex size-16 items-center justify-center rounded-2xl bg-firefly-banner text-white dark:bg-firefly-banner-dark"> 31 + <ArrowRight className="size-8 animate-pulse text-white" /> 32 32 </div> 33 33 <h2 className="text-xl font-bold text-gray-900 dark:text-gray-100"> 34 34 Loading... ··· 66 66 67 67 // Settings state 68 68 const userSettings = useSettingsStore((state) => state.settings); 69 - const handleSettingsUpdate = useSettingsStore((state) => state.updateSettings); 69 + const handleSettingsUpdate = useSettingsStore( 70 + (state) => state.updateSettings 71 + ); 70 72 71 73 // Search hook 72 74 const { ··· 93 95 session, 94 96 searchResults, 95 97 setSearchResults, 96 - currentDestinationAppId, 98 + currentDestinationAppId 97 99 ); 98 100 99 101 // Save results handler (proper state management) ··· 122 124 }); 123 125 } 124 126 }, 125 - [userSettings.saveData, savedUploads], 127 + [userSettings.saveData, savedUploads] 126 128 ); 127 129 128 130 // File upload handler ··· 147 149 saveResults(uploadId, platform, finalResults); 148 150 } 149 151 }, 150 - followLexicon, 152 + followLexicon 151 153 ); 152 154 }, 153 155 setStatusMessage, 154 - userSettings, 156 + userSettings 155 157 ); 156 158 157 159 // Load previous upload handler ··· 177 179 setCurrentPlatform(platform); 178 180 179 181 // Check if this is a new upload with no matches yet 180 - const hasMatches = data.results.some(r => r.atprotoMatches.length > 0); 182 + const hasMatches = data.results.some( 183 + (r) => r.atprotoMatches.length > 0 184 + ); 181 185 182 186 const loadedResults: SearchResult[] = data.results.map((result) => ({ 183 187 sourceUser: result.sourceUser, // SourceUser object { username, date } ··· 189 193 .filter( 190 194 (match) => 191 195 !match.followStatus || 192 - !Object.values(match.followStatus).some((status) => status), 196 + !Object.values(match.followStatus).some((status) => status) 193 197 ) 194 198 .slice(0, 1) 195 - .map((match) => match.did), 199 + .map((match) => match.did) 196 200 ), 197 201 })); 198 202 ··· 200 204 201 205 // If no matches yet, trigger search BEFORE navigating to results 202 206 if (!hasMatches) { 203 - const followLexicon = ATPROTO_APPS[currentDestinationAppId]?.followLexicon; 207 + const followLexicon = 208 + ATPROTO_APPS[currentDestinationAppId]?.followLexicon; 204 209 205 210 await searchAllUsers( 206 211 loadedResults, ··· 219 224 220 225 // Announce to screen readers only - visual feedback is navigation to results page 221 226 setAriaAnnouncement( 222 - `Loaded ${loadedResults.length} results from previous upload`, 227 + `Loaded ${loadedResults.length} results from previous upload` 223 228 ); 224 229 } catch (err) { 225 230 console.error("Failed to load upload:", err); ··· 227 232 setCurrentStep("home"); 228 233 } 229 234 }, 230 - [setStatusMessage, setCurrentStep, setSearchResults, setAriaAnnouncement, error, currentDestinationAppId, searchAllUsers, saveResults], 235 + [ 236 + setStatusMessage, 237 + setCurrentStep, 238 + setSearchResults, 239 + setAriaAnnouncement, 240 + error, 241 + currentDestinationAppId, 242 + searchAllUsers, 243 + saveResults, 244 + ] 231 245 ); 232 246 233 247 // Login handler ··· 247 261 error(errorMsg); 248 262 } 249 263 }, 250 - [login, error, setStatusMessage], 264 + [login, error, setStatusMessage] 251 265 ); 252 266 253 267 // Logout handler ··· 267 281 // Extension import handler 268 282 useEffect(() => { 269 283 const urlParams = new URLSearchParams(window.location.search); 270 - const importId = urlParams.get('importId'); 284 + const importId = urlParams.get("importId"); 271 285 272 286 if (!importId || !session) { 273 287 return; ··· 276 290 // Fetch and process extension import 277 291 async function handleExtensionImport(id: string) { 278 292 try { 279 - setStatusMessage('Loading import from extension...'); 280 - setCurrentStep('loading'); 293 + setStatusMessage("Loading import from extension..."); 294 + setCurrentStep("loading"); 281 295 282 296 const response = await fetch( 283 297 `/.netlify/functions/get-extension-import?importId=${id}` 284 298 ); 285 299 286 300 if (!response.ok) { 287 - throw new Error('Import not found or expired'); 301 + throw new Error("Import not found or expired"); 288 302 } 289 303 290 304 const importData = await response.json(); ··· 293 307 const platform = importData.platform; 294 308 setCurrentPlatform(platform); 295 309 296 - const initialResults: SearchResult[] = importData.usernames.map((username: string) => ({ 297 - sourceUser: username, 298 - sourcePlatform: platform, 299 - isSearching: true, 300 - atprotoMatches: [], 301 - selectedMatches: new Set<string>(), 302 - })); 310 + const initialResults: SearchResult[] = importData.usernames.map( 311 + (username: string) => ({ 312 + sourceUser: username, 313 + sourcePlatform: platform, 314 + isSearching: true, 315 + atprotoMatches: [], 316 + selectedMatches: new Set<string>(), 317 + }) 318 + ); 303 319 304 320 setSearchResults(initialResults); 305 321 306 322 const uploadId = crypto.randomUUID(); 307 - const followLexicon = ATPROTO_APPS[currentDestinationAppId]?.followLexicon; 323 + const followLexicon = 324 + ATPROTO_APPS[currentDestinationAppId]?.followLexicon; 308 325 309 326 // Start search 310 327 await searchAllUsers( 311 328 initialResults, 312 329 setStatusMessage, 313 330 (finalResults) => { 314 - setCurrentStep('results'); 331 + setCurrentStep("results"); 315 332 316 333 // Save results after search completes 317 334 if (finalResults.length > 0) { ··· 320 337 321 338 // Clear import ID from URL 322 339 const newUrl = new URL(window.location.href); 323 - newUrl.searchParams.delete('importId'); 324 - window.history.replaceState({}, '', newUrl); 340 + newUrl.searchParams.delete("importId"); 341 + window.history.replaceState({}, "", newUrl); 325 342 }, 326 343 followLexicon 327 344 ); 328 345 } catch (err) { 329 - console.error('Extension import error:', err); 330 - error('Failed to load import from extension. Please try again.'); 331 - setCurrentStep('home'); 346 + console.error("Extension import error:", err); 347 + error("Failed to load import from extension. Please try again."); 348 + setCurrentStep("home"); 332 349 333 350 // Clear import ID from URL on error 334 351 const newUrl = new URL(window.location.href); 335 - newUrl.searchParams.delete('importId'); 336 - window.history.replaceState({}, '', newUrl); 352 + newUrl.searchParams.delete("importId"); 353 + window.history.replaceState({}, "", newUrl); 337 354 } 338 355 } 339 356 340 357 handleExtensionImport(importId); 341 - }, [session, currentDestinationAppId, setStatusMessage, setCurrentStep, setSearchResults, searchAllUsers, saveResults, error]); 358 + }, [ 359 + session, 360 + currentDestinationAppId, 361 + setStatusMessage, 362 + setCurrentStep, 363 + setSearchResults, 364 + searchAllUsers, 365 + saveResults, 366 + error, 367 + ]); 342 368 343 369 // Load results from uploadId URL parameter 344 370 useEffect(() => { 345 371 const urlParams = new URLSearchParams(window.location.search); 346 - const uploadId = urlParams.get('uploadId'); 372 + const uploadId = urlParams.get("uploadId"); 347 373 348 374 if (!uploadId || !session) { 349 375 return; ··· 354 380 355 381 // Clean up URL parameter after loading 356 382 const newUrl = new URL(window.location.href); 357 - newUrl.searchParams.delete('uploadId'); 358 - window.history.replaceState({}, '', newUrl); 383 + newUrl.searchParams.delete("uploadId"); 384 + window.history.replaceState({}, "", newUrl); 359 385 }, [session, handleLoadUpload]); 360 386 361 387 return ( 362 388 <ErrorBoundary> 363 - <div className="min-h-screen relative overflow-hidden"> 389 + <div className="relative min-h-screen overflow-hidden"> 364 390 {/* Notification Container - errors only */} 365 391 <NotificationContainer 366 392 notifications={notifications} ··· 375 401 376 402 {/* Firefly particles - only render if motion not reduced */} 377 403 {!reducedMotion && ( 378 - <div className="fixed inset-0 pointer-events-none" aria-hidden="true"> 404 + <div className="pointer-events-none fixed inset-0" aria-hidden="true"> 379 405 {[...Array(15)].map((_, i) => ( 380 406 <Firefly 381 407 key={i} ··· 389 415 {/* Skip to main content link */} 390 416 <a 391 417 href="#main-content" 392 - className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:bg-firefly-orange focus:text-white focus:px-4 focus:py-2 focus:rounded-lg" 418 + className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 focus:rounded-lg focus:bg-firefly-orange focus:px-4 focus:py-2 focus:text-white" 393 419 > 394 420 Skip to main content 395 421 </a>
+2 -2
packages/web/src/Router.tsx
··· 1 - import { BrowserRouter, Routes, Route } from 'react-router-dom'; 2 - import App from './App'; 1 + import { BrowserRouter, Routes, Route } from "react-router-dom"; 2 + import App from "./App"; 3 3 4 4 /** 5 5 * Application Router
+15 -15
packages/web/src/components/AppHeader.tsx
··· 65 65 }, [showMenu]); 66 66 67 67 return ( 68 - <div className="bg-white dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-xl relative z-50"> 69 - <div className="max-w-6xl mx-auto px-4 py-1"> 68 + <div className="relative z-50 border-b-2 border-cyan-500/30 bg-white backdrop-blur-xl dark:border-purple-500/30 dark:bg-slate-900"> 69 + <div className="mx-auto max-w-6xl px-4 py-1"> 70 70 <div className="flex items-center justify-between"> 71 71 <button 72 72 onClick={() => onNavigate(session ? "home" : "login")} 73 - className="flex items-center space-x-3 hover:opacity-80 transition-opacity focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 rounded-lg px-2 py-1" 73 + className="flex items-center space-x-3 rounded-lg px-2 py-1 transition-opacity hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400" 74 74 > 75 - <FireflyLogo className="w-14 h-10" /> 75 + <FireflyLogo className="h-10 w-14" /> 76 76 <h1 className="font-display text-2xl font-bold text-purple-950 dark:text-cyan-50"> 77 77 ATlast 78 78 </h1> ··· 92 92 <button 93 93 ref={buttonRef} 94 94 onClick={() => setShowMenu(!showMenu)} 95 - className="flex items-center space-x-3 px-3 py-1 rounded-lg hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400" 95 + className="flex items-center space-x-3 rounded-lg px-3 py-1 transition-colors hover:bg-purple-50 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:hover:bg-slate-800 dark:focus:ring-amber-400" 96 96 > 97 97 <AvatarWithFallback 98 98 avatar={session?.avatar} 99 99 handle={session?.handle || ""} 100 100 size="sm" 101 101 /> 102 - <span className="text-sm font-medium text-purple-950 dark:text-cyan-50 hidden sm:inline"> 102 + <span className="hidden text-sm font-medium text-purple-950 dark:text-cyan-50 sm:inline"> 103 103 @{session?.handle} 104 104 </span> 105 105 <ChevronDown 106 - className={`w-4 h-4 text-purple-750 dark:text-cyan-250 transition-transform ${showMenu ? "rotate-180" : ""}`} 106 + className={`size-4 text-purple-750 transition-transform dark:text-cyan-250 ${showMenu ? "rotate-180" : ""}`} 107 107 /> 108 108 </button> 109 109 ··· 111 111 createPortal( 112 112 <div 113 113 ref={menuRef} 114 - className="fixed w-64 bg-white dark:bg-slate-900 rounded-lg shadow-2xl border-2 border-cyan-500/30 dark:border-purple-500/30 py-2 z-[9999]" 114 + className="fixed z-[9999] w-64 rounded-lg border-2 border-cyan-500/30 bg-white py-2 shadow-2xl dark:border-purple-500/30 dark:bg-slate-900" 115 115 style={{ 116 116 top: `${menuPosition.top}px`, 117 117 right: `${menuPosition.right}px`, ··· 130 130 setShowMenu(false); 131 131 onNavigate("home"); 132 132 }} 133 - className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left" 133 + className="flex w-full items-center space-x-3 px-4 py-2 text-left transition-colors hover:bg-purple-50 dark:hover:bg-slate-800" 134 134 > 135 - <Home className="w-4 h-4 text-purple-950 dark:text-cyan-50" /> 135 + <Home className="size-4 text-purple-950 dark:text-cyan-50" /> 136 136 <span className="text-purple-950 dark:text-cyan-50"> 137 137 Dashboard 138 138 </span> ··· 142 142 setShowMenu(false); 143 143 onNavigate("login"); 144 144 }} 145 - className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-purple-50 dark:hover:bg-slate-800 transition-colors text-left" 145 + className="flex w-full items-center space-x-3 px-4 py-2 text-left transition-colors hover:bg-purple-50 dark:hover:bg-slate-800" 146 146 > 147 - <Heart className="w-4 h-4 text-purple-950 dark:text-cyan-50" /> 147 + <Heart className="size-4 text-purple-950 dark:text-cyan-50" /> 148 148 <span className="text-purple-950 dark:text-cyan-50"> 149 149 Login screen 150 150 </span> ··· 154 154 setShowMenu(false); 155 155 onLogout(); 156 156 }} 157 - className="w-full flex items-center space-x-3 px-4 py-2 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-left text-red-600 dark:text-red-400" 157 + className="flex w-full items-center space-x-3 px-4 py-2 text-left text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20" 158 158 > 159 - <LogOut className="w-4 h-4" /> 159 + <LogOut className="size-4" /> 160 160 <span>Log out</span> 161 161 </button> 162 162 </div>, 163 - document.body, 163 + document.body 164 164 )} 165 165 </> 166 166 )}
+2 -2
packages/web/src/components/Firefly.tsx
··· 12 12 13 13 return ( 14 14 <div 15 - className="absolute w-1 h-1 bg-firefly-amber dark:bg-firefly-glow rounded-full opacity-40 pointer-events-none" 15 + className="pointer-events-none absolute size-1 rounded-full bg-firefly-amber opacity-40 dark:bg-firefly-glow" 16 16 style={style} 17 17 aria-hidden="true" 18 18 > 19 - <div className="absolute inset-0 bg-firefly-glow dark:bg-firefly-amber rounded-full animate-pulse blur-sm" /> 19 + <div className="absolute inset-0 animate-pulse rounded-full bg-firefly-glow blur-sm dark:bg-firefly-amber" /> 20 20 </div> 21 21 ); 22 22 }
+12 -16
packages/web/src/components/HistoryTab.tsx
··· 40 40 /> 41 41 )} 42 42 43 - <div className="flex items-center space-x-3 mb-4"> 43 + <div className="mb-4 flex items-center space-x-3"> 44 44 <div> 45 45 <h2 className="text-xl font-bold text-purple-950 dark:text-cyan-50"> 46 46 Previously Uploaded ··· 53 53 54 54 {/* Data Storage Disabled Notice */} 55 55 {!userSettings.saveData && ( 56 - <Card className="mb-4 p-4 border-orange-650/50 dark:border-amber-400/50 bg-purple-100/50 dark:bg-slate-900/50"> 56 + <Card className="mb-4 border-orange-650/50 bg-purple-100/50 p-4 dark:border-amber-400/50 dark:bg-slate-900/50"> 57 57 <div className="flex items-start space-x-3"> 58 - <Database className="w-5 h-5 text-orange-600 dark:text-amber-400 flex-shrink-0 mt-0.5" /> 58 + <Database className="mt-0.5 size-5 flex-shrink-0 text-orange-600 dark:text-amber-400" /> 59 59 <div> 60 - <h3 className="font-semibold text-purple-950 dark:text-cyan-50 mb-1"> 60 + <h3 className="mb-1 font-semibold text-purple-950 dark:text-cyan-50"> 61 61 Data Storage Disabled 62 62 </h3> 63 63 <p className="text-sm text-purple-900 dark:text-cyan-100"> ··· 91 91 ] 92 92 ]; 93 93 return ( 94 - <Card 95 - key={upload.uploadId} 96 - variant="upload" 97 - className="w-full" 98 - > 94 + <Card key={upload.uploadId} variant="upload" className="w-full"> 99 95 <CardItem 100 96 padding="p-4" 101 97 badgeIndentClass="sm:pl-[56px]" 102 98 onClick={() => onLoadUpload(upload.uploadId)} 103 99 avatar={ 104 100 <div 105 - className={`w-10 h-10 bg-gradient-to-r ${getPlatformColor(upload.sourcePlatform)} rounded-xl flex items-center justify-center flex-shrink-0 shadow-md`} 101 + className={`size-10 bg-gradient-to-r ${getPlatformColor(upload.sourcePlatform)} flex flex-shrink-0 items-center justify-center rounded-xl shadow-md`} 106 102 > 107 - <Sparkles className="w-6 h-6 text-white" /> 103 + <Sparkles className="size-6 text-white" /> 108 104 </div> 109 105 } 110 106 content={ 111 107 <> 112 108 <div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2"> 113 - <div className="font-semibold text-purple-950 dark:text-cyan-50 capitalize leading-tight"> 109 + <div className="font-semibold capitalize leading-tight text-purple-950 dark:text-cyan-50"> 114 110 {upload.sourcePlatform} 115 111 </div> 116 - <div className="flex items-center gap-2 flex-shrink-0"> 117 - <span className="text-sm text-purple-750 dark:text-cyan-250 whitespace-nowrap flex-shrink-0"> 112 + <div className="flex flex-shrink-0 items-center gap-2"> 113 + <span className="flex-shrink-0 whitespace-nowrap text-sm text-purple-750 dark:text-cyan-250"> 118 114 {upload.matchedUsers}{" "} 119 115 {upload.matchedUsers === 1 ? "match" : "matches"} 120 116 </span> ··· 126 122 target="_blank" 127 123 rel="noopener noreferrer" 128 124 onClick={(e) => e.stopPropagation()} 129 - className="text-sm text-purple-750 dark:text-cyan-250 hover:underline leading-tight flex items-center space-x-1 w-fit" 125 + className="flex w-fit items-center space-x-1 text-sm leading-tight text-purple-750 hover:underline dark:text-cyan-250" 130 126 > 131 127 <span>{destApp.action} on</span> 132 128 133 129 <FaviconIcon 134 130 url={destApp.icon} 135 131 alt={destApp.name} 136 - className="w-3 h-3 mb-0.5 flex-shrink-0" 132 + className="mb-0.5 size-3 flex-shrink-0" 137 133 /> 138 134 139 135 <span>{destApp.name}</span>
+1 -1
packages/web/src/components/PlaceholderTab.tsx
··· 13 13 }: PlaceholderTabProps) { 14 14 return ( 15 15 <div className="p-6"> 16 - <div className="flex items-center space-x-3 mb-6"> 16 + <div className="mb-6 flex items-center space-x-3"> 17 17 <h2 className="text-xl font-bold text-purple-950 dark:text-cyan-50"> 18 18 {title} 19 19 </h2>
+8 -8
packages/web/src/components/PlatformSelector.tsx
··· 8 8 onPlatformSelect, 9 9 }: PlatformSelectorProps) { 10 10 return ( 11 - <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> 11 + <div className="grid grid-cols-2 gap-3 md:grid-cols-4"> 12 12 {Object.entries(PLATFORMS).map(([key, p]) => { 13 13 const PlatformIcon = p.icon; 14 14 const isEnabled = p.enabled; ··· 17 17 key={key} 18 18 onClick={() => isEnabled && onPlatformSelect(key)} 19 19 disabled={!isEnabled} 20 - className={`relative p-4 rounded-xl border-2 transition-all ${ 20 + className={`relative rounded-xl border-2 p-4 transition-all ${ 21 21 isEnabled 22 - ? "bg-purple-100/20 dark:bg-slate-900/50 hover:bg-purple-100/40 dark:hover:bg-slate-900/70 border-orange-500/50 dark:border-amber-400/50 hover:border-amber-400 dark:hover:border-amber-400/80 hover:shadow-lg cursor-pointer" 23 - : "border-cyan-500/30 dark:border-purple-500/30 opacity-50 cursor-not-allowed bg-slate-100/30 dark:bg-slate-900/30" 22 + ? "cursor-pointer border-orange-500/50 bg-purple-100/20 hover:border-amber-400 hover:bg-purple-100/40 hover:shadow-lg dark:border-amber-400/50 dark:bg-slate-900/50 dark:hover:border-amber-400/80 dark:hover:bg-slate-900/70" 23 + : "cursor-not-allowed border-cyan-500/30 bg-slate-100/30 opacity-50 dark:border-purple-500/30 dark:bg-slate-900/30" 24 24 }`} 25 25 title={isEnabled ? `Upload ${p.name} data` : "Coming soon"} 26 26 > 27 27 <PlatformIcon 28 - className={`w-6 h-6 mx-auto mb-2 ${isEnabled ? "text-purple-750 dark:text-cyan-250" : "text-purple-750/50 dark:text-cyan-250/50"}`} 28 + className={`mx-auto mb-2 size-6 ${isEnabled ? "text-purple-750 dark:text-cyan-250" : "text-purple-750/50 dark:text-cyan-250/50"}`} 29 29 /> 30 - <div className="text-sm font-medium text-center text-purple-900 dark:text-cyan-100"> 30 + <div className="text-center text-sm font-medium text-purple-900 dark:text-cyan-100"> 31 31 {p.name} 32 32 </div> 33 33 {!isEnabled && ( 34 - <div className="absolute top-2 right-2"> 35 - <span className="text-xs bg-purple-100 dark:bg-cyan-900 text-purple-600 dark:text-cyan-400 px-2 py-0.5 rounded-full"> 34 + <div className="absolute right-2 top-2"> 35 + <span className="rounded-full bg-purple-100 px-2 py-0.5 text-xs text-purple-600 dark:bg-cyan-900 dark:text-cyan-400"> 36 36 Soon 37 37 </span> 38 38 </div>
+19 -18
packages/web/src/components/SearchResultCard.tsx
··· 42 42 content={ 43 43 <> 44 44 {match.displayName && ( 45 - <div className="font-semibold text-purple-950 dark:text-cyan-50 leading-tight"> 45 + <div className="font-semibold leading-tight text-purple-950 dark:text-cyan-50"> 46 46 {match.displayName} 47 47 </div> 48 48 )} ··· 50 50 href={`https://bsky.app/profile/${match.handle}`} 51 51 target="_blank" 52 52 rel="noopener noreferrer" 53 - className="text-sm text-purple-750 dark:text-cyan-250 hover:underline leading-tight" 53 + className="text-sm leading-tight text-purple-750 hover:underline dark:text-cyan-250" 54 54 > 55 55 @{match.handle} 56 56 </a> ··· 69 69 {typeof match.postCount === "number" && match.postCount > 0 && ( 70 70 <StatBadge value={match.postCount} label="posts" /> 71 71 )} 72 - {typeof match.followerCount === "number" && match.followerCount > 0 && ( 73 - <StatBadge value={match.followerCount} label="followers" /> 74 - )} 72 + {typeof match.followerCount === "number" && 73 + match.followerCount > 0 && ( 74 + <StatBadge value={match.followerCount} label="followers" /> 75 + )} 75 76 <Badge variant="match">{match.matchScore}% match</Badge> 76 77 </> 77 78 } ··· 94 95 }) => { 95 96 const currentApp = useMemo( 96 97 () => getAtprotoAppWithFallback(destinationAppId), 97 - [destinationAppId], 98 + [destinationAppId] 98 99 ); 99 100 100 101 const currentLexicon = useMemo( 101 102 () => currentApp?.followLexicon || "app.bsky.graph.follow", 102 - [currentApp], 103 + [currentApp] 103 104 ); 104 105 105 106 const displayMatches = useMemo( 106 107 () => 107 108 isExpanded ? result.atprotoMatches : result.atprotoMatches.slice(0, 1), 108 - [isExpanded, result.atprotoMatches], 109 + [isExpanded, result.atprotoMatches] 109 110 ); 110 111 111 112 const hasMoreMatches = result.atprotoMatches.length > 1; ··· 113 114 return ( 114 115 <Card variant="result"> 115 116 {/* Source User */} 116 - <div className="px-4 py-3 bg-purple-100 dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30"> 117 - <div className="flex justify-between gap-2 items-center"> 118 - <div className="flex-1 min-w-0"> 117 + <div className="border-b-2 border-cyan-500/30 bg-purple-100 px-4 py-3 dark:border-purple-500/30 dark:bg-slate-900"> 118 + <div className="flex items-center justify-between gap-2"> 119 + <div className="min-w-0 flex-1"> 119 120 <div className="flex flex-wrap gap-x-2 gap-y-1"> 120 - <span className="font-bold text-purple-950 dark:text-cyan-50 truncate text-base"> 121 + <span className="truncate text-base font-bold text-purple-950 dark:text-cyan-50"> 121 122 @{result.sourceUser.username} 122 123 </span> 123 124 </div> 124 125 </div> 125 - <div className="text-sm text-purple-750 dark:text-cyan-250 whitespace-nowrap flex-shrink-0"> 126 + <div className="flex-shrink-0 whitespace-nowrap text-sm text-purple-750 dark:text-cyan-250"> 126 127 {result.atprotoMatches.length}{" "} 127 128 {result.atprotoMatches.length === 1 ? "match" : "matches"} 128 129 </div> ··· 131 132 132 133 {/* ATProto Matches */} 133 134 {result.atprotoMatches.length === 0 ? ( 134 - <div className="text-center py-6"> 135 - <MessageCircle className="w-8 h-8 mx-auto mb-2 opacity-50 text-purple-750 dark:text-cyan-250" /> 135 + <div className="py-6 text-center"> 136 + <MessageCircle className="mx-auto mb-2 size-8 text-purple-750 opacity-50 dark:text-cyan-250" /> 136 137 <p className="text-sm text-purple-950 dark:text-cyan-50"> 137 138 Not found on the ATmosphere yet 138 139 </p> ··· 158 159 {hasMoreMatches && ( 159 160 <button 160 161 onClick={onToggleExpand} 161 - className="w-full py-2 text-sm text-purple-600 hover:text-purple-950 dark:text-cyan-400 dark:hover:text-cyan-50 font-medium transition-colors flex items-center justify-center space-x-1 border-t-2 border-cyan-500/30 dark:border-purple-500/30 hover:border-orange-500 dark:hover:border-amber-400/50" 162 + className="flex w-full items-center justify-center space-x-1 border-t-2 border-cyan-500/30 py-2 text-sm font-medium text-purple-600 transition-colors hover:border-orange-500 hover:text-purple-950 dark:border-purple-500/30 dark:text-cyan-400 dark:hover:border-amber-400/50 dark:hover:text-cyan-50" 162 163 > 163 164 <span> 164 165 {isExpanded ··· 166 167 : `Show ${result.atprotoMatches.length - 1} more ${result.atprotoMatches.length - 1 === 1 ? "option" : "options"}`} 167 168 </span> 168 169 <ChevronDown 169 - className={`w-4 h-4 transition-transform ${isExpanded ? "rotate-180" : ""}`} 170 + className={`size-4 transition-transform ${isExpanded ? "rotate-180" : ""}`} 170 171 /> 171 172 </button> 172 173 )} ··· 174 175 )} 175 176 </Card> 176 177 ); 177 - }, 178 + } 178 179 ); 179 180 180 181 SearchResultCard.displayName = "SearchResultCard";
+43 -43
packages/web/src/components/SetupWizard.tsx
··· 33 33 }: SetupWizardProps) { 34 34 const [wizardStep, setWizardStep] = useState(0); 35 35 const [selectedPlatforms, setSelectedPlatforms] = useState<Set<string>>( 36 - new Set(), 36 + new Set() 37 37 ); 38 38 const [platformDestinations, setPlatformDestinations] = 39 39 useState<PlatformDestinations>(currentSettings.platformDestinations); 40 40 const [saveData, setSaveData] = useState(currentSettings.saveData); 41 41 const [enableAutomation, setEnableAutomation] = useState( 42 - currentSettings.enableAutomation, 42 + currentSettings.enableAutomation 43 43 ); 44 44 const [automationFrequency, setAutomationFrequency] = useState( 45 - currentSettings.automationFrequency, 45 + currentSettings.automationFrequency 46 46 ); 47 47 48 48 if (!isOpen) return null; ··· 80 80 value: app.id, 81 81 label: app.name, 82 82 icon: app.icon, 83 - }), 83 + }) 84 84 ); 85 85 86 86 return ( 87 - <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> 87 + <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"> 88 88 <Card 89 89 variant="wizard" 90 - className="max-w-2xl w-full max-h-[90vh] flex flex-col" 90 + className="flex max-h-[90vh] w-full max-w-2xl flex-col" 91 91 > 92 92 {/* Header */} 93 - <div className="px-6 py-4 border-b-2 border-cyan-500/30 dark:border-purple-500/30 flex-shrink-0"> 94 - <div className="flex items-center justify-between mb-3"> 93 + <div className="flex-shrink-0 border-b-2 border-cyan-500/30 px-6 py-4 dark:border-purple-500/30"> 94 + <div className="mb-3 flex items-center justify-between"> 95 95 <div className="flex items-center space-x-3"> 96 - <div className="w-10 h-10 bg-firefly-banner dark:bg-firefly-banner-dark rounded-xl flex items-center justify-center shadow-md"> 97 - <Heart className="w-5 h-5 text-white" /> 96 + <div className="flex size-10 items-center justify-center rounded-xl bg-firefly-banner shadow-md dark:bg-firefly-banner-dark"> 97 + <Heart className="size-5 text-white" /> 98 98 </div> 99 99 <h2 className="text-2xl font-bold text-purple-950 dark:text-cyan-50"> 100 100 Setup Assistant ··· 102 102 </div> 103 103 <button 104 104 onClick={onClose} 105 - className="text-purple-600 dark:text-cyan-400 hover:text-purple-950 dark:hover:text-cyan-50 transition-colors" 105 + className="text-purple-600 transition-colors hover:text-purple-950 dark:text-cyan-400 dark:hover:text-cyan-50" 106 106 > 107 - <X className="w-6 h-6" /> 107 + <X className="size-6" /> 108 108 </button> 109 109 </div> 110 110 {/* Progress */} ··· 120 120 </div> 121 121 122 122 {/* Content - Scrollable */} 123 - <div className="px-6 py-4 overflow-y-auto flex-1"> 123 + <div className="flex-1 overflow-y-auto px-6 py-4"> 124 124 {wizardStep === 0 && ( 125 - <div className="text-center space-y-3"> 126 - <div className="text-6xl mb-2">👋</div> 125 + <div className="space-y-3 text-center"> 126 + <div className="mb-2 text-6xl">👋</div> 127 127 <h3 className="text-2xl font-bold text-purple-950 dark:text-cyan-50"> 128 128 Welcome to ATlast! 129 129 </h3> 130 - <p className="text-purple-750 dark:text-cyan-250 max-w-md mx-auto"> 130 + <p className="mx-auto max-w-md text-purple-750 dark:text-cyan-250"> 131 131 Let's get you set up in just a few steps. We'll help you 132 132 configure how you want to reconnect with your community on the 133 133 ATmosphere. ··· 144 144 Select one or more platforms you follow people on. We'll help 145 145 you find them on the ATmosphere. 146 146 </p> 147 - <div className="grid grid-cols-3 gap-3 mt-3"> 147 + <div className="mt-3 grid grid-cols-3 gap-3"> 148 148 {Object.entries(PLATFORMS).map(([key, p]) => { 149 149 const isSelected = selectedPlatforms.has(key); 150 150 return ( 151 151 <button 152 152 key={key} 153 153 onClick={() => togglePlatform(key)} 154 - className={`p-4 rounded-xl border-2 transition-all relative ${ 154 + className={`relative rounded-xl border-2 p-4 transition-all ${ 155 155 isSelected 156 - ? "bg-purple-100/50 dark:bg-slate-950/50 border-2 border-purple-500 dark:border-cyan-500 text-purple-950 dark:text-cyan-50" 157 - : "border-cyan-500/30 dark:border-purple-500/30 hover:bg-purple-100/50 dark:hover:bg-slate-950/50 hover:border-orange-500 dark:hover:border-amber-400" 156 + ? "border-2 border-purple-500 bg-purple-100/50 text-purple-950 dark:border-cyan-500 dark:bg-slate-950/50 dark:text-cyan-50" 157 + : "border-cyan-500/30 hover:border-orange-500 hover:bg-purple-100/50 dark:border-purple-500/30 dark:hover:border-amber-400 dark:hover:bg-slate-950/50" 158 158 }`} 159 159 > 160 160 {isSelected && ( 161 - <div className="absolute -top-2 -right-2 w-6 h-6 bg-orange-500 dark:bg-amber-400 rounded-full flex items-center justify-center shadow-md"> 162 - <Check className="w-4 h-4 text-white dark:text-slate-900" /> 161 + <div className="absolute -right-2 -top-2 flex size-6 items-center justify-center rounded-full bg-orange-500 shadow-md dark:bg-amber-400"> 162 + <Check className="size-4 text-white dark:text-slate-900" /> 163 163 </div> 164 164 )} 165 165 <PlatformBadge 166 166 platformKey={key} 167 167 showName={false} 168 168 size="lg" 169 - className="justify-center mb-2" 169 + className="mb-2 justify-center" 170 170 /> 171 171 <div className="text-sm font-medium text-purple-950 dark:text-cyan-50"> 172 172 {p.name} ··· 176 176 })} 177 177 </div> 178 178 {selectedPlatforms.size > 0 && ( 179 - <div className="mt-3 px-3 py-2 rounded-lg border border-orange-650/30 dark:border-amber-400/30"> 179 + <div className="mt-3 rounded-lg border border-orange-650/30 px-3 py-2 dark:border-amber-400/30"> 180 180 <p className="text-sm text-purple-750 dark:text-cyan-250"> 181 181 ✨ {selectedPlatforms.size} platform 182 182 {selectedPlatforms.size !== 1 ? "s" : ""} selected ··· 195 195 Choose which ATmosphere app to use for each platform. You can 196 196 change this later. 197 197 </p> 198 - <div className="space-y-4 mt-3"> 198 + <div className="mt-3 space-y-4"> 199 199 {platformsToShow.map(([key, p]) => { 200 200 return ( 201 201 <div 202 202 key={key} 203 - className="flex items-center gap-3 px-3 max-w-lg mx-sm" 203 + className="mx-sm flex max-w-lg items-center gap-3 px-3" 204 204 > 205 205 <PlatformBadge platformKey={key} size="sm" /> 206 206 <DropdownWithIcons ··· 227 227 {wizardStep === 3 && ( 228 228 <div className="space-y-3"> 229 229 <div> 230 - <h3 className="text-xl font-bold text-purple-950 dark:text-cyan-50 mb-1"> 230 + <h3 className="mb-1 text-xl font-bold text-purple-950 dark:text-cyan-50"> 231 231 Privacy & Automation 232 232 </h3> 233 233 <p className="text-sm text-purple-750 dark:text-cyan-250"> ··· 257 257 value={automationFrequency} 258 258 onChange={(e) => 259 259 setAutomationFrequency( 260 - e.target.value as "Weekly" | "Monthly" | "Quarterly", 260 + e.target.value as "Weekly" | "Monthly" | "Quarterly" 261 261 ) 262 262 } 263 - className="mt-3 ml-auto max-w-xs px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm w-full text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors" 263 + className="ml-auto mt-3 w-full max-w-xs rounded-lg border border-cyan-500/30 bg-white px-3 py-2 text-sm text-purple-950 transition-colors hover:border-cyan-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-purple-500/30 dark:bg-slate-800 dark:text-cyan-50 dark:hover:border-purple-400 dark:focus:ring-amber-400" 264 264 > 265 265 <option value="daily">Check daily</option> 266 266 <option value="weekly">Check weekly</option> ··· 273 273 )} 274 274 275 275 {wizardStep === 4 && ( 276 - <div className="text-center space-y-3"> 277 - <div className="text-6xl mb-2">🎉</div> 276 + <div className="space-y-3 text-center"> 277 + <div className="mb-2 text-6xl">🎉</div> 278 278 <h3 className="text-2xl font-bold text-purple-950 dark:text-cyan-50"> 279 279 You're all set! 280 280 </h3> 281 - <p className="text-purple-750 dark:text-cyan-250 max-w-md mx-auto"> 281 + <p className="mx-auto max-w-md text-purple-750 dark:text-cyan-250"> 282 282 Your preferences have been saved. You can change them anytime in 283 283 Settings. 284 284 </p> 285 - <div className="px-4 py-3 mt-3"> 286 - <h4 className="font-semibold text-purple-950 dark:text-cyan-50 mb-2"> 285 + <div className="mt-3 px-4 py-3"> 286 + <h4 className="mb-2 font-semibold text-purple-950 dark:text-cyan-50"> 287 287 Quick Summary: 288 288 </h4> 289 - <ul className="text-sm text-purple-750 dark:text-cyan-250 space-y-1 text-left max-w-sm mx-auto"> 289 + <ul className="mx-auto max-w-sm space-y-1 text-left text-sm text-purple-750 dark:text-cyan-250"> 290 290 <li className="flex items-center space-x-2"> 291 - <Check className="w-4 h-4 text-orange-500" /> 291 + <Check className="size-4 text-orange-500" /> 292 292 <span> 293 293 Data Storage: {saveData ? "Enabled" : "Disabled"} 294 294 </span> 295 295 </li> 296 296 <li className="flex items-center space-x-2"> 297 - <Check className="w-4 h-4 text-orange-500" /> 297 + <Check className="size-4 text-orange-500" /> 298 298 <span> 299 299 Automation: {enableAutomation ? "Enabled" : "Disabled"} 300 300 </span> 301 301 </li> 302 302 <li className="flex items-center space-x-2"> 303 - <Check className="w-4 h-4 text-orange-500" /> 303 + <Check className="size-4 text-orange-500" /> 304 304 <span> 305 305 Platforms:{" "} 306 306 {selectedPlatforms.size > 0 ··· 310 310 </span> 311 311 </li> 312 312 <li className="flex items-center space-x-2"> 313 - <Check className="w-4 h-4 text-orange-500" /> 313 + <Check className="size-4 text-orange-500" /> 314 314 <span>Ready to upload your first file!</span> 315 315 </li> 316 316 </ul> ··· 320 320 </div> 321 321 322 322 {/* Footer */} 323 - <div className="px-6 py-4 border-t-2 border-cyan-500/30 dark:border-purple-500/30 flex items-center justify-between flex-shrink-0"> 323 + <div className="flex flex-shrink-0 items-center justify-between border-t-2 border-cyan-500/30 px-6 py-4 dark:border-purple-500/30"> 324 324 <button 325 325 onClick={() => wizardStep > 0 && setWizardStep(wizardStep - 1)} 326 326 disabled={wizardStep === 0} 327 - className="px-4 py-2 text-purple-750 dark:text-cyan-250 hover:text-purple-950 dark:hover:text-cyan-50 disabled:opacity-30 disabled:cursor-not-allowed transition-colors" 327 + className="px-4 py-2 text-purple-750 transition-colors hover:text-purple-950 disabled:cursor-not-allowed disabled:opacity-30 dark:text-cyan-250 dark:hover:text-cyan-50" 328 328 > 329 329 Back 330 330 </button> ··· 336 336 handleComplete(); 337 337 } 338 338 }} 339 - className="px-6 py-2 bg-orange-600 hover:bg-orange-500 text-white rounded-lg font-medium shadow-md hover:shadow-lg transition-all flex items-center space-x-2" 339 + className="flex items-center space-x-2 rounded-lg bg-orange-600 px-6 py-2 font-medium text-white shadow-md transition-all hover:bg-orange-500 hover:shadow-lg" 340 340 > 341 341 <span> 342 342 {wizardStep === wizardSteps.length - 1 ? "Get Started" : "Next"} 343 343 </span> 344 344 {wizardStep < wizardSteps.length - 1 && ( 345 - <ChevronRight className="w-4 h-4" /> 345 + <ChevronRight className="size-4" /> 346 346 )} 347 347 </button> 348 348 </div>
+6 -6
packages/web/src/components/TabNavigation.tsx
··· 33 33 onTabChange, 34 34 }: TabNavigationProps) { 35 35 return ( 36 - <div className="overflow-x-auto scrollbar-hide px-4"> 37 - <div className="flex space-x-1 border-b-2 border-cyan-500/30 dark:border-purple-500/30 min-w-max"> 36 + <div className="scrollbar-hide overflow-x-auto px-4"> 37 + <div className="flex min-w-max space-x-1 border-b-2 border-cyan-500/30 dark:border-purple-500/30"> 38 38 {tabs.map((tab) => { 39 39 const Icon = tab.icon; 40 40 return ( 41 41 <button 42 42 key={tab.id} 43 43 onClick={() => onTabChange(tab.id)} 44 - className={`flex items-center space-x-2 px-4 py-3 border-b-2 transition-all whitespace-nowrap ${ 44 + className={`flex items-center space-x-2 whitespace-nowrap border-b-2 px-4 py-3 transition-all ${ 45 45 activeTab === tab.id 46 - ? "border-orange-500 dark:border-orange-400 text-orange-650 dark:text-amber-400" 47 - : "border-transparent text-purple-750 dark:text-cyan-250 hover:text-purple-900 dark:hover:text-cyan-100" 46 + ? "border-orange-500 text-orange-650 dark:border-orange-400 dark:text-amber-400" 47 + : "border-transparent text-purple-750 hover:text-purple-900 dark:text-cyan-250 dark:hover:text-cyan-100" 48 48 }`} 49 49 > 50 - <Icon className="w-4 h-4" /> 50 + <Icon className="size-4" /> 51 51 <span className="font-medium">{tab.label}</span> 52 52 </button> 53 53 );
+6 -6
packages/web/src/components/ThemeControls.tsx
··· 17 17 <div className="flex items-center space-x-2"> 18 18 <button 19 19 onClick={onToggleMotion} 20 - className="p-2 bg-white/90 dark:bg-slate-800/90 backdrop-blur-sm rounded-lg border border-slate-200 dark:border-slate-700 hover:bg-white dark:hover:bg-slate-700 transition-colors shadow-lg" 20 + className="rounded-lg border border-slate-200 bg-white/90 p-2 shadow-lg backdrop-blur-sm transition-colors hover:bg-white dark:border-slate-700 dark:bg-slate-800/90 dark:hover:bg-slate-700" 21 21 aria-label={reducedMotion ? "Enable animations" : "Reduce motion"} 22 22 title={reducedMotion ? "Enable animations" : "Reduce motion"} 23 23 > 24 24 {reducedMotion ? ( 25 - <Play className="w-5 h-5 text-slate-700 dark:text-slate-300" /> 25 + <Play className="size-5 text-slate-700 dark:text-slate-300" /> 26 26 ) : ( 27 - <Pause className="w-5 h-5 text-slate-700 dark:text-slate-300" /> 27 + <Pause className="size-5 text-slate-700 dark:text-slate-300" /> 28 28 )} 29 29 </button> 30 30 <button 31 31 onClick={onToggleTheme} 32 - className="p-2 bg-white/90 dark:bg-slate-800/90 backdrop-blur-sm rounded-lg border border-slate-200 dark:border-slate-700 hover:bg-white dark:hover:bg-slate-700 transition-colors shadow-lg" 32 + className="rounded-lg border border-slate-200 bg-white/90 p-2 shadow-lg backdrop-blur-sm transition-colors hover:bg-white dark:border-slate-700 dark:bg-slate-800/90 dark:hover:bg-slate-700" 33 33 aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"} 34 34 > 35 35 {isDark ? ( 36 - <Sun className="w-5 h-5 text-firefly-amber" /> 36 + <Sun className="size-5 text-firefly-amber" /> 37 37 ) : ( 38 - <Moon className="w-5 h-5 text-slate-700" /> 38 + <Moon className="size-5 text-slate-700" /> 39 39 )} 40 40 </button> 41 41 </div>
+1 -1
packages/web/src/components/UploadTab.tsx
··· 10 10 onPlatformSelect: (platform: string) => void; 11 11 onFileUpload: ( 12 12 e: React.ChangeEvent<HTMLInputElement>, 13 - platform: string, 13 + platform: string 14 14 ) => void; 15 15 selectedPlatform: string; 16 16 }
+2 -1
packages/web/src/components/VirtualizedResultsList.tsx
··· 31 31 // Enable dynamic measurement - virtualizer will measure actual rendered heights 32 32 // and adjust positioning automatically 33 33 measureElement: 34 - typeof window !== "undefined" && navigator.userAgent.indexOf("Firefox") === -1 34 + typeof window !== "undefined" && 35 + navigator.userAgent.indexOf("Firefox") === -1 35 36 ? (element) => element.getBoundingClientRect().height 36 37 : undefined, 37 38 });
+3 -3
packages/web/src/components/common/AvatarWithFallback.tsx
··· 23 23 if (!avatar || imageError) { 24 24 return ( 25 25 <div 26 - className={`${container} bg-gradient-to-br from-cyan-400 to-purple-500 rounded-full flex items-center justify-center shadow-sm ${className}`} 26 + className={`${container} flex items-center justify-center rounded-full bg-gradient-to-br from-cyan-400 to-purple-500 shadow-sm ${className}`} 27 27 aria-label={`${handle}'s avatar`} 28 28 > 29 - <span className={`text-white font-bold ${text}`}> 29 + <span className={`font-bold text-white ${text}`}> 30 30 {fallbackInitial} 31 31 </span> 32 32 </div> ··· 42 42 loading="lazy" 43 43 /> 44 44 ); 45 - }, 45 + } 46 46 ); 47 47 48 48 AvatarWithFallback.displayName = "AvatarWithFallback";
+4 -4
packages/web/src/components/common/Button.tsx
··· 21 21 disabled, 22 22 ...props 23 23 }, 24 - ref, 24 + ref 25 25 ) => { 26 26 const baseStyles = 27 27 "inline-flex items-center justify-center font-semibold transition-all focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"; ··· 50 50 {...props} 51 51 > 52 52 {isLoading ? ( 53 - <div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" /> 53 + <div className="mr-2 size-5 animate-spin rounded-full border-2 border-white border-t-transparent" /> 54 54 ) : Icon ? ( 55 - <Icon className="w-5 h-5 mr-2" /> 55 + <Icon className="mr-2 size-5" /> 56 56 ) : null} 57 57 {children} 58 58 </button> 59 59 ); 60 - }, 60 + } 61 61 ); 62 62 63 63 Button.displayName = "Button";
+7 -5
packages/web/src/components/common/CardItem.tsx
··· 33 33 }) => { 34 34 return ( 35 35 <div 36 - className={`${padding} cursor-pointer hover:scale-[1.01] transition-transform`} 36 + className={`${padding} cursor-pointer transition-transform hover:scale-[1.01]`} 37 37 onClick={onClick} 38 38 > 39 39 {/* Top row: Avatar + Content + Action */} 40 - <div className="flex items-start gap-3 mb-1"> 40 + <div className="mb-1 flex items-start gap-3"> 41 41 {avatar} 42 - <div className="flex-1 min-w-0">{content}</div> 42 + <div className="min-w-0 flex-1">{content}</div> 43 43 {action} 44 44 </div> 45 45 46 46 {/* Badges row - responsive indent based on avatar size */} 47 - <div className={`flex items-center flex-wrap gap-2 pl-0 ${badgeIndentClass}`}> 47 + <div 48 + className={`flex flex-wrap items-center gap-2 pl-0 ${badgeIndentClass}`} 49 + > 48 50 {badges} 49 51 </div> 50 52 51 53 {/* Optional description - same responsive indent as badges */} 52 54 {description && ( 53 55 <div 54 - className={`text-sm text-purple-900 dark:text-cyan-100 line-clamp-2 mt-1 pt-2 pl-1 ${badgeIndentClass}`} 56 + className={`mt-1 line-clamp-2 pl-1 pt-2 text-sm text-purple-900 dark:text-cyan-100 ${badgeIndentClass}`} 55 57 > 56 58 {description} 57 59 </div>
+1 -1
packages/web/src/components/common/Dropdown.tsx
··· 25 25 <select 26 26 value={value} 27 27 onChange={(e) => onChange(e.target.value)} 28 - className={`px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors ${fullWidth ? "w-full" : ""} ${className}`} 28 + className={`rounded-lg border border-cyan-500/30 bg-white px-3 py-2 text-sm text-purple-950 transition-colors hover:border-cyan-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-purple-500/30 dark:bg-slate-800 dark:text-cyan-50 dark:hover:border-purple-400 dark:focus:ring-amber-400 ${fullWidth ? "w-full" : ""} ${className}`} 29 29 > 30 30 {options.map((option) => ( 31 31 <option key={option.value} value={option.value}>
+12 -8
packages/web/src/components/common/DropdownWithIcons.tsx
··· 58 58 <select 59 59 value={value} 60 60 onChange={(e) => onChange(e.target.value)} 61 - className={`px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors ${className}`} 61 + className={`rounded-lg border border-cyan-500/30 bg-white px-3 py-2 text-sm text-purple-950 transition-colors hover:border-cyan-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-purple-500/30 dark:bg-slate-800 dark:text-cyan-50 dark:hover:border-purple-400 dark:focus:ring-amber-400 ${className}`} 62 62 > 63 63 {options.map((option) => ( 64 64 <option key={option.value} value={option.value}> ··· 75 75 <button 76 76 type="button" 77 77 onClick={() => setIsOpen(!isOpen)} 78 - className="w-full px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors flex items-center justify-between" 78 + className="flex w-full items-center justify-between rounded-lg border border-cyan-500/30 bg-white px-3 py-2 text-sm text-purple-950 transition-colors hover:border-cyan-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-purple-500/30 dark:bg-slate-800 dark:text-cyan-50 dark:hover:border-purple-400 dark:focus:ring-amber-400" 79 79 > 80 80 <span className="flex items-center gap-2"> 81 81 {selectedOption?.icon && ( 82 82 <img 83 83 src={selectedOption.icon} 84 84 alt="" 85 - className="w-4 h-4 flex-shrink-0" 85 + className="size-4 flex-shrink-0" 86 86 /> 87 87 )} 88 88 <span>{selectedOption?.label || placeholder}</span> 89 89 </span> 90 90 <ChevronDown 91 - className={`w-4 h-4 transition-transform ${isOpen ? "rotate-180" : ""}`} 91 + className={`size-4 transition-transform ${isOpen ? "rotate-180" : ""}`} 92 92 /> 93 93 </button> 94 94 95 95 {isOpen && ( 96 - <div className="absolute z-10 w-full mt-1 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg shadow-lg max-h-60 overflow-auto"> 96 + <div className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-lg border border-cyan-500/30 bg-white shadow-lg dark:border-purple-500/30 dark:bg-slate-800"> 97 97 {options.map((option) => ( 98 98 <button 99 99 key={option.value} ··· 102 102 onChange(option.value); 103 103 setIsOpen(false); 104 104 }} 105 - className="w-full px-3 py-2 text-sm text-left hover:bg-purple-100/50 dark:hover:bg-slate-700/50 transition-colors flex items-center gap-2 relative" 105 + className="relative flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors hover:bg-purple-100/50 dark:hover:bg-slate-700/50" 106 106 > 107 107 {option.icon && ( 108 - <img src={option.icon} alt="" className="w-4 h-4 flex-shrink-0" /> 108 + <img 109 + src={option.icon} 110 + alt="" 111 + className="size-4 flex-shrink-0" 112 + /> 109 113 )} 110 114 <span className="flex-1 text-purple-950 dark:text-cyan-50"> 111 115 {option.label} 112 116 </span> 113 117 {option.value === value && ( 114 - <Check className="w-4 h-4 text-orange-500 dark:text-amber-400" /> 118 + <Check className="size-4 text-orange-500 dark:text-amber-400" /> 115 119 )} 116 120 </button> 117 121 ))}
+4 -4
packages/web/src/components/common/EmptyState.tsx
··· 15 15 className = "", 16 16 }) => { 17 17 return ( 18 - <div className={`text-center py-12 ${className}`}> 19 - <Icon className="w-16 h-16 text-purple-900 dark:text-cyan-100 mx-auto mb-4" /> 20 - <p className="text-purple-750 dark:text-cyan-250 font-medium">{title}</p> 18 + <div className={`py-12 text-center ${className}`}> 19 + <Icon className="mx-auto mb-4 size-16 text-purple-900 dark:text-cyan-100" /> 20 + <p className="font-medium text-purple-750 dark:text-cyan-250">{title}</p> 21 21 {message && ( 22 - <p className="text-sm text-purple-950 dark:text-cyan-50 mt-2"> 22 + <p className="mt-2 text-sm text-purple-950 dark:text-cyan-50"> 23 23 {message} 24 24 </p> 25 25 )}
+19 -19
packages/web/src/components/common/ErrorBoundary.tsx
··· 56 56 57 57 if (fallbackType === "inline") { 58 58 return ( 59 - <div className="p-4 bg-red-50 dark:bg-red-900/20 border-2 border-red-500 rounded-xl"> 59 + <div className="rounded-xl border-2 border-red-500 bg-red-50 p-4 dark:bg-red-900/20"> 60 60 <div className="flex items-start gap-3"> 61 - <AlertTriangle className="w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" /> 61 + <AlertTriangle className="mt-0.5 size-5 flex-shrink-0 text-red-600 dark:text-red-400" /> 62 62 <div className="flex-1"> 63 - <h3 className="font-semibold text-red-900 dark:text-red-100 mb-1"> 63 + <h3 className="mb-1 font-semibold text-red-900 dark:text-red-100"> 64 64 Something went wrong 65 65 </h3> 66 - <p className="text-sm text-red-800 dark:text-red-200 mb-3"> 66 + <p className="mb-3 text-sm text-red-800 dark:text-red-200"> 67 67 {this.state.error?.message || "An unexpected error occurred"} 68 68 </p> 69 69 <button 70 70 onClick={this.handleReset} 71 - className="inline-flex items-center gap-2 px-3 py-1.5 bg-red-600 hover:bg-red-700 text-white text-sm font-medium rounded-lg transition-colors" 71 + className="inline-flex items-center gap-2 rounded-lg bg-red-600 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-red-700" 72 72 > 73 - <RefreshCw className="w-4 h-4" /> 73 + <RefreshCw className="size-4" /> 74 74 Try Again 75 75 </button> 76 76 </div> ··· 81 81 82 82 // Full page error 83 83 return ( 84 - <div className="min-h-screen flex items-center justify-center p-4 bg-gradient-to-br from-cyan-50 via-purple-50 to-pink-50 dark:from-indigo-950 dark:via-purple-900 dark:to-slate-900"> 85 - <div className="max-w-md w-full bg-white dark:bg-slate-900 rounded-3xl shadow-2xl p-8 border-2 border-red-500"> 86 - <div className="flex justify-center mb-6"> 87 - <div className="w-16 h-16 bg-red-100 dark:bg-red-900/30 rounded-2xl flex items-center justify-center"> 88 - <AlertTriangle className="w-8 h-8 text-red-600 dark:text-red-400" /> 84 + <div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-cyan-50 via-purple-50 to-pink-50 p-4 dark:from-indigo-950 dark:via-purple-900 dark:to-slate-900"> 85 + <div className="w-full max-w-md rounded-3xl border-2 border-red-500 bg-white p-8 shadow-2xl dark:bg-slate-900"> 86 + <div className="mb-6 flex justify-center"> 87 + <div className="flex size-16 items-center justify-center rounded-2xl bg-red-100 dark:bg-red-900/30"> 88 + <AlertTriangle className="size-8 text-red-600 dark:text-red-400" /> 89 89 </div> 90 90 </div> 91 91 92 - <h1 className="text-2xl font-bold text-center text-purple-950 dark:text-cyan-50 mb-2"> 92 + <h1 className="mb-2 text-center text-2xl font-bold text-purple-950 dark:text-cyan-50"> 93 93 Oops! Something went wrong 94 94 </h1> 95 95 96 - <p className="text-center text-purple-750 dark:text-cyan-250 mb-6"> 96 + <p className="mb-6 text-center text-purple-750 dark:text-cyan-250"> 97 97 We encountered an unexpected error. Don't worry, your data is 98 98 safe. 99 99 </p> 100 100 101 101 {process.env.NODE_ENV === "development" && this.state.error && ( 102 - <details className="mb-6 p-4 bg-slate-100 dark:bg-slate-800 rounded-lg text-xs"> 103 - <summary className="cursor-pointer font-semibold text-purple-900 dark:text-cyan-100 mb-2"> 102 + <details className="mb-6 rounded-lg bg-slate-100 p-4 text-xs dark:bg-slate-800"> 103 + <summary className="mb-2 cursor-pointer font-semibold text-purple-900 dark:text-cyan-100"> 104 104 Error Details (Dev Only) 105 105 </summary> 106 106 <pre className="overflow-auto text-red-600 dark:text-red-400"> ··· 113 113 <div className="flex gap-3"> 114 114 <button 115 115 onClick={this.handleReset} 116 - className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-orange-600 hover:bg-orange-500 text-white font-semibold rounded-xl transition-colors" 116 + className="flex flex-1 items-center justify-center gap-2 rounded-xl bg-orange-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-orange-500" 117 117 > 118 - <RefreshCw className="w-5 h-5" /> 118 + <RefreshCw className="size-5" /> 119 119 Try Again 120 120 </button> 121 121 <button 122 122 onClick={this.handleGoHome} 123 - className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-slate-600 hover:bg-slate-700 text-white font-semibold rounded-xl transition-colors" 123 + className="flex flex-1 items-center justify-center gap-2 rounded-xl bg-slate-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-slate-700" 124 124 > 125 - <Home className="w-5 h-5" /> 125 + <Home className="size-5" /> 126 126 Go Home 127 127 </button> 128 128 </div>
+3 -3
packages/web/src/components/common/FollowButton.tsx
··· 36 36 <button 37 37 onClick={onToggle} 38 38 disabled={disabled || isFollowed} 39 - className={`p-2 rounded-full font-medium transition-all flex-shrink-0 self-start ${getButtonStyles()}`} 39 + className={`flex-shrink-0 self-start rounded-full p-2 font-medium transition-all ${getButtonStyles()}`} 40 40 title={getTitle()} 41 41 aria-label={getTitle()} 42 42 > 43 - <Icon className="w-4 h-4" /> 43 + <Icon className="size-4" /> 44 44 </button> 45 45 ); 46 - }, 46 + } 47 47 ); 48 48 49 49 FollowButton.displayName = "FollowButton";
+2 -2
packages/web/src/components/common/IconButton.tsx
··· 20 20 className = "", 21 21 ...props 22 22 }, 23 - ref, 23 + ref 24 24 ) => { 25 25 const baseStyles = 26 26 "inline-flex items-center justify-center transition-all focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed"; ··· 66 66 )} 67 67 </button> 68 68 ); 69 - }, 69 + } 70 70 ); 71 71 72 72 IconButton.displayName = "IconButton";
+5 -5
packages/web/src/components/common/LoadingSkeleton.tsx
··· 3 3 4 4 export const SearchResultSkeleton: React.FC = () => { 5 5 return ( 6 - <div className="bg-white/95 dark:bg-slate-800/95 backdrop-blur-xl rounded-2xl shadow-sm overflow-hidden border-2 border-slate-200 dark:border-slate-700"> 6 + <div className="overflow-hidden rounded-2xl border-2 border-slate-200 bg-white/95 shadow-sm backdrop-blur-xl dark:border-slate-700 dark:bg-slate-800/95"> 7 7 {/* Source User Skeleton */} 8 - <div className="p-4 bg-slate-50 dark:bg-slate-900/50 border-b-2 border-slate-200 dark:border-slate-700"> 8 + <div className="border-b-2 border-slate-200 bg-slate-50 p-4 dark:border-slate-700 dark:bg-slate-900/50"> 9 9 <div className="flex items-center space-x-3"> 10 10 <Skeleton variant="circular" width={40} height={40} /> 11 11 <div className="flex-1 space-y-2"> ··· 18 18 19 19 {/* Match Skeleton */} 20 20 <div className="p-4"> 21 - <div className="flex items-start space-x-3 p-3 rounded-xl bg-amber-50 dark:bg-amber-900/10 border-2 border-amber-200 dark:border-amber-800/30"> 21 + <div className="flex items-start space-x-3 rounded-xl border-2 border-amber-200 bg-amber-50 p-3 dark:border-amber-800/30 dark:bg-amber-900/10"> 22 22 <Skeleton variant="circular" width={48} height={48} /> 23 23 <div className="flex-1 space-y-2"> 24 24 <Skeleton height={16} width="75%" /> 25 25 <Skeleton height={12} width="50%" /> 26 26 <Skeleton height={12} width="100%" /> 27 - <div className="flex gap-2 mt-2"> 27 + <div className="mt-2 flex gap-2"> 28 28 <Skeleton height={20} width={80} className="rounded-full" /> 29 29 <Skeleton height={20} width={100} className="rounded-full" /> 30 30 </div> ··· 38 38 39 39 export const UploadHistorySkeleton: React.FC = () => { 40 40 return ( 41 - <div className="flex items-center space-x-4 p-4 bg-purple-100/50 dark:bg-slate-900/50 rounded-xl border-2 border-purple-500/30 dark:border-cyan-500/30"> 41 + <div className="flex items-center space-x-4 rounded-xl border-2 border-purple-500/30 bg-purple-100/50 p-4 dark:border-cyan-500/30 dark:bg-slate-900/50"> 42 42 <Skeleton variant="rectangular" width={48} height={48} /> 43 43 <div className="flex-1 space-y-2"> 44 44 <Skeleton height={16} width="60%" />
+4 -4
packages/web/src/components/common/Notification.tsx
··· 44 44 45 45 return ( 46 46 <div 47 - className={`flex items-start gap-3 p-4 rounded-xl border-2 shadow-lg ${styleMap[type]} animate-slide-in`} 47 + className={`flex items-start gap-3 rounded-xl border-2 p-4 shadow-lg ${styleMap[type]} animate-slide-in`} 48 48 role="alert" 49 49 > 50 - <Icon className="w-5 h-5 flex-shrink-0 mt-0.5" /> 50 + <Icon className="mt-0.5 size-5 flex-shrink-0" /> 51 51 <p className="flex-1 text-sm font-medium">{message}</p> 52 52 <button 53 53 onClick={onClose} 54 - className="flex-shrink-0 hover:opacity-70 transition-opacity" 54 + className="flex-shrink-0 transition-opacity hover:opacity-70" 55 55 aria-label="Close notification" 56 56 > 57 - <X className="w-5 h-5" /> 57 + <X className="size-5" /> 58 58 </button> 59 59 </div> 60 60 );
+2 -2
packages/web/src/components/common/NotificationContainer.tsx
··· 21 21 22 22 return createPortal( 23 23 <div 24 - className="fixed top-4 right-4 z-[9999] flex flex-col gap-2 max-w-md" 24 + className="fixed right-4 top-4 z-[9999] flex max-w-md flex-col gap-2" 25 25 aria-live="polite" 26 26 aria-atomic="false" 27 27 > ··· 34 34 /> 35 35 ))} 36 36 </div>, 37 - document.body, 37 + document.body 38 38 ); 39 39 }; 40 40
+1 -1
packages/web/src/components/common/PlatformBadge.tsx
··· 40 40 )} 41 41 {showName && ( 42 42 <span 43 - className={`font-medium text-purple-950 dark:text-cyan-50 capitalize ${textSizes[size]}`} 43 + className={`font-medium capitalize text-purple-950 dark:text-cyan-50 ${textSizes[size]}`} 44 44 > 45 45 {platform.name} 46 46 </span>
+1 -1
packages/web/src/components/common/ProgressBar.tsx
··· 56 56 return ( 57 57 <div className={className}> 58 58 {showLabel && ( 59 - <div className="text-sm text-purple-750 dark:text-cyan-250 mb-2"> 59 + <div className="mb-2 text-sm text-purple-750 dark:text-cyan-250"> 60 60 {percentage}% complete 61 61 </div> 62 62 )}
+1 -1
packages/web/src/components/common/Section.tsx
··· 23 23 24 24 return ( 25 25 <div className={`${containerClasses} ${className}`}> 26 - <div className="flex items-start justify-between mb-4"> 26 + <div className="mb-4 flex items-start justify-between"> 27 27 <div className="flex items-center space-x-3"> 28 28 <div> 29 29 <h2 className="text-xl font-bold text-purple-950 dark:text-cyan-50">
+7 -7
packages/web/src/components/common/SetupPrompt.tsx
··· 21 21 if (variant === "banner") { 22 22 return ( 23 23 <div 24 - className={`bg-firefly-banner-dark dark:bg-firefly-banner-dark rounded-2xl p-6 text-white mb-3 ${className}`} 24 + className={`mb-3 rounded-2xl bg-firefly-banner-dark p-6 text-white dark:bg-firefly-banner-dark ${className}`} 25 25 > 26 - <div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4"> 26 + <div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center"> 27 27 <div className="flex-1"> 28 - <h2 className="text-2xl font-bold mb-2"> 28 + <h2 className="mb-2 text-2xl font-bold"> 29 29 Need help getting started? 30 30 </h2> 31 31 <p className="text-white"> ··· 34 34 </div> 35 35 <button 36 36 onClick={onShowWizard} 37 - className="bg-white text-slate-900 px-6 py-3 rounded-xl font-semibold hover:bg-slate-100 transition-all flex items-center space-x-2 whitespace-nowrap shadow-lg" 37 + className="flex items-center space-x-2 whitespace-nowrap rounded-xl bg-white px-6 py-3 font-semibold text-slate-900 shadow-lg transition-all hover:bg-slate-100" 38 38 > 39 39 <span>Start Setup</span> 40 - <ChevronRight className="w-4 h-4" /> 40 + <ChevronRight className="size-4" /> 41 41 </button> 42 42 </div> 43 43 </div> ··· 47 47 return ( 48 48 <button 49 49 onClick={onShowWizard} 50 - className={`text-md text-orange-650 hover:text-orange-500 dark:text-amber-400 dark:hover:text-amber-300 font-medium transition-colors flex items-center space-x-1 ${className}`} 50 + className={`text-md flex items-center space-x-1 font-medium text-orange-650 transition-colors hover:text-orange-500 dark:text-amber-400 dark:hover:text-amber-300 ${className}`} 51 51 > 52 - <Settings className="w-4 h-4" /> 52 + <Settings className="size-4" /> 53 53 <span>Configure</span> 54 54 </button> 55 55 );
+1 -1
packages/web/src/components/common/Stats.tsx
··· 31 31 > 32 32 {formattedValue} 33 33 </div> 34 - <div className="text-sm text-slate-700 dark:text-slate-300 font-medium"> 34 + <div className="text-sm font-medium text-slate-700 dark:text-slate-300"> 35 35 {label} 36 36 </div> 37 37 </div>
+4 -4
packages/web/src/components/common/Toggle.tsx
··· 22 22 return ( 23 23 <div className="flex items-start justify-between"> 24 24 <div className="flex-1"> 25 - <div className="font-medium text-purple-950 dark:text-cyan-50 mb-1"> 25 + <div className="mb-1 font-medium text-purple-950 dark:text-cyan-50"> 26 26 {label} 27 27 </div> 28 28 {description && ( ··· 31 31 </p> 32 32 )} 33 33 </div> 34 - <label className="relative inline-flex items-center cursor-pointer ml-4"> 34 + <label className="relative ml-4 inline-flex cursor-pointer items-center"> 35 35 <input 36 36 type="checkbox" 37 37 id={toggleId} 38 38 checked={checked} 39 39 onChange={(e) => onChange(e.target.checked)} 40 40 disabled={disabled} 41 - className="sr-only peer" 41 + className="peer sr-only" 42 42 /> 43 - <div className="w-11 h-6 bg-gray-400 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-orange-650/50 dark:peer-focus:ring-amber-400/50 rounded-full peer dark:bg-gray-600 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-700 peer-checked:bg-orange-500 dark:peer-checked:bg-orange-400 peer-disabled:opacity-50 peer-disabled:cursor-not-allowed"></div> 43 + <div className="peer h-6 w-11 rounded-full bg-gray-400 after:absolute after:left-[2px] after:top-[2px] after:size-5 after:rounded-full after:border after:border-gray-300 after:bg-white after:transition-all after:content-[''] peer-checked:bg-orange-500 peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-orange-650/50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50 dark:border-gray-700 dark:bg-gray-600 dark:peer-checked:bg-orange-400 dark:peer-focus:ring-amber-400/50"></div> 44 44 </label> 45 45 </div> 46 46 );
+8 -4
packages/web/src/components/common/Tooltip.tsx
··· 7 7 className?: string; 8 8 } 9 9 10 - export default function Tooltip({ content, children, className = "" }: TooltipProps) { 10 + export default function Tooltip({ 11 + content, 12 + children, 13 + className = "", 14 + }: TooltipProps) { 11 15 const [isVisible, setIsVisible] = useState(false); 12 16 13 17 return ( ··· 22 26 aria-label="More information" 23 27 > 24 28 {children || ( 25 - <Info className="w-4 h-4 text-purple-750/70 dark:text-cyan-250/70 hover:text-purple-900 dark:hover:text-cyan-100 transition-colors" /> 29 + <Info className="size-4 text-purple-750/70 transition-colors hover:text-purple-900 dark:text-cyan-250/70 dark:hover:text-cyan-100" /> 26 30 )} 27 31 </button> 28 32 29 33 {isVisible && ( 30 34 <div 31 - className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 w-72 px-3 py-2 bg-slate-900 dark:bg-slate-800 text-white text-sm rounded-lg shadow-lg z-50 pointer-events-none" 35 + className="pointer-events-none absolute bottom-full left-1/2 z-50 mb-2 w-72 -translate-x-1/2 rounded-lg bg-slate-900 px-3 py-2 text-sm text-white shadow-lg dark:bg-slate-800" 32 36 role="tooltip" 33 37 > 34 38 <div className="relative"> 35 39 {content} 36 40 {/* Arrow */} 37 - <div className="absolute left-1/2 -translate-x-1/2 -bottom-5 w-0 h-0 border-l-8 border-l-transparent border-r-8 border-r-transparent border-t-8 border-t-slate-900 dark:border-t-slate-800" /> 41 + <div className="absolute -bottom-5 left-1/2 size-0 -translate-x-1/2 border-x-8 border-t-8 border-x-transparent border-t-slate-900 dark:border-t-slate-800" /> 38 42 </div> 39 43 </div> 40 44 )}
+9 -7
packages/web/src/components/login/HandleInput.tsx
··· 1 1 import { forwardRef, useState, useEffect } from "react"; 2 2 import { AtSign } from "lucide-react"; 3 3 4 - interface HandleInputProps 5 - extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> { 4 + interface HandleInputProps extends Omit< 5 + React.InputHTMLAttributes<HTMLInputElement>, 6 + "type" 7 + > { 6 8 error?: boolean; 7 9 selectedAvatar?: string | null; 8 10 } ··· 23 25 return ( 24 26 <div className="relative"> 25 27 {/* @ symbol or Profile pic */} 26 - <div className="absolute left-3 top-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 pointer-events-none z-10"> 28 + <div className="pointer-events-none absolute left-3 top-1/2 z-10 flex size-8 -translate-y-1/2 items-center justify-center"> 27 29 {showAvatar && selectedAvatar ? ( 28 30 <img 29 31 src={selectedAvatar} 30 32 alt="Selected profile" 31 - className="w-8 h-8 rounded-full object-cover border-2 border-cyan-500/50 dark:border-purple-500/50" 33 + className="size-8 rounded-full border-2 border-cyan-500/50 object-cover dark:border-purple-500/50" 32 34 /> 33 35 ) : ( 34 - <AtSign className="w-5 h-5 text-purple-750/60 dark:text-cyan-250/60" /> 36 + <AtSign className="size-5 text-purple-750/60 dark:text-cyan-250/60" /> 35 37 )} 36 38 </div> 37 39 ··· 39 41 <input 40 42 ref={ref} 41 43 type="text" 42 - className={`w-full pl-14 pr-4 py-3 bg-purple-50/50 dark:bg-slate-900/50 border-2 rounded-xl text-purple-900 dark:text-cyan-100 placeholder-purple-750/80 dark:placeholder-cyan-250/80 focus:outline-none focus:ring-2 transition-all ${ 44 + className={`w-full rounded-xl border-2 bg-purple-50/50 py-3 pl-14 pr-4 text-purple-900 placeholder-purple-750/80 transition-all focus:outline-none focus:ring-2 dark:bg-slate-900/50 dark:text-cyan-100 dark:placeholder-cyan-250/80 ${ 43 45 error 44 46 ? "border-red-500 focus:ring-red-500" 45 - : "border-cyan-500/50 dark:border-purple-500/30 focus:ring-orange-500 dark:focus:ring-amber-400 focus:border-transparent" 47 + : "border-cyan-500/50 focus:border-transparent focus:ring-orange-500 dark:border-purple-500/30 dark:focus:ring-amber-400" 46 48 } ${className || ""}`} 47 49 {...props} 48 50 />
+6 -6
packages/web/src/components/login/HeroSection.tsx
··· 9 9 }: HeroSectionProps) { 10 10 return ( 11 11 <div className="text-center md:text-left"> 12 - <div className="justify-center md:justify-start mb-4"> 12 + <div className="mb-4 justify-center md:justify-start"> 13 13 <div className="logo-glow-container"> 14 14 <FireflyLogo className="w-50 h-15" /> 15 15 </div> 16 16 </div> 17 17 18 - <h1 className="text-5xl md:text-6xl font-bold bg-gradient-to-r from-purple-600 via-cyan-500 to-pink-500 dark:from-cyan-300 dark:via-purple-300 dark:to-pink-300 bg-clip-text text-transparent mb-3 md:mb-4"> 18 + <h1 className="mb-3 bg-gradient-to-r from-purple-600 via-cyan-500 to-pink-500 bg-clip-text text-5xl font-bold text-transparent dark:from-cyan-300 dark:via-purple-300 dark:to-pink-300 md:mb-4 md:text-6xl"> 19 19 ATlast 20 20 </h1> 21 - <p className="text-xl md:text-2xl lg:text-2xl text-purple-900 dark:text-cyan-100 mb-2 font-medium"> 21 + <p className="mb-2 text-xl font-medium text-purple-900 dark:text-cyan-100 md:text-2xl lg:text-2xl"> 22 22 Find Your Light in the ATmosphere 23 23 </p> 24 - <p className="text-purple-750 dark:text-cyan-250 mb-2"> 24 + <p className="mb-2 text-purple-750 dark:text-cyan-250"> 25 25 Reconnect with your internet, one firefly at a time 26 26 </p> 27 27 28 28 {/* Decorative firefly trail - only show if motion enabled */} 29 29 {!reducedMotion && ( 30 30 <div 31 - className="mt-8 flex justify-center md:justify-start space-x-2" 31 + className="mt-8 flex justify-center space-x-2 md:justify-start" 32 32 aria-hidden="true" 33 33 > 34 34 {[...Array(5)].map((_, i) => ( 35 35 <div 36 36 key={i} 37 - className="w-2 h-2 rounded-full bg-orange-500 dark:bg-amber-400" 37 + className="size-2 rounded-full bg-orange-500 dark:bg-amber-400" 38 38 style={{ 39 39 opacity: 1 - i * 0.15, 40 40 animation: `float ${2 + i * 0.3}s ease-in-out infinite`,
+3 -3
packages/web/src/components/login/HowItWorksSection.tsx
··· 2 2 3 3 export default function HowItWorksSection() { 4 4 return ( 5 - <div className="max-w-4xl mx-auto"> 6 - <h2 className="text-2xl font-bold text-center text-purple-950 dark:text-cyan-50 mb-8"> 5 + <div className="mx-auto max-w-4xl"> 6 + <h2 className="mb-8 text-center text-2xl font-bold text-purple-950 dark:text-cyan-50"> 7 7 How It Works 8 8 </h2> 9 - <div className="grid grid-cols-2 md:grid-cols-4 gap-4"> 9 + <div className="grid grid-cols-2 gap-4 md:grid-cols-4"> 10 10 <StepCard 11 11 number={1} 12 12 color="orange"
+2 -2
packages/web/src/components/login/StepCard.tsx
··· 21 21 return ( 22 22 <div className="text-center"> 23 23 <div 24 - className={`w-12 h-12 ${colorClasses[color]} text-white rounded-full flex items-center justify-center mx-auto mb-3 font-bold text-lg shadow-md`} 24 + className={`size-12 ${colorClasses[color]} mx-auto mb-3 flex items-center justify-center rounded-full text-lg font-bold text-white shadow-md`} 25 25 aria-hidden="true" 26 26 > 27 27 {number} 28 28 </div> 29 - <h3 className="font-semibold text-purple-950 dark:text-cyan-50 mb-1"> 29 + <h3 className="mb-1 font-semibold text-purple-950 dark:text-cyan-50"> 30 30 {title} 31 31 </h3> 32 32 <p className="text-sm text-purple-900 dark:text-cyan-100">
+5 -5
packages/web/src/components/login/ValuePropCard.tsx
··· 12 12 description, 13 13 }: ValuePropCardProps) { 14 14 return ( 15 - <div className="bg-white/50 border-cyan-500/30 hover:border-cyan-400 dark:bg-slate-900/50 dark:border-purple-500/30 dark:hover:border-purple-400 backdrop-blur-xl rounded-2xl p-6 border-2 transition-all hover:scale-105 shadow-lg"> 16 - <div className="w-12 h-12 bg-gradient-to-br from-amber-300 to-orange-600 rounded-xl flex items-center justify-center mb-4 shadow-md"> 17 - <Icon className="w-6 h-6 text-slate-900" /> 15 + <div className="rounded-2xl border-2 border-cyan-500/30 bg-white/50 p-6 shadow-lg backdrop-blur-xl transition-all hover:scale-105 hover:border-cyan-400 dark:border-purple-500/30 dark:bg-slate-900/50 dark:hover:border-purple-400"> 16 + <div className="mb-4 flex size-12 items-center justify-center rounded-xl bg-gradient-to-br from-amber-300 to-orange-600 shadow-md"> 17 + <Icon className="size-6 text-slate-900" /> 18 18 </div> 19 - <h3 className="text-lg font-bold text-purple-950 dark:text-cyan-50 mb-2"> 19 + <h3 className="mb-2 text-lg font-bold text-purple-950 dark:text-cyan-50"> 20 20 {title} 21 21 </h3> 22 - <p className="text-purple-750 dark:text-cyan-250 text-sm leading-relaxed"> 22 + <p className="text-sm leading-relaxed text-purple-750 dark:text-cyan-250"> 23 23 {description} 24 24 </p> 25 25 </div>
+1 -1
packages/web/src/components/login/ValuePropsSection.tsx
··· 3 3 4 4 export default function ValuePropsSection() { 5 5 return ( 6 - <div className="grid md:grid-cols-3 gap-4 md:gap-6 mb-12 md:mb-16 max-w-5xl mx-auto"> 6 + <div className="mx-auto mb-12 grid max-w-5xl gap-4 md:mb-16 md:grid-cols-3 md:gap-6"> 7 7 <ValuePropCard 8 8 icon={Upload} 9 9 title="Share Your Light"
+2 -2
packages/web/src/hooks/useFileUpload.ts
··· 4 4 export function useFileUpload( 5 5 onSearchStart: (results: SearchResult[], platform: string) => void, 6 6 onStatusUpdate: (message: string) => void, 7 - userSettings: UserSettings, 7 + userSettings: UserSettings 8 8 ) { 9 9 async function handleFileUpload( 10 10 e: React.ChangeEvent<HTMLInputElement>, 11 - platform: string = "tiktok", 11 + platform: string = "tiktok" 12 12 ) { 13 13 const file = e.target.files?.[0]; 14 14 if (!file) return;
+129 -125
packages/web/src/hooks/useFollows.ts
··· 8 8 session: AtprotoSession | null, 9 9 searchResults: SearchResult[], 10 10 setSearchResults: ( 11 - results: SearchResult[] | ((prev: SearchResult[]) => SearchResult[]), 11 + results: SearchResult[] | ((prev: SearchResult[]) => SearchResult[]) 12 12 ) => void, 13 - destinationAppId: AtprotoAppId, 13 + destinationAppId: AtprotoAppId 14 14 ) { 15 15 const [isFollowing, setIsFollowing] = useState(false); 16 16 const [isCheckingFollowStatus, setIsCheckingFollowStatus] = useState(false); 17 17 18 - const followSelectedUsers = useCallback(async ( 19 - onUpdate: (message: string) => void, 20 - ): Promise<void> => { 21 - if (!session || isFollowing) return; 18 + const followSelectedUsers = useCallback( 19 + async (onUpdate: (message: string) => void): Promise<void> => { 20 + if (!session || isFollowing) return; 22 21 23 - const destinationApp = getAtprotoApp(destinationAppId); 24 - const followLexicon = 25 - destinationApp?.followLexicon || "app.bsky.graph.follow"; 26 - const destinationName = destinationApp?.name || "Undefined App"; 22 + const destinationApp = getAtprotoApp(destinationAppId); 23 + const followLexicon = 24 + destinationApp?.followLexicon || "app.bsky.graph.follow"; 25 + const destinationName = destinationApp?.name || "Undefined App"; 27 26 28 - const selectedUsers = searchResults.flatMap((result, resultIndex) => 29 - result.atprotoMatches 30 - .filter((match) => result.selectedMatches?.has(match.did)) 31 - .map((match) => ({ ...match, resultIndex })), 32 - ); 27 + const selectedUsers = searchResults.flatMap((result, resultIndex) => 28 + result.atprotoMatches 29 + .filter((match) => result.selectedMatches?.has(match.did)) 30 + .map((match) => ({ ...match, resultIndex })) 31 + ); 33 32 34 - if (selectedUsers.length === 0) { 35 - const msg = "No users selected to follow"; 36 - onUpdate(msg); 37 - alert(msg); 38 - return; 39 - } 33 + if (selectedUsers.length === 0) { 34 + const msg = "No users selected to follow"; 35 + onUpdate(msg); 36 + alert(msg); 37 + return; 38 + } 40 39 41 - setIsCheckingFollowStatus(true); 42 - onUpdate(`Checking follow status for ${selectedUsers.length} users...`); 40 + setIsCheckingFollowStatus(true); 41 + onUpdate(`Checking follow status for ${selectedUsers.length} users...`); 43 42 44 - let followStatusMap: Record<string, boolean> = {}; 45 - try { 46 - const dids = selectedUsers.map((u) => u.did); 47 - followStatusMap = await apiClient.checkFollowStatus(dids, followLexicon); 48 - } catch (error) { 49 - console.error("Failed to check follow status:", error); 50 - } finally { 51 - setIsCheckingFollowStatus(false); 52 - } 43 + let followStatusMap: Record<string, boolean> = {}; 44 + try { 45 + const dids = selectedUsers.map((u) => u.did); 46 + followStatusMap = await apiClient.checkFollowStatus( 47 + dids, 48 + followLexicon 49 + ); 50 + } catch (error) { 51 + console.error("Failed to check follow status:", error); 52 + } finally { 53 + setIsCheckingFollowStatus(false); 54 + } 53 55 54 - const usersToFollow = selectedUsers.filter( 55 - (user) => !followStatusMap[user.did], 56 - ); 57 - const alreadyFollowingCount = selectedUsers.length - usersToFollow.length; 58 - 59 - if (alreadyFollowingCount > 0) { 60 - onUpdate( 61 - `${alreadyFollowingCount} user${alreadyFollowingCount > 1 ? "s" : ""} already followed. Following ${usersToFollow.length} remaining...`, 56 + const usersToFollow = selectedUsers.filter( 57 + (user) => !followStatusMap[user.did] 62 58 ); 59 + const alreadyFollowingCount = selectedUsers.length - usersToFollow.length; 63 60 64 - setSearchResults((prev) => 65 - prev.map((result) => ({ 66 - ...result, 67 - atprotoMatches: result.atprotoMatches.map((match) => { 68 - if (followStatusMap[match.did]) { 69 - return { 70 - ...match, 71 - followStatus: { 72 - ...match.followStatus, 73 - [followLexicon]: true, 74 - }, 75 - }; 76 - } 77 - return match; 78 - }), 79 - })), 80 - ); 81 - } 61 + if (alreadyFollowingCount > 0) { 62 + onUpdate( 63 + `${alreadyFollowingCount} user${alreadyFollowingCount > 1 ? "s" : ""} already followed. Following ${usersToFollow.length} remaining...` 64 + ); 82 65 83 - if (usersToFollow.length === 0) { 84 - onUpdate("All selected users are already being followed!"); 85 - return; 86 - } 66 + setSearchResults((prev) => 67 + prev.map((result) => ({ 68 + ...result, 69 + atprotoMatches: result.atprotoMatches.map((match) => { 70 + if (followStatusMap[match.did]) { 71 + return { 72 + ...match, 73 + followStatus: { 74 + ...match.followStatus, 75 + [followLexicon]: true, 76 + }, 77 + }; 78 + } 79 + return match; 80 + }), 81 + })) 82 + ); 83 + } 87 84 88 - setIsFollowing(true); 89 - onUpdate( 90 - `Following ${usersToFollow.length} users on ${destinationName}...`, 91 - ); 92 - let totalFollowed = 0; 93 - let totalFailed = 0; 85 + if (usersToFollow.length === 0) { 86 + onUpdate("All selected users are already being followed!"); 87 + return; 88 + } 94 89 95 - try { 96 - const { BATCH_SIZE } = FOLLOW_CONFIG; 90 + setIsFollowing(true); 91 + onUpdate( 92 + `Following ${usersToFollow.length} users on ${destinationName}...` 93 + ); 94 + let totalFollowed = 0; 95 + let totalFailed = 0; 97 96 98 - for (let i = 0; i < usersToFollow.length; i += BATCH_SIZE) { 99 - const batch = usersToFollow.slice(i, i + BATCH_SIZE); 100 - const dids = batch.map((user) => user.did); 97 + try { 98 + const { BATCH_SIZE } = FOLLOW_CONFIG; 101 99 102 - try { 103 - const data = await apiClient.batchFollowUsers(dids, followLexicon); 104 - totalFollowed += data.succeeded; 105 - totalFailed += data.failed; 100 + for (let i = 0; i < usersToFollow.length; i += BATCH_SIZE) { 101 + const batch = usersToFollow.slice(i, i + BATCH_SIZE); 102 + const dids = batch.map((user) => user.did); 106 103 107 - data.results.forEach((result) => { 108 - if (result.success) { 109 - const user = batch.find((u) => u.did === result.did); 110 - if (user) { 111 - setSearchResults((prev) => 112 - prev.map((searchResult, index) => 113 - index === user.resultIndex 114 - ? { 115 - ...searchResult, 116 - atprotoMatches: searchResult.atprotoMatches.map( 117 - (match) => 118 - match.did === result.did 119 - ? { 120 - ...match, 121 - followStatus: { 122 - ...match.followStatus, 123 - [followLexicon]: true, 124 - }, 125 - } 126 - : match, 127 - ), 128 - } 129 - : searchResult, 130 - ), 131 - ); 104 + try { 105 + const data = await apiClient.batchFollowUsers(dids, followLexicon); 106 + totalFollowed += data.succeeded; 107 + totalFailed += data.failed; 108 + 109 + data.results.forEach((result) => { 110 + if (result.success) { 111 + const user = batch.find((u) => u.did === result.did); 112 + if (user) { 113 + setSearchResults((prev) => 114 + prev.map((searchResult, index) => 115 + index === user.resultIndex 116 + ? { 117 + ...searchResult, 118 + atprotoMatches: searchResult.atprotoMatches.map( 119 + (match) => 120 + match.did === result.did 121 + ? { 122 + ...match, 123 + followStatus: { 124 + ...match.followStatus, 125 + [followLexicon]: true, 126 + }, 127 + } 128 + : match 129 + ), 130 + } 131 + : searchResult 132 + ) 133 + ); 134 + } 132 135 } 133 - } 134 - }); 136 + }); 135 137 136 - onUpdate( 137 - `Followed ${totalFollowed} of ${usersToFollow.length} users`, 138 - ); 139 - } catch (error) { 140 - totalFailed += batch.length; 141 - console.error("Batch follow error:", error); 138 + onUpdate( 139 + `Followed ${totalFollowed} of ${usersToFollow.length} users` 140 + ); 141 + } catch (error) { 142 + totalFailed += batch.length; 143 + console.error("Batch follow error:", error); 144 + } 142 145 } 146 + 147 + const finalMsg = 148 + `Successfully followed ${totalFollowed} users` + 149 + (alreadyFollowingCount > 0 150 + ? ` (${alreadyFollowingCount} already followed)` 151 + : "") + 152 + (totalFailed > 0 ? `. ${totalFailed} failed.` : ""); 153 + onUpdate(finalMsg); 154 + } catch (error) { 155 + console.error("Batch follow error:", error); 156 + onUpdate("Error occurred while following users"); 157 + } finally { 158 + setIsFollowing(false); 143 159 } 144 - 145 - const finalMsg = 146 - `Successfully followed ${totalFollowed} users` + 147 - (alreadyFollowingCount > 0 148 - ? ` (${alreadyFollowingCount} already followed)` 149 - : "") + 150 - (totalFailed > 0 ? `. ${totalFailed} failed.` : ""); 151 - onUpdate(finalMsg); 152 - } catch (error) { 153 - console.error("Batch follow error:", error); 154 - onUpdate("Error occurred while following users"); 155 - } finally { 156 - setIsFollowing(false); 157 - } 158 - }, [session, searchResults, setSearchResults, destinationAppId, isFollowing]); 160 + }, 161 + [session, searchResults, setSearchResults, destinationAppId, isFollowing] 162 + ); 159 163 160 164 return { 161 165 isFollowing,
+3 -3
packages/web/src/hooks/useFormValidation.ts
··· 65 65 })); 66 66 return result.isValid; 67 67 }, 68 - [fields], 68 + [fields] 69 69 ); 70 70 71 71 const validateAll = useCallback( ··· 88 88 setFields(newFields); 89 89 return isValid; 90 90 }, 91 - [fields], 91 + [fields] 92 92 ); 93 93 94 94 const reset = useCallback(() => { ··· 110 110 setValue(fieldName, e.target.value), 111 111 onBlur: () => setTouched(fieldName), 112 112 }), 113 - [fields, setValue, setTouched], 113 + [fields, setValue, setTouched] 114 114 ); 115 115 116 116 return {
+5 -5
packages/web/src/hooks/useNotifications.ts
··· 16 16 setNotifications((prev) => [...prev, { id, type, message }]); 17 17 return id; 18 18 }, 19 - [], 19 + [] 20 20 ); 21 21 22 22 const removeNotification = useCallback((id: string) => { ··· 30 30 // Convenience methods 31 31 const success = useCallback( 32 32 (message: string) => addNotification("success", message), 33 - [addNotification], 33 + [addNotification] 34 34 ); 35 35 36 36 const error = useCallback( 37 37 (message: string) => addNotification("error", message), 38 - [addNotification], 38 + [addNotification] 39 39 ); 40 40 41 41 const info = useCallback( 42 42 (message: string) => addNotification("info", message), 43 - [addNotification], 43 + [addNotification] 44 44 ); 45 45 46 46 const warning = useCallback( 47 47 (message: string) => addNotification("warning", message), 48 - [addNotification], 48 + [addNotification] 49 49 ); 50 50 51 51 return {
+173 -152
packages/web/src/hooks/useSearch.ts
··· 12 12 total: 0, 13 13 }); 14 14 const [expandedResults, setExpandedResults] = useState<Set<number>>( 15 - new Set(), 15 + new Set() 16 16 ); 17 17 18 - const searchAllUsers = useCallback(async ( 19 - resultsToSearch: SearchResult[], 20 - onProgressUpdate: (message: string) => void, 21 - onComplete: (finalResults: SearchResult[]) => void, 22 - followLexicon?: string, 23 - ) => { 24 - if (!session || resultsToSearch.length === 0) return; 18 + const searchAllUsers = useCallback( 19 + async ( 20 + resultsToSearch: SearchResult[], 21 + onProgressUpdate: (message: string) => void, 22 + onComplete: (finalResults: SearchResult[]) => void, 23 + followLexicon?: string 24 + ) => { 25 + if (!session || resultsToSearch.length === 0) return; 25 26 26 - setIsSearchingAll(true); 27 - setSearchProgress({ searched: 0, found: 0, total: resultsToSearch.length }); 28 - onProgressUpdate(`Starting search for ${resultsToSearch.length} users...`); 27 + setIsSearchingAll(true); 28 + setSearchProgress({ 29 + searched: 0, 30 + found: 0, 31 + total: resultsToSearch.length, 32 + }); 33 + onProgressUpdate( 34 + `Starting search for ${resultsToSearch.length} users...` 35 + ); 29 36 30 - const { BATCH_SIZE, MAX_MATCHES } = SEARCH_CONFIG; 31 - let totalSearched = 0; 32 - let totalFound = 0; 33 - let consecutiveErrors = 0; 34 - const MAX_CONSECUTIVE_ERRORS = 3; 37 + const { BATCH_SIZE, MAX_MATCHES } = SEARCH_CONFIG; 38 + let totalSearched = 0; 39 + let totalFound = 0; 40 + let consecutiveErrors = 0; 41 + const MAX_CONSECUTIVE_ERRORS = 3; 35 42 36 - for (let i = 0; i < resultsToSearch.length; i += BATCH_SIZE) { 37 - if (totalFound >= MAX_MATCHES) { 38 - console.log( 39 - `Reached limit of ${MAX_MATCHES} matches. Stopping search.`, 40 - ); 41 - onProgressUpdate( 42 - `Search complete. Found ${totalFound} matches out of ${MAX_MATCHES} maximum.`, 43 - ); 44 - break; 45 - } 43 + for (let i = 0; i < resultsToSearch.length; i += BATCH_SIZE) { 44 + if (totalFound >= MAX_MATCHES) { 45 + console.log( 46 + `Reached limit of ${MAX_MATCHES} matches. Stopping search.` 47 + ); 48 + onProgressUpdate( 49 + `Search complete. Found ${totalFound} matches out of ${MAX_MATCHES} maximum.` 50 + ); 51 + break; 52 + } 53 + 54 + const batch = resultsToSearch.slice(i, i + BATCH_SIZE); 55 + const usernames = batch.map((r) => r.sourceUser.username); 46 56 47 - const batch = resultsToSearch.slice(i, i + BATCH_SIZE); 48 - const usernames = batch.map((r) => r.sourceUser.username); 57 + try { 58 + const data = await apiClient.batchSearchActors( 59 + usernames, 60 + followLexicon 61 + ); 49 62 50 - try { 51 - const data = await apiClient.batchSearchActors( 52 - usernames, 53 - followLexicon, 54 - ); 63 + consecutiveErrors = 0; 55 64 56 - consecutiveErrors = 0; 65 + data.results.forEach((result) => { 66 + totalSearched++; 67 + if (result.actors.length > 0) { 68 + totalFound++; 69 + } 70 + }); 57 71 58 - data.results.forEach((result) => { 59 - totalSearched++; 60 - if (result.actors.length > 0) { 61 - totalFound++; 62 - } 63 - }); 72 + setSearchProgress({ 73 + searched: totalSearched, 74 + found: totalFound, 75 + total: resultsToSearch.length, 76 + }); 77 + onProgressUpdate( 78 + `Searched ${totalSearched} of ${resultsToSearch.length} users. Found ${totalFound} matches.` 79 + ); 64 80 65 - setSearchProgress({ 66 - searched: totalSearched, 67 - found: totalFound, 68 - total: resultsToSearch.length, 69 - }); 70 - onProgressUpdate( 71 - `Searched ${totalSearched} of ${resultsToSearch.length} users. Found ${totalFound} matches.`, 72 - ); 81 + // Single state update per batch - updates results with API data 82 + setSearchResults((prev) => 83 + prev.map((result, index) => { 84 + const batchResultIndex = index - i; 85 + if ( 86 + batchResultIndex >= 0 && 87 + batchResultIndex < data.results.length 88 + ) { 89 + const batchResult = data.results[batchResultIndex]; 90 + const newSelectedMatches = new Set<string>(); 73 91 74 - // Single state update per batch - updates results with API data 75 - setSearchResults((prev) => 76 - prev.map((result, index) => { 77 - const batchResultIndex = index - i; 78 - if ( 79 - batchResultIndex >= 0 && 80 - batchResultIndex < data.results.length 81 - ) { 82 - const batchResult = data.results[batchResultIndex]; 83 - const newSelectedMatches = new Set<string>(); 92 + if (batchResult.actors.length > 0) { 93 + newSelectedMatches.add(batchResult.actors[0].did); 94 + } 84 95 85 - if (batchResult.actors.length > 0) { 86 - newSelectedMatches.add(batchResult.actors[0].did); 96 + return { 97 + ...result, 98 + atprotoMatches: batchResult.actors, 99 + isSearching: false, 100 + error: batchResult.error, 101 + selectedMatches: newSelectedMatches, 102 + }; 87 103 } 104 + return result; 105 + }) 106 + ); 88 107 89 - return { 90 - ...result, 91 - atprotoMatches: batchResult.actors, 92 - isSearching: false, 93 - error: batchResult.error, 94 - selectedMatches: newSelectedMatches, 95 - }; 96 - } 97 - return result; 98 - }), 99 - ); 108 + if (totalFound >= MAX_MATCHES) { 109 + break; 110 + } 111 + } catch (error) { 112 + console.error("Batch search error:", error); 113 + consecutiveErrors++; 100 114 101 - if (totalFound >= MAX_MATCHES) { 102 - break; 103 - } 104 - } catch (error) { 105 - console.error("Batch search error:", error); 106 - consecutiveErrors++; 115 + // Single state update on error - marks batch as failed 116 + setSearchResults((prev) => 117 + prev.map((result, index) => 118 + i <= index && index < i + BATCH_SIZE 119 + ? { ...result, isSearching: false, error: "Search failed" } 120 + : result 121 + ) 122 + ); 107 123 108 - // Single state update on error - marks batch as failed 109 - setSearchResults((prev) => 110 - prev.map((result, index) => 111 - i <= index && index < i + BATCH_SIZE 112 - ? { ...result, isSearching: false, error: "Search failed" } 113 - : result, 114 - ), 115 - ); 116 - 117 - if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { 118 - const backoffDelay = Math.min( 119 - 1000 * Math.pow(2, consecutiveErrors - MAX_CONSECUTIVE_ERRORS), 120 - 5000, 121 - ); 122 - console.log( 123 - `Rate limit detected. Backing off for ${backoffDelay}ms...`, 124 - ); 125 - onProgressUpdate(`Rate limit detected. Pausing briefly...`); 126 - await new Promise((resolve) => setTimeout(resolve, backoffDelay)); 124 + if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { 125 + const backoffDelay = Math.min( 126 + 1000 * Math.pow(2, consecutiveErrors - MAX_CONSECUTIVE_ERRORS), 127 + 5000 128 + ); 129 + console.log( 130 + `Rate limit detected. Backing off for ${backoffDelay}ms...` 131 + ); 132 + onProgressUpdate(`Rate limit detected. Pausing briefly...`); 133 + await new Promise((resolve) => setTimeout(resolve, backoffDelay)); 134 + } 127 135 } 128 136 } 129 - } 130 137 131 - setIsSearchingAll(false); 132 - onProgressUpdate( 133 - `Search complete! Found ${totalFound} matches out of ${totalSearched} users searched.`, 134 - ); 138 + setIsSearchingAll(false); 139 + onProgressUpdate( 140 + `Search complete! Found ${totalFound} matches out of ${totalSearched} users searched.` 141 + ); 135 142 136 - // Get current results from state to pass to onComplete 137 - setSearchResults((currentResults) => { 138 - onComplete(currentResults); 139 - return currentResults; 140 - }); 141 - }, [session]); 143 + // Get current results from state to pass to onComplete 144 + setSearchResults((currentResults) => { 145 + onComplete(currentResults); 146 + return currentResults; 147 + }); 148 + }, 149 + [session] 150 + ); 142 151 143 - const toggleMatchSelection = useCallback((resultIndex: number, did: string) => { 144 - setSearchResults((prev) => { 145 - // Only update the specific item instead of mapping entire array 146 - const newResults = [...prev]; 147 - const result = newResults[resultIndex]; 152 + const toggleMatchSelection = useCallback( 153 + (resultIndex: number, did: string) => { 154 + setSearchResults((prev) => { 155 + // Only update the specific item instead of mapping entire array 156 + const newResults = [...prev]; 157 + const result = newResults[resultIndex]; 148 158 149 - const newSelectedMatches = new Set(result.selectedMatches); 150 - if (newSelectedMatches.has(did)) { 151 - newSelectedMatches.delete(did); 152 - } else { 153 - newSelectedMatches.add(did); 154 - } 159 + const newSelectedMatches = new Set(result.selectedMatches); 160 + if (newSelectedMatches.has(did)) { 161 + newSelectedMatches.delete(did); 162 + } else { 163 + newSelectedMatches.add(did); 164 + } 155 165 156 - newResults[resultIndex] = { ...result, selectedMatches: newSelectedMatches }; 157 - return newResults; 158 - }); 159 - }, []); 166 + newResults[resultIndex] = { 167 + ...result, 168 + selectedMatches: newSelectedMatches, 169 + }; 170 + return newResults; 171 + }); 172 + }, 173 + [] 174 + ); 160 175 161 176 const toggleExpandResult = useCallback((index: number) => { 162 177 setExpandedResults((prev) => { ··· 167 182 }); 168 183 }, []); 169 184 170 - const selectAllMatches = useCallback((onUpdate: (message: string) => void) => { 171 - setSearchResults((prev) => { 172 - const updated = prev.map((result) => { 173 - const newSelectedMatches = new Set<string>(); 174 - if (result.atprotoMatches.length > 0) { 175 - newSelectedMatches.add(result.atprotoMatches[0].did); 176 - } 177 - return { 178 - ...result, 179 - selectedMatches: newSelectedMatches, 180 - }; 181 - }); 185 + const selectAllMatches = useCallback( 186 + (onUpdate: (message: string) => void) => { 187 + setSearchResults((prev) => { 188 + const updated = prev.map((result) => { 189 + const newSelectedMatches = new Set<string>(); 190 + if (result.atprotoMatches.length > 0) { 191 + newSelectedMatches.add(result.atprotoMatches[0].did); 192 + } 193 + return { 194 + ...result, 195 + selectedMatches: newSelectedMatches, 196 + }; 197 + }); 182 198 183 - const totalToSelect = updated.filter( 184 - (r) => r.atprotoMatches.length > 0, 185 - ).length; 186 - onUpdate(`Selected ${totalToSelect} top matches`); 199 + const totalToSelect = updated.filter( 200 + (r) => r.atprotoMatches.length > 0 201 + ).length; 202 + onUpdate(`Selected ${totalToSelect} top matches`); 187 203 188 - return updated; 189 - }); 190 - }, []); 204 + return updated; 205 + }); 206 + }, 207 + [] 208 + ); 191 209 192 - const deselectAllMatches = useCallback((onUpdate: (message: string) => void) => { 193 - setSearchResults((prev) => 194 - prev.map((result) => ({ 195 - ...result, 196 - selectedMatches: new Set<string>(), 197 - })), 198 - ); 199 - onUpdate("Cleared all selections"); 200 - }, []); 210 + const deselectAllMatches = useCallback( 211 + (onUpdate: (message: string) => void) => { 212 + setSearchResults((prev) => 213 + prev.map((result) => ({ 214 + ...result, 215 + selectedMatches: new Set<string>(), 216 + })) 217 + ); 218 + onUpdate("Cleared all selections"); 219 + }, 220 + [] 221 + ); 201 222 202 223 const totalSelected = searchResults.reduce( 203 224 (total, result) => total + (result.selectedMatches?.size || 0), 204 - 0, 225 + 0 205 226 ); 206 227 207 228 const totalFound = searchResults.filter( 208 - (r) => r.atprotoMatches.length > 0, 229 + (r) => r.atprotoMatches.length > 0 209 230 ).length; 210 231 211 232 return {
+5 -5
packages/web/src/lib/api/IApiClient.ts
··· 31 31 getUploadDetails( 32 32 uploadId: string, 33 33 page?: number, 34 - pageSize?: number, 34 + pageSize?: number 35 35 ): Promise<{ 36 36 results: SearchResult[]; 37 37 pagination?: { ··· 49 49 // Search Operations 50 50 batchSearchActors( 51 51 usernames: string[], 52 - followLexicon?: string, 52 + followLexicon?: string 53 53 ): Promise<{ results: BatchSearchResult[] }>; 54 54 55 55 checkFollowStatus( 56 56 dids: string[], 57 - followLexicon: string, 57 + followLexicon: string 58 58 ): Promise<Record<string, boolean>>; 59 59 60 60 // Follow Operations 61 61 batchFollowUsers( 62 62 dids: string[], 63 - followLexicon: string, 63 + followLexicon: string 64 64 ): Promise<{ 65 65 success: boolean; 66 66 total: number; ··· 74 74 saveResults( 75 75 uploadId: string, 76 76 sourcePlatform: string, 77 - results: SearchResult[], 77 + results: SearchResult[] 78 78 ): Promise<SaveResultsResponse | null>; 79 79 80 80 // Cache management
+7 -7
packages/web/src/lib/api/adapters/MockApiAdapter.ts
··· 71 71 72 72 async checkFollowStatus( 73 73 dids: string[], 74 - followLexicon: string, 74 + followLexicon: string 75 75 ): Promise<Record<string, boolean>> { 76 76 await delay(300); 77 77 ··· 97 97 async getUploadDetails( 98 98 uploadId: string, 99 99 page: number = 1, 100 - pageSize: number = 50, 100 + pageSize: number = 50 101 101 ): Promise<{ results: SearchResult[]; pagination?: any }> { 102 102 await delay(500); 103 103 ··· 111 111 } 112 112 113 113 async getAllUploadDetails( 114 - uploadId: string, 114 + uploadId: string 115 115 ): Promise<{ results: SearchResult[] }> { 116 116 return this.getUploadDetails(uploadId); 117 117 } 118 118 119 119 async batchSearchActors( 120 120 usernames: string[], 121 - followLexicon?: string, 121 + followLexicon?: string 122 122 ): Promise<{ results: BatchSearchResult[] }> { 123 123 await delay(800); 124 124 ··· 133 133 134 134 async batchFollowUsers( 135 135 dids: string[], 136 - followLexicon: string, 136 + followLexicon: string 137 137 ): Promise<{ 138 138 success: boolean; 139 139 total: number; ··· 163 163 async saveResults( 164 164 uploadId: string, 165 165 sourcePlatform: string, 166 - results: SearchResult[], 166 + results: SearchResult[] 167 167 ): Promise<SaveResultsResponse | null> { 168 168 await delay(500); 169 169 ··· 171 171 172 172 const uploads = JSON.parse(localStorage.getItem("mock_uploads") || "[]"); 173 173 const matchedUsers = results.filter( 174 - (r) => r.atprotoMatches.length > 0, 174 + (r) => r.atprotoMatches.length > 0 175 175 ).length; 176 176 177 177 uploads.unshift({
+12 -12
packages/web/src/lib/api/adapters/RealApiAdapter.ts
··· 126 126 async getUploadDetails( 127 127 uploadId: string, 128 128 page: number = 1, 129 - pageSize: number = 50, 129 + pageSize: number = 50 130 130 ): Promise<{ 131 131 results: SearchResult[]; 132 132 pagination?: any; ··· 135 135 "upload-details", 136 136 uploadId, 137 137 page, 138 - pageSize, 138 + pageSize 139 139 ); 140 140 const cached = this.responseCache.get<any>(cacheKey); 141 141 if (cached) { ··· 144 144 145 145 const res = await fetch( 146 146 `/.netlify/functions/get-upload-details?uploadId=${uploadId}&page=${page}&pageSize=${pageSize}`, 147 - { credentials: "include" }, 147 + { credentials: "include" } 148 148 ); 149 149 150 150 if (!res.ok) { ··· 159 159 } 160 160 161 161 async getAllUploadDetails( 162 - uploadId: string, 162 + uploadId: string 163 163 ): Promise<{ results: SearchResult[] }> { 164 164 // Check if we have all pages cached 165 165 const firstPageKey = generateCacheKey("upload-details", uploadId, 1, 100); ··· 200 200 201 201 async checkFollowStatus( 202 202 dids: string[], 203 - followLexicon: string, 203 + followLexicon: string 204 204 ): Promise<Record<string, boolean>> { 205 205 // Sort DIDs for consistent cache key 206 206 const sortedDids = [...dids].sort(); 207 207 const cacheKey = generateCacheKey( 208 208 "follow-status", 209 209 followLexicon, 210 - sortedDids.join(","), 210 + sortedDids.join(",") 211 211 ); 212 212 const cached = this.responseCache.get<Record<string, boolean>>(cacheKey); 213 213 if (cached) { ··· 227 227 228 228 const response = await res.json(); 229 229 const data = unwrapResponse<{ followStatus: Record<string, boolean> }>( 230 - response, 230 + response 231 231 ); 232 232 233 233 this.responseCache.set( 234 234 cacheKey, 235 235 data.followStatus, 236 - CACHE_CONFIG.FOLLOW_STATUS_TTL, 236 + CACHE_CONFIG.FOLLOW_STATUS_TTL 237 237 ); 238 238 return data.followStatus; 239 239 } 240 240 241 241 async batchSearchActors( 242 242 usernames: string[], 243 - followLexicon?: string, 243 + followLexicon?: string 244 244 ): Promise<{ results: BatchSearchResult[] }> { 245 245 // Sort usernames for consistent cache key 246 246 const sortedUsernames = [...usernames].sort(); 247 247 const cacheKey = generateCacheKey( 248 248 "search", 249 249 followLexicon || "default", 250 - sortedUsernames.join(","), 250 + sortedUsernames.join(",") 251 251 ); 252 252 const cached = this.responseCache.get<any>(cacheKey); 253 253 if (cached) { ··· 274 274 275 275 async batchFollowUsers( 276 276 dids: string[], 277 - followLexicon: string, 277 + followLexicon: string 278 278 ): Promise<{ 279 279 success: boolean; 280 280 total: number; ··· 308 308 async saveResults( 309 309 uploadId: string, 310 310 sourcePlatform: string, 311 - results: SearchResult[], 311 + results: SearchResult[] 312 312 ): Promise<SaveResultsResponse | null> { 313 313 try { 314 314 const resultsToSave = results
+10 -10
packages/web/src/lib/parsers/fileExtractor.ts
··· 17 17 18 18 public async processZipArchive( 19 19 zip: JSZip, 20 - rules: ParseRule[], 20 + rules: ParseRule[] 21 21 ): Promise<ExtractionResults> { 22 22 /** Core logic for extracting usernames from a successfully loaded ZIP archive. */ 23 23 const allExtracted: Record<string, string[]> = {}; ··· 27 27 const rule = rules[i]; 28 28 const ruleId = `Rule_${i + 1}_${rule.zipPath}`; 29 29 console.log( 30 - `Processing ZIP file path ${rule.zipPath} (Format: ${rule.format})`, 30 + `Processing ZIP file path ${rule.zipPath} (Format: ${rule.format})` 31 31 ); 32 32 33 33 // 1. Get file object from ZIP 34 34 const fileInZip = zip.file(rule.zipPath); 35 35 if (!fileInZip) { 36 36 console.warn( 37 - `WARNING: File not found in ZIP: '${rule.zipPath}'. Skipping rule.`, 37 + `WARNING: File not found in ZIP: '${rule.zipPath}'. Skipping rule.` 38 38 ); 39 39 continue; 40 40 } ··· 65 65 * Check if file is a ZIP by reading magic number 66 66 */ 67 67 async function checkIfZipFile( 68 - file: File | ArrayBuffer | Blob, 68 + file: File | ArrayBuffer | Blob 69 69 ): Promise<boolean> { 70 70 try { 71 71 const blob = ··· 88 88 */ 89 89 export async function parseDataFile( 90 90 file: File | ArrayBuffer | Blob, 91 - platform: string, 91 + platform: string 92 92 ): Promise<string[]> { 93 93 const rules = getRulesForPlatform(platform); 94 94 ··· 109 109 const results = await extractor.processZipArchive(zip, rules); 110 110 111 111 console.log( 112 - `Successfully extracted ${results.uniqueUsernames.length} usernames from ZIP archive.`, 112 + `Successfully extracted ${results.uniqueUsernames.length} usernames from ZIP archive.` 113 113 ); 114 114 return results.uniqueUsernames; 115 115 } catch (e) { ··· 123 123 // We need a File object to get the name and content easily 124 124 if (!(file instanceof File) && !(file instanceof Blob)) { 125 125 console.error( 126 - "Input failed ZIP check and lacks a name/content structure for single file parsing (must be File or Blob).", 126 + "Input failed ZIP check and lacks a name/content structure for single file parsing (must be File or Blob)." 127 127 ); 128 128 return []; 129 129 } ··· 146 146 147 147 if (!matchingRule) { 148 148 console.error( 149 - `Could not match single file '${singleFile.name}' (extension: ${fileExt}) to any rule for platform ${platform}. Available formats: ${rules.map((r) => r.format).join(", ")}`, 149 + `Could not match single file '${singleFile.name}' (extension: ${fileExt}) to any rule for platform ${platform}. Available formats: ${rules.map((r) => r.format).join(", ")}` 150 150 ); 151 151 return []; 152 152 } 153 153 154 154 console.log( 155 - `Matched single file '${singleFile.name}' to rule format: ${matchingRule.format}`, 155 + `Matched single file '${singleFile.name}' to rule format: ${matchingRule.format}` 156 156 ); 157 157 158 158 // 3. Process as single file content ··· 162 162 163 163 const uniqueUsernames = Array.from(new Set(extracted)).sort(); 164 164 console.log( 165 - `Successfully extracted ${uniqueUsernames.length} unique usernames from single file.`, 165 + `Successfully extracted ${uniqueUsernames.length} unique usernames from single file.` 166 166 ); 167 167 168 168 return uniqueUsernames;
+6 -6
packages/web/src/lib/parsers/parserLogic.ts
··· 8 8 **/ 9 9 export function parseTextOrHtml( 10 10 content: string, 11 - regexPattern: string, 11 + regexPattern: string 12 12 ): string[] { 13 13 try { 14 14 // 'g' for global matching, 's' for multiline (DOTALL equivalent) ··· 39 39 40 40 if (pathKeys.length < 2) { 41 41 console.error( 42 - "JSON rule must have at least two path keys (list key and target key).", 42 + "JSON rule must have at least two path keys (list key and target key)." 43 43 ); 44 44 return []; 45 45 } ··· 60 60 currentData = currentData[key]; 61 61 } else { 62 62 console.error( 63 - `ERROR: Could not traverse JSON path up to key: ${key}. Path: ${listContainerPath.join(" -> ")}`, 63 + `ERROR: Could not traverse JSON path up to key: ${key}. Path: ${listContainerPath.join(" -> ")}` 64 64 ); 65 65 return []; 66 66 } ··· 84 84 } 85 85 } else { 86 86 console.error( 87 - `ERROR: Expected an array at key '${listKey}' but found a different type.`, 87 + `ERROR: Expected an array at key '${listKey}' but found a different type.` 88 88 ); 89 89 } 90 90 } else { 91 91 console.error( 92 - `ERROR: List key '${listKey}' not found at its expected position.`, 92 + `ERROR: List key '${listKey}' not found at its expected position.` 93 93 ); 94 94 } 95 95 ··· 121 121 } 122 122 } 123 123 console.error( 124 - `ERROR: Unsupported format or invalid rule type for rule with path: ${rule.zipPath}`, 124 + `ERROR: Unsupported format or invalid rule type for rule with path: ${rule.zipPath}` 125 125 ); 126 126 return []; 127 127 }
+2 -2
packages/web/src/lib/utils/platform.ts
··· 49 49 */ 50 50 export function getAtprotoAppWithFallback( 51 51 appId: AtprotoAppId, 52 - defaultApp: AtprotoAppId = "bluesky", 52 + defaultApp: AtprotoAppId = "bluesky" 53 53 ): AtprotoApp { 54 54 return ( 55 55 getAtprotoApp(appId) || getAtprotoApp(defaultApp) || ATPROTO_APPS.bluesky ··· 74 74 export function getEnabledPlatforms(): Array<[string, PlatformConfig]> { 75 75 if (!enabledPlatformsCache) { 76 76 enabledPlatformsCache = Object.entries(PLATFORMS).filter( 77 - ([_, config]) => config.enabled, 77 + ([_, config]) => config.enabled 78 78 ); 79 79 } 80 80 return enabledPlatformsCache;
+5 -5
packages/web/src/lib/validation.ts
··· 13 13 */ 14 14 function validateWithZod<T>( 15 15 schema: z.ZodSchema<T>, 16 - value: unknown, 16 + value: unknown 17 17 ): ValidationResult { 18 18 const result = schema.safeParse(value); 19 19 if (result.success) { ··· 43 43 }) 44 44 .refine((val) => !/^[.-]|[.-]$/.test(val), { 45 45 message: "Handle cannot start or end with . or -", 46 - }), 46 + }) 47 47 ); 48 48 49 49 const emailSchema = z ··· 71 71 */ 72 72 export function validateRequired( 73 73 value: string, 74 - fieldName: string = "This field", 74 + fieldName: string = "This field" 75 75 ): ValidationResult { 76 76 const schema = z.string().trim().min(1, `${fieldName} is required`); 77 77 return validateWithZod(schema, value); ··· 83 83 export function validateMinLength( 84 84 value: string, 85 85 minLength: number, 86 - fieldName: string = "This field", 86 + fieldName: string = "This field" 87 87 ): ValidationResult { 88 88 const schema = z 89 89 .string() ··· 98 98 export function validateMaxLength( 99 99 value: string, 100 100 maxLength: number, 101 - fieldName: string = "This field", 101 + fieldName: string = "This field" 102 102 ): ValidationResult { 103 103 const schema = z 104 104 .string()
+1 -1
packages/web/src/main.tsx
··· 6 6 ReactDOM.createRoot(document.getElementById("root")!).render( 7 7 <React.StrictMode> 8 8 <Router /> 9 - </React.StrictMode>, 9 + </React.StrictMode> 10 10 );
+4 -4
packages/web/src/pages/Home.tsx
··· 25 25 onNavigate: (step: "home" | "login") => void; 26 26 onFileUpload: ( 27 27 e: React.ChangeEvent<HTMLInputElement>, 28 - platform: string, 28 + platform: string 29 29 ) => void; 30 30 onLoadUpload: (uploadId: string) => void; 31 31 currentStep: string; ··· 97 97 /> 98 98 99 99 {/* Header */} 100 - <div className="bg-white dark:bg-slate-900 border-b-2 border-cyan-500/50 dark:border-purple-500/50 overflow-x-auto"> 100 + <div className="overflow-x-auto border-b-2 border-cyan-500/50 bg-white dark:border-purple-500/50 dark:bg-slate-900"> 101 101 <AppHeader 102 102 session={session} 103 103 onLogout={onLogout} ··· 110 110 /> 111 111 </div> 112 112 113 - <div className="max-w-6xl mx-auto px-4 py-8"> 114 - <div className="max-w-6xl mx-auto bg-slate-100/50 dark:bg-slate-900/50 backdrop-blur-xl rounded-3xl p-3 border-2 border-cyan-500/30 dark:border-purple-500/30 mb-8"> 113 + <div className="mx-auto max-w-6xl px-4 py-8"> 114 + <div className="mx-auto mb-8 max-w-6xl rounded-3xl border-2 border-cyan-500/30 bg-slate-100/50 p-3 backdrop-blur-xl dark:border-purple-500/30 dark:bg-slate-900/50"> 115 115 {/* Tab Navigation */} 116 116 <TabNavigation activeTab={activeTab} onTabChange={setActiveTab} /> 117 117
+7 -7
packages/web/src/pages/Loading.tsx
··· 75 75 76 76 {/* Platform Banner - Searching State */} 77 77 <div 78 - className={`bg-firefly-banner dark:bg-firefly-banner-dark text-white`} 78 + className={`bg-firefly-banner text-white dark:bg-firefly-banner-dark`} 79 79 > 80 - <div className="max-w-3xl mx-auto px-4 py-6"> 80 + <div className="mx-auto max-w-3xl px-4 py-6"> 81 81 <div className="flex items-center justify-between"> 82 82 <div className="flex items-center space-x-4"> 83 83 <PlatformBadge ··· 87 87 /> 88 88 <div> 89 89 <h2 className="text-xl font-bold">Finding Your Fireflies</h2> 90 - <p className="text-white text-sm"> 90 + <p className="text-sm text-white"> 91 91 Searching the ATmosphere for {platform.name} follows... 92 92 </p> 93 93 </div> ··· 103 103 </div> 104 104 105 105 {/* Progress Stats */} 106 - <div className="bg-white/95 dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-sm"> 107 - <div className="max-w-3xl mx-auto px-4 py-4"> 108 - <StatsGroup stats={statsData} className="grid-cols-3 mb-4" /> 106 + <div className="border-b-2 border-cyan-500/30 bg-white/95 backdrop-blur-sm dark:border-purple-500/30 dark:bg-slate-900"> 107 + <div className="mx-auto max-w-3xl p-4"> 108 + <StatsGroup stats={statsData} className="mb-4 grid-cols-3" /> 109 109 110 110 <ProgressBar 111 111 current={searchProgress.searched} ··· 116 116 </div> 117 117 118 118 {/* Skeleton Results - Matches layout of Results page */} 119 - <div className="max-w-3xl mx-auto px-4 py-4 space-y-4"> 119 + <div className="mx-auto max-w-3xl space-y-4 p-4"> 120 120 {[...Array(8)].map((_, i) => ( 121 121 <SearchResultSkeleton key={i} /> 122 122 ))}
+20 -20
packages/web/src/pages/Login.tsx
··· 49 49 try { 50 50 const url = new URL( 51 51 "xrpc/app.bsky.actor.searchActorsTypeahead", 52 - "https://public.api.bsky.app", 52 + "https://public.api.bsky.app" 53 53 ); 54 54 url.searchParams.set("q", handle); 55 55 url.searchParams.set("limit", "1"); ··· 137 137 138 138 return ( 139 139 <div className="min-h-screen"> 140 - <div className="max-w-6xl mx-auto px-4 py-8 md:py-12"> 140 + <div className="mx-auto max-w-6xl px-4 py-8 md:py-12"> 141 141 {/* Hero Section - Side by side on desktop */} 142 - <div className="grid md:grid-cols-2 gap-8 md:gap-12 items-start mb-12 md:mb-16"> 142 + <div className="mb-12 grid items-start gap-8 md:mb-16 md:grid-cols-2 md:gap-12"> 143 143 <HeroSection reducedMotion={reducedMotion} /> 144 144 145 145 {/* Right: Login Card or Dashboard Button */} 146 146 <div className="w-full"> 147 147 {session ? ( 148 - <div className="bg-white/50 dark:bg-slate-900/50 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-xl rounded-3xl p-8 border-2 shadow-2xl"> 149 - <div className="text-center mb-6"> 150 - <h2 className="text-2xl font-bold text-purple-950 dark:text-cyan-50 mb-2"> 148 + <div className="rounded-3xl border-2 border-cyan-500/30 bg-white/50 p-8 shadow-2xl backdrop-blur-xl dark:border-purple-500/30 dark:bg-slate-900/50"> 149 + <div className="mb-6 text-center"> 150 + <h2 className="mb-2 text-2xl font-bold text-purple-950 dark:text-cyan-50"> 151 151 You're logged in! 152 152 </h2> 153 153 <p className="text-purple-750 dark:text-cyan-250"> ··· 157 157 158 158 <button 159 159 onClick={() => onNavigate?.("home")} 160 - className="w-full bg-firefly-banner dark:bg-firefly-banner-dark text-white py-4 rounded-xl font-bold text-lg transition-all shadow-lg hover:shadow-xl focus:ring-4 focus:ring-orange-500 dark:focus:ring-amber-400 focus:outline-none flex items-center justify-center space-x-2" 160 + className="flex w-full items-center justify-center space-x-2 rounded-xl bg-firefly-banner py-4 text-lg font-bold text-white shadow-lg transition-all hover:shadow-xl focus:outline-none focus:ring-4 focus:ring-orange-500 dark:bg-firefly-banner-dark dark:focus:ring-amber-400" 161 161 > 162 162 <span>Go to Dashboard</span> 163 - <ArrowRight className="w-5 h-5" /> 163 + <ArrowRight className="size-5" /> 164 164 </button> 165 165 </div> 166 166 ) : ( 167 - <div className="bg-white/50 dark:bg-slate-900/50 border-cyan-500/30 dark:border-purple-500/30 backdrop-blur-xl rounded-3xl p-8 border-2 shadow-2xl"> 168 - <h2 className="text-2xl font-bold text-purple-950 dark:text-cyan-50 mb-2 text-center"> 167 + <div className="rounded-3xl border-2 border-cyan-500/30 bg-white/50 p-8 shadow-2xl backdrop-blur-xl dark:border-purple-500/30 dark:bg-slate-900/50"> 168 + <h2 className="mb-2 text-center text-2xl font-bold text-purple-950 dark:text-cyan-50"> 169 169 Light Up Your Network 170 170 </h2> 171 - <p className="text-purple-750 dark:text-cyan-250 text-center mb-6"> 171 + <p className="mb-6 text-center text-purple-750 dark:text-cyan-250"> 172 172 Reconnect in the ATmosphere 173 173 <sup className="ml-0.5"> 174 174 <Tooltip 175 175 content={ 176 176 <div className="text-left"> 177 - <p className="font-semibold mb-1"> 177 + <p className="mb-1 font-semibold"> 178 178 What's the ATmosphere? 179 179 </p> 180 180 <p className="text-xs leading-relaxed"> ··· 196 196 <div> 197 197 <label 198 198 htmlFor="atproto-handle" 199 - className="block text-sm font-semibold text-purple-900 dark:text-cyan-100 mb-2" 199 + className="mb-2 block text-sm font-semibold text-purple-900 dark:text-cyan-100" 200 200 > 201 201 Your ATmosphere Handle 202 202 </label> ··· 222 222 </actor-typeahead> 223 223 {strippedAtMessage && ( 224 224 <div className="mt-2 flex items-center gap-2 text-sm text-cyan-700 dark:text-cyan-300"> 225 - <Info className="w-4 h-4 flex-shrink-0" /> 225 + <Info className="size-4 flex-shrink-0" /> 226 226 <span> 227 227 No need for the @ symbol - we've removed it for you! 228 228 </span> ··· 234 234 className="mt-2 flex items-center gap-2 text-sm text-red-600 dark:text-red-400" 235 235 role="alert" 236 236 > 237 - <AlertCircle className="w-4 h-4 flex-shrink-0" /> 237 + <AlertCircle className="size-4 flex-shrink-0" /> 238 238 <span>{fields.handle.error}</span> 239 239 </div> 240 240 )} ··· 243 243 <button 244 244 type="submit" 245 245 disabled={isSubmitting} 246 - className="w-full bg-firefly-banner dark:bg-firefly-banner-dark text-white py-4 rounded-xl font-bold text-lg transition-all shadow-lg hover:shadow-xl focus:ring-4 focus:ring-orange-500 dark:focus:ring-amber-400 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center" 246 + className="flex w-full items-center justify-center rounded-xl bg-firefly-banner py-4 text-lg font-bold text-white shadow-lg transition-all hover:shadow-xl focus:outline-none focus:ring-4 focus:ring-orange-500 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-firefly-banner-dark dark:focus:ring-amber-400" 247 247 aria-label="Connect to the ATmosphere" 248 248 > 249 249 {isSubmitting ? ( 250 250 <> 251 - <div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" /> 251 + <div className="mr-2 size-5 animate-spin rounded-full border-2 border-white border-t-transparent" /> 252 252 <span>Connecting...</span> 253 253 </> 254 254 ) : ( ··· 257 257 </button> 258 258 </form> 259 259 260 - <div className="mt-6 pt-6 border-t-2 border-cyan-500/30 dark:border-purple-500/30"> 260 + <div className="mt-6 border-t-2 border-cyan-500/30 pt-6 dark:border-purple-500/30"> 261 261 <div className="flex items-start space-x-2 text-sm text-purple-900 dark:text-cyan-100"> 262 262 <svg 263 - className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" 263 + className="mt-0.5 size-5 flex-shrink-0 text-green-500" 264 264 fill="currentColor" 265 265 viewBox="0 0 20 20" 266 266 aria-hidden="true" ··· 275 275 <p className="font-semibold text-purple-950 dark:text-cyan-50"> 276 276 Secure OAuth Connection 277 277 </p> 278 - <p className="text-xs mt-1"> 278 + <p className="mt-1 text-xs"> 279 279 You will be directed to your account to authorize 280 280 access. We never see your password and you can revoke 281 281 access anytime.
+18 -14
packages/web/src/pages/Results.tsx
··· 97 97 /> 98 98 99 99 {/* Platform Info Banner */} 100 - <div className="bg-firefly-banner dark:bg-firefly-banner-dark text-white relative overflow-hidden"> 100 + <div className="relative overflow-hidden bg-firefly-banner text-white dark:bg-firefly-banner-dark"> 101 101 {!reducedMotion && ( 102 102 <div className="absolute inset-0 opacity-20" aria-hidden="true"> 103 103 {[...Array(10)].map((_, i) => { 104 - const animations = ["animate-float-1", "animate-float-2", "animate-float-3"]; 104 + const animations = [ 105 + "animate-float-1", 106 + "animate-float-2", 107 + "animate-float-3", 108 + ]; 105 109 return ( 106 110 <div 107 111 key={i} 108 - className={`absolute w-1 h-1 bg-white rounded-full ${animations[i % 3]}`} 112 + className={`absolute size-1 rounded-full bg-white ${animations[i % 3]}`} 109 113 style={{ 110 114 left: `${Math.random() * 100}%`, 111 115 top: `${Math.random() * 100}%`, ··· 115 119 })} 116 120 </div> 117 121 )} 118 - <div className="max-w-3xl mx-auto px-4 py-6 relative"> 122 + <div className="relative mx-auto max-w-3xl px-4 py-6"> 119 123 <div className="flex items-center justify-between"> 120 124 <div className="flex items-center space-x-4"> 121 - <div className="w-12 h-12 bg-white/20 backdrop-blur rounded-xl flex items-center justify-center shadow-lg"> 122 - <Sparkles className="w-6 h-6 text-white" /> 125 + <div className="flex size-12 items-center justify-center rounded-xl bg-white/20 shadow-lg backdrop-blur"> 126 + <Sparkles className="size-6 text-white" /> 123 127 </div> 124 128 <div> 125 129 <h2 className="text-xl font-bold"> 126 130 {totalFound} Connections Found! 127 131 </h2> 128 - <p className="text-white/95 text-sm"> 132 + <p className="text-sm text-white/95"> 129 133 From {searchResults.length} {platform.name} follows 130 134 </p> 131 135 </div> ··· 141 145 </div> 142 146 143 147 {/* Action Buttons */} 144 - <div className="bg-white/95 dark:bg-slate-900 border-b-2 border-cyan-500/30 dark:border-purple-500/30 sticky top-0 z-10 backdrop-blur-sm"> 145 - <div className="max-w-3xl mx-auto px-4 py-3 flex space-x-2"> 148 + <div className="sticky top-0 z-10 border-b-2 border-cyan-500/30 bg-white/95 backdrop-blur-sm dark:border-purple-500/30 dark:bg-slate-900"> 149 + <div className="mx-auto flex max-w-3xl space-x-2 px-4 py-3"> 146 150 <Button onClick={onSelectAll} variant="primary" className="flex-1"> 147 151 Select All 148 152 </Button> ··· 157 161 </div> 158 162 159 163 {/* Feed Results */} 160 - <div className="max-w-3xl mx-auto px-4 py-4"> 164 + <div className="mx-auto max-w-3xl p-4"> 161 165 <VirtualizedResultsList 162 166 results={sortedResults} 163 167 expandedResults={expandedResults} ··· 170 174 171 175 {/* Fixed Bottom Action Bar */} 172 176 {totalSelected > 0 && ( 173 - <div className="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-white via-white to-transparent dark:from-slate-900 dark:via-slate-900 dark:to-transparent pt-8 pb-6"> 174 - <div className="max-w-3xl mx-auto px-4"> 177 + <div className="fixed inset-x-0 bottom-0 bg-gradient-to-t from-white via-white to-transparent pb-6 pt-8 dark:from-slate-900 dark:via-slate-900 dark:to-transparent"> 178 + <div className="mx-auto max-w-3xl px-4"> 175 179 <button 176 180 onClick={onFollowSelected} 177 181 disabled={isFollowing} 178 - className="w-full bg-firefly-banner dark:bg-firefly-banner-dark text-white hover:from-amber-600 hover:via-orange-600 hover:to-pink-600 py-5 rounded-2xl font-bold text-lg transition-all shadow-2xl hover:shadow-3xl flex items-center justify-center space-x-3 hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100" 182 + className="hover:shadow-3xl flex w-full items-center justify-center space-x-3 rounded-2xl bg-firefly-banner py-5 text-lg font-bold text-white shadow-2xl transition-all hover:scale-105 hover:from-amber-600 hover:via-orange-600 hover:to-pink-600 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100 dark:bg-firefly-banner-dark" 179 183 > 180 184 <FaviconIcon 181 185 url={destinationApp.icon} 182 186 alt={destinationApp.name} 183 - className="w-5 h-5" 187 + className="size-5" 184 188 useButtonStyling={true} 185 189 /> 186 190 <span>
+22 -22
packages/web/src/pages/Settings.tsx
··· 50 50 <Card 51 51 variant="upload" 52 52 onClick={onOpenWizard} 53 - className="w-full flex items-start space-x-4 p-4 text-left" 53 + className="flex w-full items-start space-x-4 p-4 text-left" 54 54 > 55 - <div className="w-12 h-12 bg-firefly-banner dark:bg-firefly-banner-dark rounded-xl flex items-center justify-center flex-shrink-0 shadow-md"> 56 - <SettingsIcon className="w-6 h-6 text-white" /> 55 + <div className="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-firefly-banner shadow-md dark:bg-firefly-banner-dark"> 56 + <SettingsIcon className="size-6 text-white" /> 57 57 </div> 58 - <div className="flex-1 min-w-0"> 59 - <div className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2 mb-1"> 60 - <div className="font-semibold text-purple-950 dark:text-cyan-50 leading-tight"> 58 + <div className="min-w-0 flex-1"> 59 + <div className="mb-1 flex flex-wrap items-start justify-between gap-x-4 gap-y-2"> 60 + <div className="font-semibold leading-tight text-purple-950 dark:text-cyan-50"> 61 61 Run Setup Wizard 62 62 </div> 63 63 </div> 64 - <p className="text-sm text-purple-750 dark:text-cyan-250 leading-tight"> 64 + <p className="text-sm leading-tight text-purple-750 dark:text-cyan-250"> 65 65 Configure platform destinations, privacy, and automation settings 66 66 </p> 67 67 </div> 68 - <ChevronRight className="w-5 h-5 text-purple-500 dark:text-cyan-400 flex-shrink-0 self-center" /> 68 + <ChevronRight className="size-5 flex-shrink-0 self-center text-purple-500 dark:text-cyan-400" /> 69 69 </Card> 70 70 71 71 {/* Current Configuration */} 72 - <div className="mt-2 py-2 px-3"> 73 - <h3 className="font-semibold text-purple-950 dark:text-cyan-50 mb-3"> 72 + <div className="mt-2 px-3 py-2"> 73 + <h3 className="mb-3 font-semibold text-purple-950 dark:text-cyan-50"> 74 74 Current Configuration 75 75 </h3> 76 - <div className="gap-8 flex flex-wrap text-sm"> 76 + <div className="flex flex-wrap gap-8 text-sm"> 77 77 <div> 78 - <div className="text-purple-750 dark:text-cyan-250 mb-1"> 78 + <div className="mb-1 text-purple-750 dark:text-cyan-250"> 79 79 Data Storage 80 80 </div> 81 81 <Badge variant="status"> ··· 83 83 </Badge> 84 84 </div> 85 85 <div> 86 - <div className="text-purple-750 dark:text-cyan-250 mb-1"> 86 + <div className="mb-1 text-purple-750 dark:text-cyan-250"> 87 87 Automation 88 88 </div> 89 89 <Badge variant="status"> ··· 93 93 </Badge> 94 94 </div> 95 95 <div> 96 - <div className="text-purple-750 dark:text-cyan-250 mb-1"> 96 + <div className="mb-1 text-purple-750 dark:text-cyan-250"> 97 97 Wizard 98 98 </div> 99 99 <Badge variant="status"> ··· 110 110 description="Where matches should go for each platform" 111 111 divider 112 112 > 113 - <Card className="mt-3 px-3 py-2 rounded-lg border-orange-650/50 dark:border-amber-400/50"> 113 + <Card className="mt-3 rounded-lg border-orange-650/50 px-3 py-2 dark:border-amber-400/50"> 114 114 <p className="text-sm text-purple-900 dark:text-cyan-100"> 115 115 💡 <strong>Tip:</strong> Choose different apps for different 116 116 platforms based on content type. For example, send TikTok matches to ··· 118 118 </p> 119 119 </Card> 120 120 121 - <div className="py-2 space-y-0"> 121 + <div className="space-y-0 py-2"> 122 122 {Object.entries(PLATFORMS).map(([key, p]) => { 123 123 const currentDestination = 124 124 userSettings.platformDestinations[ ··· 128 128 return ( 129 129 <div 130 130 key={key} 131 - className="flex items-center justify-between gap-3 px-3 py-2 rounded-xl transition-colors" 131 + className="flex items-center justify-between gap-3 rounded-xl px-3 py-2 transition-colors" 132 132 > 133 133 <PlatformBadge 134 134 platformKey={key} 135 135 size="sm" 136 - className="flex-1 min-w-0" 136 + className="min-w-0 flex-1" 137 137 /> 138 138 <DropdownWithIcons 139 139 value={currentDestination} ··· 152 152 title="Privacy & Data" 153 153 description="Control how your data is stored" 154 154 > 155 - <div className="px-3 space-y-4"> 155 + <div className="space-y-4 px-3"> 156 156 {/* Save Data Toggle */} 157 157 <Toggle 158 158 checked={userSettings.saveData} ··· 176 176 /> 177 177 178 178 {userSettings.enableAutomation && ( 179 - <div className="flex items-center gap-3 px-0 mt-4"> 180 - <label className="text-sm font-medium text-purple-950 dark:text-cyan-50 whitespace-nowrap"> 179 + <div className="mt-4 flex items-center gap-3 px-0"> 180 + <label className="whitespace-nowrap text-sm font-medium text-purple-950 dark:text-cyan-50"> 181 181 Frequency 182 182 </label> 183 183 <select ··· 190 190 | "Quarterly", 191 191 }) 192 192 } 193 - className="flex-1 px-3 py-2 bg-white dark:bg-slate-800 border border-cyan-500/30 dark:border-purple-500/30 rounded-lg text-sm text-purple-950 dark:text-cyan-50 hover:border-cyan-400 dark:hover:border-purple-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:focus:ring-amber-400 transition-colors" 193 + className="flex-1 rounded-lg border border-cyan-500/30 bg-white px-3 py-2 text-sm text-purple-950 transition-colors hover:border-cyan-400 focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-purple-500/30 dark:bg-slate-800 dark:text-cyan-50 dark:hover:border-purple-400 dark:focus:ring-amber-400" 194 194 > 195 195 <option value="daily">Check daily</option> 196 196 <option value="weekly">Check weekly</option>
+2 -2
packages/web/src/stores/useSettingsStore.ts
··· 47 47 state.setIsLoading(false); 48 48 } 49 49 }, 50 - }, 51 - ), 50 + } 51 + ) 52 52 );
+1936
pnpm-lock.yaml
··· 230 230 specifier: ^5.0.9 231 231 version: 5.0.9(@types/react@19.2.7)(react@18.3.1) 232 232 devDependencies: 233 + '@eslint/js': 234 + specifier: ^9.39.0 235 + version: 9.39.2 233 236 '@types/jszip': 234 237 specifier: ^3.4.0 235 238 version: 3.4.1 ··· 239 242 '@types/react-dom': 240 243 specifier: ^19.1.9 241 244 version: 19.2.3(@types/react@19.2.7) 245 + '@typescript-eslint/eslint-plugin': 246 + specifier: ^8.20.0 247 + version: 8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 248 + '@typescript-eslint/parser': 249 + specifier: ^8.20.0 250 + version: 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 242 251 '@vitejs/plugin-react': 243 252 specifier: ^4.2.1 244 253 version: 4.7.0(vite@5.4.21(@types/node@24.10.4)) 245 254 autoprefixer: 246 255 specifier: ^10.4.21 247 256 version: 10.4.23(postcss@8.5.6) 257 + eslint: 258 + specifier: ^9.19.0 259 + version: 9.39.2(jiti@1.21.7) 260 + eslint-plugin-jsx-a11y: 261 + specifier: ^6.10.2 262 + version: 6.10.2(eslint@9.39.2(jiti@1.21.7)) 263 + eslint-plugin-react: 264 + specifier: ^7.37.2 265 + version: 7.37.5(eslint@9.39.2(jiti@1.21.7)) 266 + eslint-plugin-react-hooks: 267 + specifier: ^5.1.0 268 + version: 5.2.0(eslint@9.39.2(jiti@1.21.7)) 269 + eslint-plugin-tailwindcss: 270 + specifier: ^3.17.5 271 + version: 3.18.2(tailwindcss@3.4.19) 248 272 postcss: 249 273 specifier: ^8.5.6 250 274 version: 8.5.6 275 + prettier: 276 + specifier: ^3.4.2 277 + version: 3.8.0 278 + prettier-plugin-tailwindcss: 279 + specifier: ^0.6.11 280 + version: 0.6.14(prettier@3.8.0) 251 281 tailwindcss: 252 282 specifier: ^3.4.0 253 283 version: 3.4.19 ··· 898 928 cpu: [x64] 899 929 os: [win32] 900 930 931 + '@eslint-community/eslint-utils@4.9.1': 932 + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} 933 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 934 + peerDependencies: 935 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 936 + 937 + '@eslint-community/regexpp@4.12.2': 938 + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} 939 + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 940 + 941 + '@eslint/config-array@0.21.1': 942 + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} 943 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 944 + 945 + '@eslint/config-helpers@0.4.2': 946 + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} 947 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 948 + 949 + '@eslint/core@0.17.0': 950 + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} 951 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 952 + 953 + '@eslint/eslintrc@3.3.3': 954 + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} 955 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 956 + 957 + '@eslint/js@9.39.2': 958 + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} 959 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 960 + 961 + '@eslint/object-schema@2.1.7': 962 + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} 963 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 964 + 965 + '@eslint/plugin-kit@0.4.1': 966 + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} 967 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 968 + 901 969 '@fastify/busboy@3.2.0': 902 970 resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} 971 + 972 + '@humanfs/core@0.19.1': 973 + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 974 + engines: {node: '>=18.18.0'} 975 + 976 + '@humanfs/node@0.16.7': 977 + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 978 + engines: {node: '>=18.18.0'} 979 + 980 + '@humanwhocodes/module-importer@1.0.1': 981 + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 982 + engines: {node: '>=12.22'} 983 + 984 + '@humanwhocodes/retry@0.4.3': 985 + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 986 + engines: {node: '>=18.18'} 903 987 904 988 '@icons-pack/react-simple-icons@13.8.0': 905 989 resolution: {integrity: sha512-iZrhL1fSklfCCVn68IYHaAoKfcby3RakUTn2tRPyHBkhr2tkYqeQbjJWf+NizIYBzKBn2IarDJXmTdXd6CuEfw==} ··· 1227 1311 '@types/har-format@1.2.16': 1228 1312 resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} 1229 1313 1314 + '@types/json-schema@7.0.15': 1315 + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 1316 + 1230 1317 '@types/jszip@3.4.1': 1231 1318 resolution: {integrity: sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A==} 1232 1319 deprecated: This is a stub types definition. jszip provides its own type definitions, so you do not need this installed. ··· 1260 1347 '@types/yauzl@2.10.3': 1261 1348 resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} 1262 1349 1350 + '@typescript-eslint/eslint-plugin@8.53.1': 1351 + resolution: {integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==} 1352 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1353 + peerDependencies: 1354 + '@typescript-eslint/parser': ^8.53.1 1355 + eslint: ^8.57.0 || ^9.0.0 1356 + typescript: '>=4.8.4 <6.0.0' 1357 + 1358 + '@typescript-eslint/parser@8.53.1': 1359 + resolution: {integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==} 1360 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1361 + peerDependencies: 1362 + eslint: ^8.57.0 || ^9.0.0 1363 + typescript: '>=4.8.4 <6.0.0' 1364 + 1263 1365 '@typescript-eslint/project-service@8.50.1': 1264 1366 resolution: {integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==} 1265 1367 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1266 1368 peerDependencies: 1267 1369 typescript: '>=4.8.4 <6.0.0' 1268 1370 1371 + '@typescript-eslint/project-service@8.53.1': 1372 + resolution: {integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==} 1373 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1374 + peerDependencies: 1375 + typescript: '>=4.8.4 <6.0.0' 1376 + 1377 + '@typescript-eslint/scope-manager@8.53.1': 1378 + resolution: {integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==} 1379 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1380 + 1269 1381 '@typescript-eslint/tsconfig-utils@8.50.1': 1270 1382 resolution: {integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==} 1271 1383 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1272 1384 peerDependencies: 1273 1385 typescript: '>=4.8.4 <6.0.0' 1274 1386 1387 + '@typescript-eslint/tsconfig-utils@8.53.1': 1388 + resolution: {integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==} 1389 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1390 + peerDependencies: 1391 + typescript: '>=4.8.4 <6.0.0' 1392 + 1393 + '@typescript-eslint/type-utils@8.53.1': 1394 + resolution: {integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==} 1395 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1396 + peerDependencies: 1397 + eslint: ^8.57.0 || ^9.0.0 1398 + typescript: '>=4.8.4 <6.0.0' 1399 + 1275 1400 '@typescript-eslint/types@8.50.1': 1276 1401 resolution: {integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==} 1277 1402 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1278 1403 1404 + '@typescript-eslint/types@8.53.1': 1405 + resolution: {integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==} 1406 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1407 + 1279 1408 '@typescript-eslint/typescript-estree@8.50.1': 1280 1409 resolution: {integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==} 1281 1410 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1282 1411 peerDependencies: 1283 1412 typescript: '>=4.8.4 <6.0.0' 1284 1413 1414 + '@typescript-eslint/typescript-estree@8.53.1': 1415 + resolution: {integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==} 1416 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1417 + peerDependencies: 1418 + typescript: '>=4.8.4 <6.0.0' 1419 + 1420 + '@typescript-eslint/utils@8.53.1': 1421 + resolution: {integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==} 1422 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1423 + peerDependencies: 1424 + eslint: ^8.57.0 || ^9.0.0 1425 + typescript: '>=4.8.4 <6.0.0' 1426 + 1285 1427 '@typescript-eslint/visitor-keys@8.50.1': 1286 1428 resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==} 1429 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1430 + 1431 + '@typescript-eslint/visitor-keys@8.53.1': 1432 + resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} 1287 1433 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1288 1434 1289 1435 '@vercel/nft@0.29.4': ··· 1345 1491 peerDependencies: 1346 1492 acorn: ^8 1347 1493 1494 + acorn-jsx@5.3.2: 1495 + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 1496 + peerDependencies: 1497 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 1498 + 1348 1499 acorn@8.15.0: 1349 1500 resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 1350 1501 engines: {node: '>=0.4.0'} ··· 1357 1508 resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} 1358 1509 engines: {node: '>= 14'} 1359 1510 1511 + ajv@6.12.6: 1512 + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 1513 + 1360 1514 ansi-regex@5.0.1: 1361 1515 resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 1362 1516 engines: {node: '>=8'} ··· 1398 1552 argparse@2.0.1: 1399 1553 resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 1400 1554 1555 + aria-query@5.3.2: 1556 + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} 1557 + engines: {node: '>= 0.4'} 1558 + 1559 + array-buffer-byte-length@1.0.2: 1560 + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} 1561 + engines: {node: '>= 0.4'} 1562 + 1563 + array-includes@3.1.9: 1564 + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} 1565 + engines: {node: '>= 0.4'} 1566 + 1401 1567 array-union@2.1.0: 1402 1568 resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 1403 1569 engines: {node: '>=8'} 1404 1570 1571 + array.prototype.findlast@1.2.5: 1572 + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} 1573 + engines: {node: '>= 0.4'} 1574 + 1575 + array.prototype.flat@1.3.3: 1576 + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} 1577 + engines: {node: '>= 0.4'} 1578 + 1579 + array.prototype.flatmap@1.3.3: 1580 + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} 1581 + engines: {node: '>= 0.4'} 1582 + 1583 + array.prototype.tosorted@1.1.4: 1584 + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} 1585 + engines: {node: '>= 0.4'} 1586 + 1587 + arraybuffer.prototype.slice@1.0.4: 1588 + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} 1589 + engines: {node: '>= 0.4'} 1590 + 1405 1591 ast-module-types@6.0.1: 1406 1592 resolution: {integrity: sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==} 1407 1593 engines: {node: '>=18'} 1408 1594 1595 + ast-types-flow@0.0.8: 1596 + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} 1597 + 1598 + async-function@1.0.0: 1599 + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} 1600 + engines: {node: '>= 0.4'} 1601 + 1409 1602 async-sema@3.1.1: 1410 1603 resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} 1411 1604 ··· 1418 1611 hasBin: true 1419 1612 peerDependencies: 1420 1613 postcss: ^8.1.0 1614 + 1615 + available-typed-arrays@1.0.7: 1616 + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} 1617 + engines: {node: '>= 0.4'} 1421 1618 1422 1619 await-lock@2.2.2: 1423 1620 resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 1424 1621 1622 + axe-core@4.11.1: 1623 + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} 1624 + engines: {node: '>=4'} 1625 + 1626 + axobject-query@4.1.0: 1627 + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} 1628 + engines: {node: '>= 0.4'} 1629 + 1425 1630 b4a@1.7.3: 1426 1631 resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} 1427 1632 peerDependencies: ··· 1458 1663 boolbase@1.0.0: 1459 1664 resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} 1460 1665 1666 + brace-expansion@1.1.12: 1667 + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 1668 + 1461 1669 brace-expansion@2.0.2: 1462 1670 resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 1463 1671 ··· 1483 1691 buffer@6.0.3: 1484 1692 resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 1485 1693 1694 + call-bind-apply-helpers@1.0.2: 1695 + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 1696 + engines: {node: '>= 0.4'} 1697 + 1698 + call-bind@1.0.8: 1699 + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} 1700 + engines: {node: '>= 0.4'} 1701 + 1702 + call-bound@1.0.4: 1703 + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} 1704 + engines: {node: '>= 0.4'} 1705 + 1486 1706 callsite@1.0.0: 1487 1707 resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} 1488 1708 ··· 1503 1723 1504 1724 caniuse-lite@1.0.30001761: 1505 1725 resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} 1726 + 1727 + chalk@4.1.2: 1728 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 1729 + engines: {node: '>=10'} 1506 1730 1507 1731 chokidar@3.6.0: 1508 1732 resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} ··· 1575 1799 compress-commons@6.0.2: 1576 1800 resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} 1577 1801 engines: {node: '>= 14'} 1802 + 1803 + concat-map@0.0.1: 1804 + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 1578 1805 1579 1806 consola@3.4.2: 1580 1807 resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} ··· 1673 1900 1674 1901 csstype@3.2.3: 1675 1902 resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 1903 + 1904 + damerau-levenshtein@1.0.8: 1905 + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} 1906 + 1907 + data-view-buffer@1.0.2: 1908 + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} 1909 + engines: {node: '>= 0.4'} 1910 + 1911 + data-view-byte-length@1.0.2: 1912 + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} 1913 + engines: {node: '>= 0.4'} 1914 + 1915 + data-view-byte-offset@1.0.1: 1916 + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} 1917 + engines: {node: '>= 0.4'} 1676 1918 1677 1919 date-fns@4.1.0: 1678 1920 resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} ··· 1689 1931 decache@4.6.2: 1690 1932 resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} 1691 1933 1934 + deep-is@0.1.4: 1935 + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 1936 + 1937 + define-data-property@1.1.4: 1938 + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 1939 + engines: {node: '>= 0.4'} 1940 + 1941 + define-properties@1.2.1: 1942 + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 1943 + engines: {node: '>= 0.4'} 1944 + 1692 1945 detect-libc@2.1.2: 1693 1946 resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} 1694 1947 engines: {node: '>=8'} ··· 1749 2002 dlv@1.1.3: 1750 2003 resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 1751 2004 2005 + doctrine@2.1.0: 2006 + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} 2007 + engines: {node: '>=0.10.0'} 2008 + 1752 2009 dom-serializer@2.0.0: 1753 2010 resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} 1754 2011 ··· 1773 2030 resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} 1774 2031 engines: {node: '>=12'} 1775 2032 2033 + dunder-proto@1.0.1: 2034 + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 2035 + engines: {node: '>= 0.4'} 2036 + 1776 2037 eastasianwidth@0.2.0: 1777 2038 resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 1778 2039 ··· 1813 2074 error-ex@1.3.4: 1814 2075 resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} 1815 2076 2077 + es-abstract@1.24.1: 2078 + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} 2079 + engines: {node: '>= 0.4'} 2080 + 2081 + es-define-property@1.0.1: 2082 + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 2083 + engines: {node: '>= 0.4'} 2084 + 2085 + es-errors@1.3.0: 2086 + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 2087 + engines: {node: '>= 0.4'} 2088 + 2089 + es-iterator-helpers@1.2.2: 2090 + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} 2091 + engines: {node: '>= 0.4'} 2092 + 1816 2093 es-module-lexer@1.7.0: 1817 2094 resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} 1818 2095 2096 + es-object-atoms@1.1.1: 2097 + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 2098 + engines: {node: '>= 0.4'} 2099 + 2100 + es-set-tostringtag@2.1.0: 2101 + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} 2102 + engines: {node: '>= 0.4'} 2103 + 2104 + es-shim-unscopables@1.1.0: 2105 + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} 2106 + engines: {node: '>= 0.4'} 2107 + 2108 + es-to-primitive@1.3.0: 2109 + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} 2110 + engines: {node: '>= 0.4'} 2111 + 1819 2112 esbuild@0.19.12: 1820 2113 resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} 1821 2114 engines: {node: '>=12'} ··· 1839 2132 resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 1840 2133 engines: {node: '>=0.8.0'} 1841 2134 2135 + escape-string-regexp@4.0.0: 2136 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 2137 + engines: {node: '>=10'} 2138 + 1842 2139 escodegen@2.1.0: 1843 2140 resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} 1844 2141 engines: {node: '>=6.0'} 1845 2142 hasBin: true 1846 2143 2144 + eslint-plugin-jsx-a11y@6.10.2: 2145 + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} 2146 + engines: {node: '>=4.0'} 2147 + peerDependencies: 2148 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 2149 + 2150 + eslint-plugin-react-hooks@5.2.0: 2151 + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} 2152 + engines: {node: '>=10'} 2153 + peerDependencies: 2154 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 2155 + 2156 + eslint-plugin-react@7.37.5: 2157 + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} 2158 + engines: {node: '>=4'} 2159 + peerDependencies: 2160 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 2161 + 2162 + eslint-plugin-tailwindcss@3.18.2: 2163 + resolution: {integrity: sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==} 2164 + engines: {node: '>=18.12.0'} 2165 + peerDependencies: 2166 + tailwindcss: ^3.4.0 2167 + 2168 + eslint-scope@8.4.0: 2169 + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 2170 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 2171 + 2172 + eslint-visitor-keys@3.4.3: 2173 + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 2174 + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 2175 + 1847 2176 eslint-visitor-keys@4.2.1: 1848 2177 resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 1849 2178 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1850 2179 2180 + eslint@9.39.2: 2181 + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} 2182 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 2183 + hasBin: true 2184 + peerDependencies: 2185 + jiti: '*' 2186 + peerDependenciesMeta: 2187 + jiti: 2188 + optional: true 2189 + 1851 2190 esm-env@1.2.2: 1852 2191 resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 1853 2192 2193 + espree@10.4.0: 2194 + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 2195 + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 2196 + 1854 2197 esprima@4.0.1: 1855 2198 resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 1856 2199 engines: {node: '>=4'} 1857 2200 hasBin: true 1858 2201 2202 + esquery@1.7.0: 2203 + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} 2204 + engines: {node: '>=0.10'} 2205 + 2206 + esrecurse@4.3.0: 2207 + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 2208 + engines: {node: '>=4.0'} 2209 + 1859 2210 estraverse@5.3.0: 1860 2211 resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 1861 2212 engines: {node: '>=4.0'} ··· 1887 2238 engines: {node: '>= 10.17.0'} 1888 2239 hasBin: true 1889 2240 2241 + fast-deep-equal@3.1.3: 2242 + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 2243 + 1890 2244 fast-fifo@1.3.2: 1891 2245 resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} 1892 2246 ··· 1894 2248 resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 1895 2249 engines: {node: '>=8.6.0'} 1896 2250 2251 + fast-json-stable-stringify@2.1.0: 2252 + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 2253 + 2254 + fast-levenshtein@2.0.6: 2255 + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 2256 + 1897 2257 fastq@1.20.1: 1898 2258 resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} 1899 2259 ··· 1912 2272 fecha@4.2.3: 1913 2273 resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} 1914 2274 2275 + file-entry-cache@8.0.0: 2276 + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 2277 + engines: {node: '>=16.0.0'} 2278 + 1915 2279 file-uri-to-path@1.0.0: 1916 2280 resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} 1917 2281 ··· 1943 2307 resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 1944 2308 engines: {node: '>=8'} 1945 2309 2310 + find-up@5.0.0: 2311 + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 2312 + engines: {node: '>=10'} 2313 + 1946 2314 find-up@7.0.0: 1947 2315 resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} 1948 2316 engines: {node: '>=18'} 1949 2317 2318 + flat-cache@4.0.1: 2319 + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 2320 + engines: {node: '>=16'} 2321 + 2322 + flatted@3.3.3: 2323 + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 2324 + 1950 2325 fn.name@1.1.0: 1951 2326 resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} 2327 + 2328 + for-each@0.3.5: 2329 + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} 2330 + engines: {node: '>= 0.4'} 1952 2331 1953 2332 foreground-child@3.3.1: 1954 2333 resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} ··· 1969 2348 function-bind@1.1.2: 1970 2349 resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 1971 2350 2351 + function.prototype.name@1.1.8: 2352 + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} 2353 + engines: {node: '>= 0.4'} 2354 + 2355 + functions-have-names@1.2.3: 2356 + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} 2357 + 2358 + generator-function@2.0.1: 2359 + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} 2360 + engines: {node: '>= 0.4'} 2361 + 1972 2362 gensync@1.0.0-beta.2: 1973 2363 resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1974 2364 engines: {node: '>=6.9.0'} ··· 1981 2371 resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 1982 2372 engines: {node: 6.* || 8.* || >= 10.*} 1983 2373 2374 + get-intrinsic@1.3.0: 2375 + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 2376 + engines: {node: '>= 0.4'} 2377 + 2378 + get-proto@1.0.1: 2379 + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 2380 + engines: {node: '>= 0.4'} 2381 + 1984 2382 get-stream@5.2.0: 1985 2383 resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} 1986 2384 engines: {node: '>=8'} ··· 1988 2386 get-stream@8.0.1: 1989 2387 resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} 1990 2388 engines: {node: '>=16'} 2389 + 2390 + get-symbol-description@1.1.0: 2391 + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} 2392 + engines: {node: '>= 0.4'} 1991 2393 1992 2394 gh-pages@6.3.0: 1993 2395 resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} ··· 2006 2408 resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} 2007 2409 hasBin: true 2008 2410 2411 + globals@14.0.0: 2412 + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 2413 + engines: {node: '>=18'} 2414 + 2415 + globalthis@1.0.4: 2416 + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} 2417 + engines: {node: '>= 0.4'} 2418 + 2009 2419 globby@11.1.0: 2010 2420 resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 2011 2421 engines: {node: '>=10'} ··· 2015 2425 engines: {node: '>=0.6.0'} 2016 2426 hasBin: true 2017 2427 2428 + gopd@1.2.0: 2429 + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 2430 + engines: {node: '>= 0.4'} 2431 + 2018 2432 graceful-fs@4.2.11: 2019 2433 resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 2020 2434 2435 + has-bigints@1.1.0: 2436 + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} 2437 + engines: {node: '>= 0.4'} 2438 + 2439 + has-flag@4.0.0: 2440 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 2441 + engines: {node: '>=8'} 2442 + 2443 + has-property-descriptors@1.0.2: 2444 + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 2445 + 2446 + has-proto@1.2.0: 2447 + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} 2448 + engines: {node: '>= 0.4'} 2449 + 2450 + has-symbols@1.1.0: 2451 + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 2452 + engines: {node: '>= 0.4'} 2453 + 2454 + has-tostringtag@1.0.2: 2455 + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 2456 + engines: {node: '>= 0.4'} 2457 + 2021 2458 hasown@2.0.2: 2022 2459 resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 2023 2460 engines: {node: '>= 0.4'} ··· 2041 2478 resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 2042 2479 engines: {node: '>= 4'} 2043 2480 2481 + ignore@7.0.5: 2482 + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 2483 + engines: {node: '>= 4'} 2484 + 2044 2485 image-size@2.0.2: 2045 2486 resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} 2046 2487 engines: {node: '>=16.x'} ··· 2064 2505 inherits@2.0.4: 2065 2506 resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 2066 2507 2508 + internal-slot@1.1.0: 2509 + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} 2510 + engines: {node: '>= 0.4'} 2511 + 2067 2512 ipaddr.js@2.3.0: 2068 2513 resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} 2069 2514 engines: {node: '>= 10'} 2070 2515 2516 + is-array-buffer@3.0.5: 2517 + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} 2518 + engines: {node: '>= 0.4'} 2519 + 2071 2520 is-arrayish@0.2.1: 2072 2521 resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} 2073 2522 2523 + is-async-function@2.1.1: 2524 + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} 2525 + engines: {node: '>= 0.4'} 2526 + 2527 + is-bigint@1.1.0: 2528 + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} 2529 + engines: {node: '>= 0.4'} 2530 + 2074 2531 is-binary-path@2.1.0: 2075 2532 resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 2076 2533 engines: {node: '>=8'} 2077 2534 2535 + is-boolean-object@1.2.2: 2536 + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} 2537 + engines: {node: '>= 0.4'} 2538 + 2539 + is-callable@1.2.7: 2540 + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} 2541 + engines: {node: '>= 0.4'} 2542 + 2078 2543 is-core-module@2.16.1: 2079 2544 resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 2080 2545 engines: {node: '>= 0.4'} 2081 2546 2547 + is-data-view@1.0.2: 2548 + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} 2549 + engines: {node: '>= 0.4'} 2550 + 2551 + is-date-object@1.1.0: 2552 + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} 2553 + engines: {node: '>= 0.4'} 2554 + 2082 2555 is-extglob@2.1.1: 2083 2556 resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 2084 2557 engines: {node: '>=0.10.0'} 2558 + 2559 + is-finalizationregistry@1.1.1: 2560 + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} 2561 + engines: {node: '>= 0.4'} 2085 2562 2086 2563 is-fullwidth-code-point@3.0.0: 2087 2564 resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 2088 2565 engines: {node: '>=8'} 2089 2566 2567 + is-generator-function@1.1.2: 2568 + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} 2569 + engines: {node: '>= 0.4'} 2570 + 2090 2571 is-glob@4.0.3: 2091 2572 resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 2092 2573 engines: {node: '>=0.10.0'} 2093 2574 2575 + is-map@2.0.3: 2576 + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} 2577 + engines: {node: '>= 0.4'} 2578 + 2579 + is-negative-zero@2.0.3: 2580 + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} 2581 + engines: {node: '>= 0.4'} 2582 + 2583 + is-number-object@1.1.1: 2584 + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} 2585 + engines: {node: '>= 0.4'} 2586 + 2094 2587 is-number@7.0.0: 2095 2588 resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 2096 2589 engines: {node: '>=0.12.0'} ··· 2103 2596 resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} 2104 2597 engines: {node: '>=8'} 2105 2598 2599 + is-regex@1.2.1: 2600 + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} 2601 + engines: {node: '>= 0.4'} 2602 + 2603 + is-set@2.0.3: 2604 + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} 2605 + engines: {node: '>= 0.4'} 2606 + 2607 + is-shared-array-buffer@1.0.4: 2608 + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} 2609 + engines: {node: '>= 0.4'} 2610 + 2106 2611 is-stream@2.0.1: 2107 2612 resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 2108 2613 engines: {node: '>=8'} ··· 2115 2620 resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} 2116 2621 engines: {node: '>=18'} 2117 2622 2623 + is-string@1.1.1: 2624 + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} 2625 + engines: {node: '>= 0.4'} 2626 + 2627 + is-symbol@1.1.1: 2628 + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} 2629 + engines: {node: '>= 0.4'} 2630 + 2631 + is-typed-array@1.1.15: 2632 + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} 2633 + engines: {node: '>= 0.4'} 2634 + 2118 2635 is-url-superb@4.0.0: 2119 2636 resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} 2120 2637 engines: {node: '>=10'} ··· 2122 2639 is-url@1.2.4: 2123 2640 resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} 2124 2641 2642 + is-weakmap@2.0.2: 2643 + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} 2644 + engines: {node: '>= 0.4'} 2645 + 2646 + is-weakref@1.1.1: 2647 + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} 2648 + engines: {node: '>= 0.4'} 2649 + 2650 + is-weakset@2.0.4: 2651 + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} 2652 + engines: {node: '>= 0.4'} 2653 + 2125 2654 isarray@1.0.0: 2126 2655 resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 2127 2656 2657 + isarray@2.0.5: 2658 + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} 2659 + 2128 2660 isexe@2.0.0: 2129 2661 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 2130 2662 2131 2663 iso-datestring-validator@2.2.2: 2132 2664 resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 2665 + 2666 + iterator.prototype@1.1.5: 2667 + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} 2668 + engines: {node: '>= 0.4'} 2133 2669 2134 2670 jackspeak@3.4.3: 2135 2671 resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} ··· 2162 2698 engines: {node: '>=6'} 2163 2699 hasBin: true 2164 2700 2701 + json-buffer@3.0.1: 2702 + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 2703 + 2165 2704 json-parse-even-better-errors@2.3.1: 2166 2705 resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} 2706 + 2707 + json-schema-traverse@0.4.1: 2708 + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 2709 + 2710 + json-stable-stringify-without-jsonify@1.0.1: 2711 + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 2167 2712 2168 2713 json5@2.2.3: 2169 2714 resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} ··· 2173 2718 jsonfile@6.2.0: 2174 2719 resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} 2175 2720 2721 + jsx-ast-utils@3.3.5: 2722 + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} 2723 + engines: {node: '>=4.0'} 2724 + 2176 2725 jszip@3.10.1: 2177 2726 resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} 2178 2727 ··· 2184 2733 resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} 2185 2734 engines: {node: '>=18'} 2186 2735 2736 + keyv@4.5.4: 2737 + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 2738 + 2187 2739 kuler@2.0.0: 2188 2740 resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} 2189 2741 ··· 2192 2744 engines: {node: '>=8'} 2193 2745 hasBin: true 2194 2746 2747 + language-subtag-registry@0.3.23: 2748 + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} 2749 + 2750 + language-tags@1.0.9: 2751 + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} 2752 + engines: {node: '>=0.10'} 2753 + 2195 2754 lazystream@1.0.1: 2196 2755 resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} 2197 2756 engines: {node: '>= 0.6.3'} 2198 2757 2758 + levn@0.4.1: 2759 + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 2760 + engines: {node: '>= 0.8.0'} 2761 + 2199 2762 lie@3.3.0: 2200 2763 resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} 2201 2764 ··· 2210 2773 resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 2211 2774 engines: {node: '>=8'} 2212 2775 2776 + locate-path@6.0.0: 2777 + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 2778 + engines: {node: '>=10'} 2779 + 2213 2780 locate-path@7.2.0: 2214 2781 resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} 2215 2782 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} ··· 2217 2784 lodash.memoize@4.1.2: 2218 2785 resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} 2219 2786 2787 + lodash.merge@4.6.2: 2788 + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 2789 + 2220 2790 lodash.uniq@4.5.0: 2221 2791 resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} 2222 2792 ··· 2256 2826 resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} 2257 2827 engines: {node: '>=8'} 2258 2828 2829 + math-intrinsics@1.1.0: 2830 + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 2831 + engines: {node: '>= 0.4'} 2832 + 2259 2833 mdn-data@2.0.28: 2260 2834 resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} 2261 2835 ··· 2281 2855 resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} 2282 2856 engines: {node: '>=12'} 2283 2857 2858 + minimatch@3.1.2: 2859 + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 2860 + 2284 2861 minimatch@5.1.6: 2285 2862 resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 2286 2863 engines: {node: '>=10'} ··· 2318 2895 resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 2319 2896 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2320 2897 hasBin: true 2898 + 2899 + natural-compare@1.4.0: 2900 + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 2321 2901 2322 2902 no-case@3.0.4: 2323 2903 resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} ··· 2374 2954 resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 2375 2955 engines: {node: '>= 6'} 2376 2956 2957 + object-inspect@1.13.4: 2958 + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 2959 + engines: {node: '>= 0.4'} 2960 + 2961 + object-keys@1.1.1: 2962 + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 2963 + engines: {node: '>= 0.4'} 2964 + 2965 + object.assign@4.1.7: 2966 + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} 2967 + engines: {node: '>= 0.4'} 2968 + 2969 + object.entries@1.1.9: 2970 + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} 2971 + engines: {node: '>= 0.4'} 2972 + 2973 + object.fromentries@2.0.8: 2974 + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} 2975 + engines: {node: '>= 0.4'} 2976 + 2977 + object.values@1.2.1: 2978 + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} 2979 + engines: {node: '>= 0.4'} 2980 + 2377 2981 once@1.4.0: 2378 2982 resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 2379 2983 ··· 2384 2988 resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} 2385 2989 engines: {node: '>=12'} 2386 2990 2991 + optionator@0.9.4: 2992 + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 2993 + engines: {node: '>= 0.8.0'} 2994 + 2995 + own-keys@1.0.1: 2996 + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} 2997 + engines: {node: '>= 0.4'} 2998 + 2387 2999 p-event@6.0.1: 2388 3000 resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} 2389 3001 engines: {node: '>=16.17'} ··· 2392 3004 resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 2393 3005 engines: {node: '>=6'} 2394 3006 3007 + p-limit@3.1.0: 3008 + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 3009 + engines: {node: '>=10'} 3010 + 2395 3011 p-limit@4.0.0: 2396 3012 resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} 2397 3013 engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} ··· 2399 3015 p-locate@4.1.0: 2400 3016 resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 2401 3017 engines: {node: '>=8'} 3018 + 3019 + p-locate@5.0.0: 3020 + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 3021 + engines: {node: '>=10'} 2402 3022 2403 3023 p-locate@6.0.0: 2404 3024 resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} ··· 2501 3121 pkg-dir@4.2.0: 2502 3122 resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} 2503 3123 engines: {node: '>=8'} 3124 + 3125 + possible-typed-array-names@1.1.0: 3126 + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} 3127 + engines: {node: '>= 0.4'} 2504 3128 2505 3129 postcss-calc@10.1.1: 2506 3130 resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} ··· 2742 3366 engines: {node: '>=18'} 2743 3367 hasBin: true 2744 3368 3369 + prelude-ls@1.2.1: 3370 + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 3371 + engines: {node: '>= 0.8.0'} 3372 + 3373 + prettier-plugin-tailwindcss@0.6.14: 3374 + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} 3375 + engines: {node: '>=14.21.3'} 3376 + peerDependencies: 3377 + '@ianvs/prettier-plugin-sort-imports': '*' 3378 + '@prettier/plugin-hermes': '*' 3379 + '@prettier/plugin-oxc': '*' 3380 + '@prettier/plugin-pug': '*' 3381 + '@shopify/prettier-plugin-liquid': '*' 3382 + '@trivago/prettier-plugin-sort-imports': '*' 3383 + '@zackad/prettier-plugin-twig': '*' 3384 + prettier: ^3.0 3385 + prettier-plugin-astro: '*' 3386 + prettier-plugin-css-order: '*' 3387 + prettier-plugin-import-sort: '*' 3388 + prettier-plugin-jsdoc: '*' 3389 + prettier-plugin-marko: '*' 3390 + prettier-plugin-multiline-arrays: '*' 3391 + prettier-plugin-organize-attributes: '*' 3392 + prettier-plugin-organize-imports: '*' 3393 + prettier-plugin-sort-imports: '*' 3394 + prettier-plugin-style-order: '*' 3395 + prettier-plugin-svelte: '*' 3396 + peerDependenciesMeta: 3397 + '@ianvs/prettier-plugin-sort-imports': 3398 + optional: true 3399 + '@prettier/plugin-hermes': 3400 + optional: true 3401 + '@prettier/plugin-oxc': 3402 + optional: true 3403 + '@prettier/plugin-pug': 3404 + optional: true 3405 + '@shopify/prettier-plugin-liquid': 3406 + optional: true 3407 + '@trivago/prettier-plugin-sort-imports': 3408 + optional: true 3409 + '@zackad/prettier-plugin-twig': 3410 + optional: true 3411 + prettier-plugin-astro: 3412 + optional: true 3413 + prettier-plugin-css-order: 3414 + optional: true 3415 + prettier-plugin-import-sort: 3416 + optional: true 3417 + prettier-plugin-jsdoc: 3418 + optional: true 3419 + prettier-plugin-marko: 3420 + optional: true 3421 + prettier-plugin-multiline-arrays: 3422 + optional: true 3423 + prettier-plugin-organize-attributes: 3424 + optional: true 3425 + prettier-plugin-organize-imports: 3426 + optional: true 3427 + prettier-plugin-sort-imports: 3428 + optional: true 3429 + prettier-plugin-style-order: 3430 + optional: true 3431 + prettier-plugin-svelte: 3432 + optional: true 3433 + 3434 + prettier@3.8.0: 3435 + resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==} 3436 + engines: {node: '>=14'} 3437 + hasBin: true 3438 + 2745 3439 process-nextick-args@2.0.1: 2746 3440 resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 2747 3441 ··· 2749 3443 resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 2750 3444 engines: {node: '>= 0.6.0'} 2751 3445 3446 + prop-types@15.8.1: 3447 + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} 3448 + 2752 3449 pump@3.0.3: 2753 3450 resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} 3451 + 3452 + punycode@2.3.1: 3453 + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 3454 + engines: {node: '>=6'} 2754 3455 2755 3456 queue-microtask@1.2.3: 2756 3457 resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} ··· 2762 3463 resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} 2763 3464 peerDependencies: 2764 3465 react: ^18.3.1 3466 + 3467 + react-is@16.13.1: 3468 + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} 2765 3469 2766 3470 react-refresh@0.17.0: 2767 3471 resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} ··· 2821 3525 resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} 2822 3526 engines: {node: '>= 14.18.0'} 2823 3527 3528 + reflect.getprototypeof@1.0.10: 3529 + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} 3530 + engines: {node: '>= 0.4'} 3531 + 3532 + regexp.prototype.flags@1.5.4: 3533 + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} 3534 + engines: {node: '>= 0.4'} 3535 + 2824 3536 remove-trailing-separator@1.1.0: 2825 3537 resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} 2826 3538 ··· 2860 3572 run-parallel@1.2.0: 2861 3573 resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 2862 3574 3575 + safe-array-concat@1.1.3: 3576 + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} 3577 + engines: {node: '>=0.4'} 3578 + 2863 3579 safe-buffer@5.1.2: 2864 3580 resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 2865 3581 2866 3582 safe-buffer@5.2.1: 2867 3583 resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 2868 3584 3585 + safe-push-apply@1.0.0: 3586 + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} 3587 + engines: {node: '>= 0.4'} 3588 + 3589 + safe-regex-test@1.1.0: 3590 + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} 3591 + engines: {node: '>= 0.4'} 3592 + 2869 3593 safe-stable-stringify@2.5.0: 2870 3594 resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} 2871 3595 engines: {node: '>=10'} ··· 2888 3612 set-cookie-parser@2.7.2: 2889 3613 resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} 2890 3614 3615 + set-function-length@1.2.2: 3616 + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 3617 + engines: {node: '>= 0.4'} 3618 + 3619 + set-function-name@2.0.2: 3620 + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} 3621 + engines: {node: '>= 0.4'} 3622 + 3623 + set-proto@1.0.0: 3624 + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} 3625 + engines: {node: '>= 0.4'} 3626 + 2891 3627 setimmediate@1.0.5: 2892 3628 resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} 2893 3629 ··· 2899 3635 resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 2900 3636 engines: {node: '>=8'} 2901 3637 3638 + side-channel-list@1.0.0: 3639 + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 3640 + engines: {node: '>= 0.4'} 3641 + 3642 + side-channel-map@1.0.1: 3643 + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} 3644 + engines: {node: '>= 0.4'} 3645 + 3646 + side-channel-weakmap@1.0.2: 3647 + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} 3648 + engines: {node: '>= 0.4'} 3649 + 3650 + side-channel@1.1.0: 3651 + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} 3652 + engines: {node: '>= 0.4'} 3653 + 2902 3654 signal-exit@4.1.0: 2903 3655 resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 2904 3656 engines: {node: '>=14'} ··· 2935 3687 2936 3688 stack-trace@0.0.10: 2937 3689 resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} 3690 + 3691 + stop-iteration-iterator@1.1.0: 3692 + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} 3693 + engines: {node: '>= 0.4'} 2938 3694 2939 3695 streamx@2.23.0: 2940 3696 resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} ··· 2947 3703 resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 2948 3704 engines: {node: '>=12'} 2949 3705 3706 + string.prototype.includes@2.0.1: 3707 + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} 3708 + engines: {node: '>= 0.4'} 3709 + 3710 + string.prototype.matchall@4.0.12: 3711 + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} 3712 + engines: {node: '>= 0.4'} 3713 + 3714 + string.prototype.repeat@1.0.0: 3715 + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} 3716 + 3717 + string.prototype.trim@1.2.10: 3718 + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} 3719 + engines: {node: '>= 0.4'} 3720 + 3721 + string.prototype.trimend@1.0.9: 3722 + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} 3723 + engines: {node: '>= 0.4'} 3724 + 3725 + string.prototype.trimstart@1.0.8: 3726 + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} 3727 + engines: {node: '>= 0.4'} 3728 + 2950 3729 string_decoder@1.1.1: 2951 3730 resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 2952 3731 ··· 2965 3744 resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} 2966 3745 engines: {node: '>=12'} 2967 3746 3747 + strip-json-comments@3.1.1: 3748 + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 3749 + engines: {node: '>=8'} 3750 + 2968 3751 strip-outer@1.0.1: 2969 3752 resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} 2970 3753 engines: {node: '>=0.10.0'} ··· 2980 3763 engines: {node: '>=16 || 14 >=14.17'} 2981 3764 hasBin: true 2982 3765 3766 + supports-color@7.2.0: 3767 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 3768 + engines: {node: '>=8'} 3769 + 2983 3770 supports-preserve-symlinks-flag@1.0.0: 2984 3771 resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 2985 3772 engines: {node: '>= 0.4'} ··· 3056 3843 peerDependencies: 3057 3844 typescript: '>=4.8.4' 3058 3845 3846 + ts-api-utils@2.4.0: 3847 + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} 3848 + engines: {node: '>=18.12'} 3849 + peerDependencies: 3850 + typescript: '>=4.8.4' 3851 + 3059 3852 ts-interface-checker@0.1.13: 3060 3853 resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 3061 3854 3062 3855 tslib@2.8.1: 3063 3856 resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 3064 3857 3858 + type-check@0.4.0: 3859 + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 3860 + engines: {node: '>= 0.8.0'} 3861 + 3065 3862 type-fest@4.41.0: 3066 3863 resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} 3067 3864 engines: {node: '>=16'} 3068 3865 3866 + typed-array-buffer@1.0.3: 3867 + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} 3868 + engines: {node: '>= 0.4'} 3869 + 3870 + typed-array-byte-length@1.0.3: 3871 + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} 3872 + engines: {node: '>= 0.4'} 3873 + 3874 + typed-array-byte-offset@1.0.4: 3875 + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} 3876 + engines: {node: '>= 0.4'} 3877 + 3878 + typed-array-length@1.0.7: 3879 + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} 3880 + engines: {node: '>= 0.4'} 3881 + 3069 3882 typescript@5.9.3: 3070 3883 resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 3071 3884 engines: {node: '>=14.17'} ··· 3074 3887 uint8arrays@3.0.0: 3075 3888 resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 3076 3889 3890 + unbox-primitive@1.1.0: 3891 + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} 3892 + engines: {node: '>= 0.4'} 3893 + 3077 3894 undici-types@6.21.0: 3078 3895 resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 3079 3896 ··· 3104 3921 hasBin: true 3105 3922 peerDependencies: 3106 3923 browserslist: '>= 4.21.0' 3924 + 3925 + uri-js@4.4.1: 3926 + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 3107 3927 3108 3928 urlpattern-polyfill@10.1.0: 3109 3929 resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} ··· 3166 3986 whatwg-url@5.0.0: 3167 3987 resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} 3168 3988 3989 + which-boxed-primitive@1.1.1: 3990 + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} 3991 + engines: {node: '>= 0.4'} 3992 + 3993 + which-builtin-type@1.2.1: 3994 + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} 3995 + engines: {node: '>= 0.4'} 3996 + 3997 + which-collection@1.0.2: 3998 + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} 3999 + engines: {node: '>= 0.4'} 4000 + 4001 + which-typed-array@1.1.20: 4002 + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} 4003 + engines: {node: '>= 0.4'} 4004 + 3169 4005 which@2.0.2: 3170 4006 resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 3171 4007 engines: {node: '>= 8'} ··· 3178 4014 winston@3.19.0: 3179 4015 resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} 3180 4016 engines: {node: '>= 12.0.0'} 4017 + 4018 + word-wrap@1.2.5: 4019 + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 4020 + engines: {node: '>=0.10.0'} 3181 4021 3182 4022 wrap-ansi@7.0.0: 3183 4023 resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} ··· 3219 4059 3220 4060 yauzl@2.10.0: 3221 4061 resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} 4062 + 4063 + yocto-queue@0.1.0: 4064 + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 4065 + engines: {node: '>=10'} 3222 4066 3223 4067 yocto-queue@1.2.2: 3224 4068 resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} ··· 3790 4634 '@esbuild/win32-x64@0.27.2': 3791 4635 optional: true 3792 4636 4637 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@1.21.7))': 4638 + dependencies: 4639 + eslint: 9.39.2(jiti@1.21.7) 4640 + eslint-visitor-keys: 3.4.3 4641 + 4642 + '@eslint-community/regexpp@4.12.2': {} 4643 + 4644 + '@eslint/config-array@0.21.1': 4645 + dependencies: 4646 + '@eslint/object-schema': 2.1.7 4647 + debug: 4.4.3 4648 + minimatch: 3.1.2 4649 + transitivePeerDependencies: 4650 + - supports-color 4651 + 4652 + '@eslint/config-helpers@0.4.2': 4653 + dependencies: 4654 + '@eslint/core': 0.17.0 4655 + 4656 + '@eslint/core@0.17.0': 4657 + dependencies: 4658 + '@types/json-schema': 7.0.15 4659 + 4660 + '@eslint/eslintrc@3.3.3': 4661 + dependencies: 4662 + ajv: 6.12.6 4663 + debug: 4.4.3 4664 + espree: 10.4.0 4665 + globals: 14.0.0 4666 + ignore: 5.3.2 4667 + import-fresh: 3.3.1 4668 + js-yaml: 4.1.1 4669 + minimatch: 3.1.2 4670 + strip-json-comments: 3.1.1 4671 + transitivePeerDependencies: 4672 + - supports-color 4673 + 4674 + '@eslint/js@9.39.2': {} 4675 + 4676 + '@eslint/object-schema@2.1.7': {} 4677 + 4678 + '@eslint/plugin-kit@0.4.1': 4679 + dependencies: 4680 + '@eslint/core': 0.17.0 4681 + levn: 0.4.1 4682 + 3793 4683 '@fastify/busboy@3.2.0': {} 4684 + 4685 + '@humanfs/core@0.19.1': {} 4686 + 4687 + '@humanfs/node@0.16.7': 4688 + dependencies: 4689 + '@humanfs/core': 0.19.1 4690 + '@humanwhocodes/retry': 0.4.3 4691 + 4692 + '@humanwhocodes/module-importer@1.0.1': {} 4693 + 4694 + '@humanwhocodes/retry@0.4.3': {} 3794 4695 3795 4696 '@icons-pack/react-simple-icons@13.8.0(react@18.3.1)': 3796 4697 dependencies: ··· 4157 5058 4158 5059 '@types/har-format@1.2.16': {} 4159 5060 5061 + '@types/json-schema@7.0.15': {} 5062 + 4160 5063 '@types/jszip@3.4.1': 4161 5064 dependencies: 4162 5065 jszip: 3.10.1 ··· 4194 5097 '@types/node': 24.10.4 4195 5098 optional: true 4196 5099 5100 + '@typescript-eslint/eslint-plugin@8.53.1(@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': 5101 + dependencies: 5102 + '@eslint-community/regexpp': 4.12.2 5103 + '@typescript-eslint/parser': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 5104 + '@typescript-eslint/scope-manager': 8.53.1 5105 + '@typescript-eslint/type-utils': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 5106 + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 5107 + '@typescript-eslint/visitor-keys': 8.53.1 5108 + eslint: 9.39.2(jiti@1.21.7) 5109 + ignore: 7.0.5 5110 + natural-compare: 1.4.0 5111 + ts-api-utils: 2.4.0(typescript@5.9.3) 5112 + typescript: 5.9.3 5113 + transitivePeerDependencies: 5114 + - supports-color 5115 + 5116 + '@typescript-eslint/parser@8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': 5117 + dependencies: 5118 + '@typescript-eslint/scope-manager': 8.53.1 5119 + '@typescript-eslint/types': 8.53.1 5120 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) 5121 + '@typescript-eslint/visitor-keys': 8.53.1 5122 + debug: 4.4.3 5123 + eslint: 9.39.2(jiti@1.21.7) 5124 + typescript: 5.9.3 5125 + transitivePeerDependencies: 5126 + - supports-color 5127 + 4197 5128 '@typescript-eslint/project-service@8.50.1(typescript@5.9.3)': 4198 5129 dependencies: 4199 5130 '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) ··· 4203 5134 transitivePeerDependencies: 4204 5135 - supports-color 4205 5136 5137 + '@typescript-eslint/project-service@8.53.1(typescript@5.9.3)': 5138 + dependencies: 5139 + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) 5140 + '@typescript-eslint/types': 8.53.1 5141 + debug: 4.4.3 5142 + typescript: 5.9.3 5143 + transitivePeerDependencies: 5144 + - supports-color 5145 + 5146 + '@typescript-eslint/scope-manager@8.53.1': 5147 + dependencies: 5148 + '@typescript-eslint/types': 8.53.1 5149 + '@typescript-eslint/visitor-keys': 8.53.1 5150 + 4206 5151 '@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)': 4207 5152 dependencies: 4208 5153 typescript: 5.9.3 4209 5154 5155 + '@typescript-eslint/tsconfig-utils@8.53.1(typescript@5.9.3)': 5156 + dependencies: 5157 + typescript: 5.9.3 5158 + 5159 + '@typescript-eslint/type-utils@8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': 5160 + dependencies: 5161 + '@typescript-eslint/types': 8.53.1 5162 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) 5163 + '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) 5164 + debug: 4.4.3 5165 + eslint: 9.39.2(jiti@1.21.7) 5166 + ts-api-utils: 2.4.0(typescript@5.9.3) 5167 + typescript: 5.9.3 5168 + transitivePeerDependencies: 5169 + - supports-color 5170 + 4210 5171 '@typescript-eslint/types@8.50.1': {} 5172 + 5173 + '@typescript-eslint/types@8.53.1': {} 4211 5174 4212 5175 '@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)': 4213 5176 dependencies: ··· 4224 5187 transitivePeerDependencies: 4225 5188 - supports-color 4226 5189 5190 + '@typescript-eslint/typescript-estree@8.53.1(typescript@5.9.3)': 5191 + dependencies: 5192 + '@typescript-eslint/project-service': 8.53.1(typescript@5.9.3) 5193 + '@typescript-eslint/tsconfig-utils': 8.53.1(typescript@5.9.3) 5194 + '@typescript-eslint/types': 8.53.1 5195 + '@typescript-eslint/visitor-keys': 8.53.1 5196 + debug: 4.4.3 5197 + minimatch: 9.0.5 5198 + semver: 7.7.3 5199 + tinyglobby: 0.2.15 5200 + ts-api-utils: 2.4.0(typescript@5.9.3) 5201 + typescript: 5.9.3 5202 + transitivePeerDependencies: 5203 + - supports-color 5204 + 5205 + '@typescript-eslint/utils@8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': 5206 + dependencies: 5207 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7)) 5208 + '@typescript-eslint/scope-manager': 8.53.1 5209 + '@typescript-eslint/types': 8.53.1 5210 + '@typescript-eslint/typescript-estree': 8.53.1(typescript@5.9.3) 5211 + eslint: 9.39.2(jiti@1.21.7) 5212 + typescript: 5.9.3 5213 + transitivePeerDependencies: 5214 + - supports-color 5215 + 4227 5216 '@typescript-eslint/visitor-keys@8.50.1': 4228 5217 dependencies: 4229 5218 '@typescript-eslint/types': 8.50.1 5219 + eslint-visitor-keys: 4.2.1 5220 + 5221 + '@typescript-eslint/visitor-keys@8.53.1': 5222 + dependencies: 5223 + '@typescript-eslint/types': 8.53.1 4230 5224 eslint-visitor-keys: 4.2.1 4231 5225 4232 5226 '@vercel/nft@0.29.4(rollup@4.54.0)': ··· 4331 5325 dependencies: 4332 5326 acorn: 8.15.0 4333 5327 5328 + acorn-jsx@5.3.2(acorn@8.15.0): 5329 + dependencies: 5330 + acorn: 8.15.0 5331 + 4334 5332 acorn@8.15.0: {} 4335 5333 4336 5334 actor-typeahead@0.1.2: {} 4337 5335 4338 5336 agent-base@7.1.4: {} 4339 5337 5338 + ajv@6.12.6: 5339 + dependencies: 5340 + fast-deep-equal: 3.1.3 5341 + fast-json-stable-stringify: 2.1.0 5342 + json-schema-traverse: 0.4.1 5343 + uri-js: 4.4.1 5344 + 4340 5345 ansi-regex@5.0.1: {} 4341 5346 4342 5347 ansi-regex@6.2.2: {} ··· 4383 5388 4384 5389 argparse@2.0.1: {} 4385 5390 5391 + aria-query@5.3.2: {} 5392 + 5393 + array-buffer-byte-length@1.0.2: 5394 + dependencies: 5395 + call-bound: 1.0.4 5396 + is-array-buffer: 3.0.5 5397 + 5398 + array-includes@3.1.9: 5399 + dependencies: 5400 + call-bind: 1.0.8 5401 + call-bound: 1.0.4 5402 + define-properties: 1.2.1 5403 + es-abstract: 1.24.1 5404 + es-object-atoms: 1.1.1 5405 + get-intrinsic: 1.3.0 5406 + is-string: 1.1.1 5407 + math-intrinsics: 1.1.0 5408 + 4386 5409 array-union@2.1.0: {} 4387 5410 5411 + array.prototype.findlast@1.2.5: 5412 + dependencies: 5413 + call-bind: 1.0.8 5414 + define-properties: 1.2.1 5415 + es-abstract: 1.24.1 5416 + es-errors: 1.3.0 5417 + es-object-atoms: 1.1.1 5418 + es-shim-unscopables: 1.1.0 5419 + 5420 + array.prototype.flat@1.3.3: 5421 + dependencies: 5422 + call-bind: 1.0.8 5423 + define-properties: 1.2.1 5424 + es-abstract: 1.24.1 5425 + es-shim-unscopables: 1.1.0 5426 + 5427 + array.prototype.flatmap@1.3.3: 5428 + dependencies: 5429 + call-bind: 1.0.8 5430 + define-properties: 1.2.1 5431 + es-abstract: 1.24.1 5432 + es-shim-unscopables: 1.1.0 5433 + 5434 + array.prototype.tosorted@1.1.4: 5435 + dependencies: 5436 + call-bind: 1.0.8 5437 + define-properties: 1.2.1 5438 + es-abstract: 1.24.1 5439 + es-errors: 1.3.0 5440 + es-shim-unscopables: 1.1.0 5441 + 5442 + arraybuffer.prototype.slice@1.0.4: 5443 + dependencies: 5444 + array-buffer-byte-length: 1.0.2 5445 + call-bind: 1.0.8 5446 + define-properties: 1.2.1 5447 + es-abstract: 1.24.1 5448 + es-errors: 1.3.0 5449 + get-intrinsic: 1.3.0 5450 + is-array-buffer: 3.0.5 5451 + 4388 5452 ast-module-types@6.0.1: {} 4389 5453 5454 + ast-types-flow@0.0.8: {} 5455 + 5456 + async-function@1.0.0: {} 5457 + 4390 5458 async-sema@3.1.1: {} 4391 5459 4392 5460 async@3.2.6: {} ··· 4400 5468 postcss: 8.5.6 4401 5469 postcss-value-parser: 4.2.0 4402 5470 5471 + available-typed-arrays@1.0.7: 5472 + dependencies: 5473 + possible-typed-array-names: 1.1.0 5474 + 4403 5475 await-lock@2.2.2: {} 4404 5476 5477 + axe-core@4.11.1: {} 5478 + 5479 + axobject-query@4.1.0: {} 5480 + 4405 5481 b4a@1.7.3: {} 4406 5482 4407 5483 balanced-match@1.0.2: {} ··· 4419 5495 file-uri-to-path: 1.0.0 4420 5496 4421 5497 boolbase@1.0.0: {} 5498 + 5499 + brace-expansion@1.1.12: 5500 + dependencies: 5501 + balanced-match: 1.0.2 5502 + concat-map: 0.0.1 4422 5503 4423 5504 brace-expansion@2.0.2: 4424 5505 dependencies: ··· 4447 5528 base64-js: 1.5.1 4448 5529 ieee754: 1.2.1 4449 5530 5531 + call-bind-apply-helpers@1.0.2: 5532 + dependencies: 5533 + es-errors: 1.3.0 5534 + function-bind: 1.1.2 5535 + 5536 + call-bind@1.0.8: 5537 + dependencies: 5538 + call-bind-apply-helpers: 1.0.2 5539 + es-define-property: 1.0.1 5540 + get-intrinsic: 1.3.0 5541 + set-function-length: 1.2.2 5542 + 5543 + call-bound@1.0.4: 5544 + dependencies: 5545 + call-bind-apply-helpers: 1.0.2 5546 + get-intrinsic: 1.3.0 5547 + 4450 5548 callsite@1.0.0: {} 4451 5549 4452 5550 callsites@3.1.0: {} ··· 4463 5561 lodash.uniq: 4.5.0 4464 5562 4465 5563 caniuse-lite@1.0.30001761: {} 5564 + 5565 + chalk@4.1.2: 5566 + dependencies: 5567 + ansi-styles: 4.3.0 5568 + supports-color: 7.2.0 4466 5569 4467 5570 chokidar@3.6.0: 4468 5571 dependencies: ··· 4532 5635 is-stream: 2.0.1 4533 5636 normalize-path: 3.0.0 4534 5637 readable-stream: 4.7.0 5638 + 5639 + concat-map@0.0.1: {} 4535 5640 4536 5641 consola@3.4.2: {} 4537 5642 ··· 4650 5755 4651 5756 csstype@3.2.3: {} 4652 5757 5758 + damerau-levenshtein@1.0.8: {} 5759 + 5760 + data-view-buffer@1.0.2: 5761 + dependencies: 5762 + call-bound: 1.0.4 5763 + es-errors: 1.3.0 5764 + is-data-view: 1.0.2 5765 + 5766 + data-view-byte-length@1.0.2: 5767 + dependencies: 5768 + call-bound: 1.0.4 5769 + es-errors: 1.3.0 5770 + is-data-view: 1.0.2 5771 + 5772 + data-view-byte-offset@1.0.1: 5773 + dependencies: 5774 + call-bound: 1.0.4 5775 + es-errors: 1.3.0 5776 + is-data-view: 1.0.2 5777 + 4653 5778 date-fns@4.1.0: {} 4654 5779 4655 5780 debug@4.4.3: ··· 4660 5785 dependencies: 4661 5786 callsite: 1.0.0 4662 5787 5788 + deep-is@0.1.4: {} 5789 + 5790 + define-data-property@1.1.4: 5791 + dependencies: 5792 + es-define-property: 1.0.1 5793 + es-errors: 1.3.0 5794 + gopd: 1.2.0 5795 + 5796 + define-properties@1.2.1: 5797 + dependencies: 5798 + define-data-property: 1.1.4 5799 + has-property-descriptors: 1.0.2 5800 + object-keys: 1.1.1 5801 + 4663 5802 detect-libc@2.1.2: {} 4664 5803 4665 5804 detective-amd@6.0.1: ··· 4727 5866 path-type: 4.0.0 4728 5867 4729 5868 dlv@1.1.3: {} 5869 + 5870 + doctrine@2.1.0: 5871 + dependencies: 5872 + esutils: 2.0.3 4730 5873 4731 5874 dom-serializer@2.0.0: 4732 5875 dependencies: ··· 4757 5900 4758 5901 dotenv@16.6.1: {} 4759 5902 5903 + dunder-proto@1.0.1: 5904 + dependencies: 5905 + call-bind-apply-helpers: 1.0.2 5906 + es-errors: 1.3.0 5907 + gopd: 1.2.0 5908 + 4760 5909 eastasianwidth@0.2.0: {} 4761 5910 4762 5911 electron-to-chromium@1.5.267: {} ··· 4785 5934 dependencies: 4786 5935 is-arrayish: 0.2.1 4787 5936 5937 + es-abstract@1.24.1: 5938 + dependencies: 5939 + array-buffer-byte-length: 1.0.2 5940 + arraybuffer.prototype.slice: 1.0.4 5941 + available-typed-arrays: 1.0.7 5942 + call-bind: 1.0.8 5943 + call-bound: 1.0.4 5944 + data-view-buffer: 1.0.2 5945 + data-view-byte-length: 1.0.2 5946 + data-view-byte-offset: 1.0.1 5947 + es-define-property: 1.0.1 5948 + es-errors: 1.3.0 5949 + es-object-atoms: 1.1.1 5950 + es-set-tostringtag: 2.1.0 5951 + es-to-primitive: 1.3.0 5952 + function.prototype.name: 1.1.8 5953 + get-intrinsic: 1.3.0 5954 + get-proto: 1.0.1 5955 + get-symbol-description: 1.1.0 5956 + globalthis: 1.0.4 5957 + gopd: 1.2.0 5958 + has-property-descriptors: 1.0.2 5959 + has-proto: 1.2.0 5960 + has-symbols: 1.1.0 5961 + hasown: 2.0.2 5962 + internal-slot: 1.1.0 5963 + is-array-buffer: 3.0.5 5964 + is-callable: 1.2.7 5965 + is-data-view: 1.0.2 5966 + is-negative-zero: 2.0.3 5967 + is-regex: 1.2.1 5968 + is-set: 2.0.3 5969 + is-shared-array-buffer: 1.0.4 5970 + is-string: 1.1.1 5971 + is-typed-array: 1.1.15 5972 + is-weakref: 1.1.1 5973 + math-intrinsics: 1.1.0 5974 + object-inspect: 1.13.4 5975 + object-keys: 1.1.1 5976 + object.assign: 4.1.7 5977 + own-keys: 1.0.1 5978 + regexp.prototype.flags: 1.5.4 5979 + safe-array-concat: 1.1.3 5980 + safe-push-apply: 1.0.0 5981 + safe-regex-test: 1.1.0 5982 + set-proto: 1.0.0 5983 + stop-iteration-iterator: 1.1.0 5984 + string.prototype.trim: 1.2.10 5985 + string.prototype.trimend: 1.0.9 5986 + string.prototype.trimstart: 1.0.8 5987 + typed-array-buffer: 1.0.3 5988 + typed-array-byte-length: 1.0.3 5989 + typed-array-byte-offset: 1.0.4 5990 + typed-array-length: 1.0.7 5991 + unbox-primitive: 1.1.0 5992 + which-typed-array: 1.1.20 5993 + 5994 + es-define-property@1.0.1: {} 5995 + 5996 + es-errors@1.3.0: {} 5997 + 5998 + es-iterator-helpers@1.2.2: 5999 + dependencies: 6000 + call-bind: 1.0.8 6001 + call-bound: 1.0.4 6002 + define-properties: 1.2.1 6003 + es-abstract: 1.24.1 6004 + es-errors: 1.3.0 6005 + es-set-tostringtag: 2.1.0 6006 + function-bind: 1.1.2 6007 + get-intrinsic: 1.3.0 6008 + globalthis: 1.0.4 6009 + gopd: 1.2.0 6010 + has-property-descriptors: 1.0.2 6011 + has-proto: 1.2.0 6012 + has-symbols: 1.1.0 6013 + internal-slot: 1.1.0 6014 + iterator.prototype: 1.1.5 6015 + safe-array-concat: 1.1.3 6016 + 4788 6017 es-module-lexer@1.7.0: {} 6018 + 6019 + es-object-atoms@1.1.1: 6020 + dependencies: 6021 + es-errors: 1.3.0 6022 + 6023 + es-set-tostringtag@2.1.0: 6024 + dependencies: 6025 + es-errors: 1.3.0 6026 + get-intrinsic: 1.3.0 6027 + has-tostringtag: 1.0.2 6028 + hasown: 2.0.2 6029 + 6030 + es-shim-unscopables@1.1.0: 6031 + dependencies: 6032 + hasown: 2.0.2 6033 + 6034 + es-to-primitive@1.3.0: 6035 + dependencies: 6036 + is-callable: 1.2.7 6037 + is-date-object: 1.1.0 6038 + is-symbol: 1.1.1 4789 6039 4790 6040 esbuild@0.19.12: 4791 6041 optionalDependencies: ··· 4872 6122 4873 6123 escape-string-regexp@1.0.5: {} 4874 6124 6125 + escape-string-regexp@4.0.0: {} 6126 + 4875 6127 escodegen@2.1.0: 4876 6128 dependencies: 4877 6129 esprima: 4.0.1 ··· 4880 6132 optionalDependencies: 4881 6133 source-map: 0.6.1 4882 6134 6135 + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(jiti@1.21.7)): 6136 + dependencies: 6137 + aria-query: 5.3.2 6138 + array-includes: 3.1.9 6139 + array.prototype.flatmap: 1.3.3 6140 + ast-types-flow: 0.0.8 6141 + axe-core: 4.11.1 6142 + axobject-query: 4.1.0 6143 + damerau-levenshtein: 1.0.8 6144 + emoji-regex: 9.2.2 6145 + eslint: 9.39.2(jiti@1.21.7) 6146 + hasown: 2.0.2 6147 + jsx-ast-utils: 3.3.5 6148 + language-tags: 1.0.9 6149 + minimatch: 3.1.2 6150 + object.fromentries: 2.0.8 6151 + safe-regex-test: 1.1.0 6152 + string.prototype.includes: 2.0.1 6153 + 6154 + eslint-plugin-react-hooks@5.2.0(eslint@9.39.2(jiti@1.21.7)): 6155 + dependencies: 6156 + eslint: 9.39.2(jiti@1.21.7) 6157 + 6158 + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@1.21.7)): 6159 + dependencies: 6160 + array-includes: 3.1.9 6161 + array.prototype.findlast: 1.2.5 6162 + array.prototype.flatmap: 1.3.3 6163 + array.prototype.tosorted: 1.1.4 6164 + doctrine: 2.1.0 6165 + es-iterator-helpers: 1.2.2 6166 + eslint: 9.39.2(jiti@1.21.7) 6167 + estraverse: 5.3.0 6168 + hasown: 2.0.2 6169 + jsx-ast-utils: 3.3.5 6170 + minimatch: 3.1.2 6171 + object.entries: 1.1.9 6172 + object.fromentries: 2.0.8 6173 + object.values: 1.2.1 6174 + prop-types: 15.8.1 6175 + resolve: 2.0.0-next.5 6176 + semver: 6.3.1 6177 + string.prototype.matchall: 4.0.12 6178 + string.prototype.repeat: 1.0.0 6179 + 6180 + eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.19): 6181 + dependencies: 6182 + fast-glob: 3.3.3 6183 + postcss: 8.5.6 6184 + tailwindcss: 3.4.19 6185 + 6186 + eslint-scope@8.4.0: 6187 + dependencies: 6188 + esrecurse: 4.3.0 6189 + estraverse: 5.3.0 6190 + 6191 + eslint-visitor-keys@3.4.3: {} 6192 + 4883 6193 eslint-visitor-keys@4.2.1: {} 4884 6194 6195 + eslint@9.39.2(jiti@1.21.7): 6196 + dependencies: 6197 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7)) 6198 + '@eslint-community/regexpp': 4.12.2 6199 + '@eslint/config-array': 0.21.1 6200 + '@eslint/config-helpers': 0.4.2 6201 + '@eslint/core': 0.17.0 6202 + '@eslint/eslintrc': 3.3.3 6203 + '@eslint/js': 9.39.2 6204 + '@eslint/plugin-kit': 0.4.1 6205 + '@humanfs/node': 0.16.7 6206 + '@humanwhocodes/module-importer': 1.0.1 6207 + '@humanwhocodes/retry': 0.4.3 6208 + '@types/estree': 1.0.8 6209 + ajv: 6.12.6 6210 + chalk: 4.1.2 6211 + cross-spawn: 7.0.6 6212 + debug: 4.4.3 6213 + escape-string-regexp: 4.0.0 6214 + eslint-scope: 8.4.0 6215 + eslint-visitor-keys: 4.2.1 6216 + espree: 10.4.0 6217 + esquery: 1.7.0 6218 + esutils: 2.0.3 6219 + fast-deep-equal: 3.1.3 6220 + file-entry-cache: 8.0.0 6221 + find-up: 5.0.0 6222 + glob-parent: 6.0.2 6223 + ignore: 5.3.2 6224 + imurmurhash: 0.1.4 6225 + is-glob: 4.0.3 6226 + json-stable-stringify-without-jsonify: 1.0.1 6227 + lodash.merge: 4.6.2 6228 + minimatch: 3.1.2 6229 + natural-compare: 1.4.0 6230 + optionator: 0.9.4 6231 + optionalDependencies: 6232 + jiti: 1.21.7 6233 + transitivePeerDependencies: 6234 + - supports-color 6235 + 4885 6236 esm-env@1.2.2: {} 4886 6237 6238 + espree@10.4.0: 6239 + dependencies: 6240 + acorn: 8.15.0 6241 + acorn-jsx: 5.3.2(acorn@8.15.0) 6242 + eslint-visitor-keys: 4.2.1 6243 + 4887 6244 esprima@4.0.1: {} 4888 6245 6246 + esquery@1.7.0: 6247 + dependencies: 6248 + estraverse: 5.3.0 6249 + 6250 + esrecurse@4.3.0: 6251 + dependencies: 6252 + estraverse: 5.3.0 6253 + 4889 6254 estraverse@5.3.0: {} 4890 6255 4891 6256 estree-walker@2.0.2: {} ··· 4924 6289 transitivePeerDependencies: 4925 6290 - supports-color 4926 6291 6292 + fast-deep-equal@3.1.3: {} 6293 + 4927 6294 fast-fifo@1.3.2: {} 4928 6295 4929 6296 fast-glob@3.3.3: ··· 4934 6301 merge2: 1.4.1 4935 6302 micromatch: 4.0.8 4936 6303 6304 + fast-json-stable-stringify@2.1.0: {} 6305 + 6306 + fast-levenshtein@2.0.6: {} 6307 + 4937 6308 fastq@1.20.1: 4938 6309 dependencies: 4939 6310 reusify: 1.1.0 ··· 4947 6318 picomatch: 4.0.3 4948 6319 4949 6320 fecha@4.2.3: {} 6321 + 6322 + file-entry-cache@8.0.0: 6323 + dependencies: 6324 + flat-cache: 4.0.1 4950 6325 4951 6326 file-uri-to-path@1.0.0: {} 4952 6327 ··· 4977 6352 locate-path: 5.0.0 4978 6353 path-exists: 4.0.0 4979 6354 6355 + find-up@5.0.0: 6356 + dependencies: 6357 + locate-path: 6.0.0 6358 + path-exists: 4.0.0 6359 + 4980 6360 find-up@7.0.0: 4981 6361 dependencies: 4982 6362 locate-path: 7.2.0 4983 6363 path-exists: 5.0.0 4984 6364 unicorn-magic: 0.1.0 4985 6365 6366 + flat-cache@4.0.1: 6367 + dependencies: 6368 + flatted: 3.3.3 6369 + keyv: 4.5.4 6370 + 6371 + flatted@3.3.3: {} 6372 + 4986 6373 fn.name@1.1.0: {} 6374 + 6375 + for-each@0.3.5: 6376 + dependencies: 6377 + is-callable: 1.2.7 4987 6378 4988 6379 foreground-child@3.3.1: 4989 6380 dependencies: ··· 5003 6394 5004 6395 function-bind@1.1.2: {} 5005 6396 6397 + function.prototype.name@1.1.8: 6398 + dependencies: 6399 + call-bind: 1.0.8 6400 + call-bound: 1.0.4 6401 + define-properties: 1.2.1 6402 + functions-have-names: 1.2.3 6403 + hasown: 2.0.2 6404 + is-callable: 1.2.7 6405 + 6406 + functions-have-names@1.2.3: {} 6407 + 6408 + generator-function@2.0.1: {} 6409 + 5006 6410 gensync@1.0.0-beta.2: {} 5007 6411 5008 6412 get-amd-module-type@6.0.1: ··· 5011 6415 node-source-walk: 7.0.1 5012 6416 5013 6417 get-caller-file@2.0.5: {} 6418 + 6419 + get-intrinsic@1.3.0: 6420 + dependencies: 6421 + call-bind-apply-helpers: 1.0.2 6422 + es-define-property: 1.0.1 6423 + es-errors: 1.3.0 6424 + es-object-atoms: 1.1.1 6425 + function-bind: 1.1.2 6426 + get-proto: 1.0.1 6427 + gopd: 1.2.0 6428 + has-symbols: 1.1.0 6429 + hasown: 2.0.2 6430 + math-intrinsics: 1.1.0 6431 + 6432 + get-proto@1.0.1: 6433 + dependencies: 6434 + dunder-proto: 1.0.1 6435 + es-object-atoms: 1.1.1 5014 6436 5015 6437 get-stream@5.2.0: 5016 6438 dependencies: ··· 5018 6440 5019 6441 get-stream@8.0.1: {} 5020 6442 6443 + get-symbol-description@1.1.0: 6444 + dependencies: 6445 + call-bound: 1.0.4 6446 + es-errors: 1.3.0 6447 + get-intrinsic: 1.3.0 6448 + 5021 6449 gh-pages@6.3.0: 5022 6450 dependencies: 5023 6451 async: 3.2.6 ··· 5045 6473 package-json-from-dist: 1.0.1 5046 6474 path-scurry: 1.11.1 5047 6475 6476 + globals@14.0.0: {} 6477 + 6478 + globalthis@1.0.4: 6479 + dependencies: 6480 + define-properties: 1.2.1 6481 + gopd: 1.2.0 6482 + 5048 6483 globby@11.1.0: 5049 6484 dependencies: 5050 6485 array-union: 2.1.0 ··· 5057 6492 gonzales-pe@4.3.0: 5058 6493 dependencies: 5059 6494 minimist: 1.2.8 6495 + 6496 + gopd@1.2.0: {} 5060 6497 5061 6498 graceful-fs@4.2.11: {} 5062 6499 6500 + has-bigints@1.1.0: {} 6501 + 6502 + has-flag@4.0.0: {} 6503 + 6504 + has-property-descriptors@1.0.2: 6505 + dependencies: 6506 + es-define-property: 1.0.1 6507 + 6508 + has-proto@1.2.0: 6509 + dependencies: 6510 + dunder-proto: 1.0.1 6511 + 6512 + has-symbols@1.1.0: {} 6513 + 6514 + has-tostringtag@1.0.2: 6515 + dependencies: 6516 + has-symbols: 1.1.0 6517 + 5063 6518 hasown@2.0.2: 5064 6519 dependencies: 5065 6520 function-bind: 1.1.2 ··· 5081 6536 5082 6537 ignore@5.3.2: {} 5083 6538 6539 + ignore@7.0.5: {} 6540 + 5084 6541 image-size@2.0.2: {} 5085 6542 5086 6543 immediate@3.0.6: {} ··· 5096 6553 5097 6554 inherits@2.0.4: {} 5098 6555 6556 + internal-slot@1.1.0: 6557 + dependencies: 6558 + es-errors: 1.3.0 6559 + hasown: 2.0.2 6560 + side-channel: 1.1.0 6561 + 5099 6562 ipaddr.js@2.3.0: {} 5100 6563 6564 + is-array-buffer@3.0.5: 6565 + dependencies: 6566 + call-bind: 1.0.8 6567 + call-bound: 1.0.4 6568 + get-intrinsic: 1.3.0 6569 + 5101 6570 is-arrayish@0.2.1: {} 5102 6571 6572 + is-async-function@2.1.1: 6573 + dependencies: 6574 + async-function: 1.0.0 6575 + call-bound: 1.0.4 6576 + get-proto: 1.0.1 6577 + has-tostringtag: 1.0.2 6578 + safe-regex-test: 1.1.0 6579 + 6580 + is-bigint@1.1.0: 6581 + dependencies: 6582 + has-bigints: 1.1.0 6583 + 5103 6584 is-binary-path@2.1.0: 5104 6585 dependencies: 5105 6586 binary-extensions: 2.3.0 5106 6587 6588 + is-boolean-object@1.2.2: 6589 + dependencies: 6590 + call-bound: 1.0.4 6591 + has-tostringtag: 1.0.2 6592 + 6593 + is-callable@1.2.7: {} 6594 + 5107 6595 is-core-module@2.16.1: 5108 6596 dependencies: 5109 6597 hasown: 2.0.2 5110 6598 6599 + is-data-view@1.0.2: 6600 + dependencies: 6601 + call-bound: 1.0.4 6602 + get-intrinsic: 1.3.0 6603 + is-typed-array: 1.1.15 6604 + 6605 + is-date-object@1.1.0: 6606 + dependencies: 6607 + call-bound: 1.0.4 6608 + has-tostringtag: 1.0.2 6609 + 5111 6610 is-extglob@2.1.1: {} 5112 6611 6612 + is-finalizationregistry@1.1.1: 6613 + dependencies: 6614 + call-bound: 1.0.4 6615 + 5113 6616 is-fullwidth-code-point@3.0.0: {} 5114 6617 6618 + is-generator-function@1.1.2: 6619 + dependencies: 6620 + call-bound: 1.0.4 6621 + generator-function: 2.0.1 6622 + get-proto: 1.0.1 6623 + has-tostringtag: 1.0.2 6624 + safe-regex-test: 1.1.0 6625 + 5115 6626 is-glob@4.0.3: 5116 6627 dependencies: 5117 6628 is-extglob: 2.1.1 5118 6629 6630 + is-map@2.0.3: {} 6631 + 6632 + is-negative-zero@2.0.3: {} 6633 + 6634 + is-number-object@1.1.1: 6635 + dependencies: 6636 + call-bound: 1.0.4 6637 + has-tostringtag: 1.0.2 6638 + 5119 6639 is-number@7.0.0: {} 5120 6640 5121 6641 is-path-inside@4.0.0: {} 5122 6642 5123 6643 is-plain-obj@2.1.0: {} 5124 6644 6645 + is-regex@1.2.1: 6646 + dependencies: 6647 + call-bound: 1.0.4 6648 + gopd: 1.2.0 6649 + has-tostringtag: 1.0.2 6650 + hasown: 2.0.2 6651 + 6652 + is-set@2.0.3: {} 6653 + 6654 + is-shared-array-buffer@1.0.4: 6655 + dependencies: 6656 + call-bound: 1.0.4 6657 + 5125 6658 is-stream@2.0.1: {} 5126 6659 5127 6660 is-stream@3.0.0: {} 5128 6661 5129 6662 is-stream@4.0.1: {} 5130 6663 6664 + is-string@1.1.1: 6665 + dependencies: 6666 + call-bound: 1.0.4 6667 + has-tostringtag: 1.0.2 6668 + 6669 + is-symbol@1.1.1: 6670 + dependencies: 6671 + call-bound: 1.0.4 6672 + has-symbols: 1.1.0 6673 + safe-regex-test: 1.1.0 6674 + 6675 + is-typed-array@1.1.15: 6676 + dependencies: 6677 + which-typed-array: 1.1.20 6678 + 5131 6679 is-url-superb@4.0.0: {} 5132 6680 5133 6681 is-url@1.2.4: {} 5134 6682 6683 + is-weakmap@2.0.2: {} 6684 + 6685 + is-weakref@1.1.1: 6686 + dependencies: 6687 + call-bound: 1.0.4 6688 + 6689 + is-weakset@2.0.4: 6690 + dependencies: 6691 + call-bound: 1.0.4 6692 + get-intrinsic: 1.3.0 6693 + 5135 6694 isarray@1.0.0: {} 6695 + 6696 + isarray@2.0.5: {} 5136 6697 5137 6698 isexe@2.0.0: {} 5138 6699 5139 6700 iso-datestring-validator@2.2.2: {} 6701 + 6702 + iterator.prototype@1.1.5: 6703 + dependencies: 6704 + define-data-property: 1.1.4 6705 + es-object-atoms: 1.1.1 6706 + get-intrinsic: 1.3.0 6707 + get-proto: 1.0.1 6708 + has-symbols: 1.1.0 6709 + set-function-name: 2.0.2 5140 6710 5141 6711 jackspeak@3.4.3: 5142 6712 dependencies: ··· 5164 6734 5165 6735 jsesc@3.1.0: {} 5166 6736 6737 + json-buffer@3.0.1: {} 6738 + 5167 6739 json-parse-even-better-errors@2.3.1: {} 5168 6740 6741 + json-schema-traverse@0.4.1: {} 6742 + 6743 + json-stable-stringify-without-jsonify@1.0.1: {} 6744 + 5169 6745 json5@2.2.3: {} 5170 6746 5171 6747 jsonfile@6.2.0: ··· 5174 6750 optionalDependencies: 5175 6751 graceful-fs: 4.2.11 5176 6752 6753 + jsx-ast-utils@3.3.5: 6754 + dependencies: 6755 + array-includes: 3.1.9 6756 + array.prototype.flat: 1.3.3 6757 + object.assign: 4.1.7 6758 + object.values: 1.2.1 6759 + 5177 6760 jszip@3.10.1: 5178 6761 dependencies: 5179 6762 lie: 3.3.0 ··· 5185 6768 5186 6769 jwt-decode@4.0.0: {} 5187 6770 6771 + keyv@4.5.4: 6772 + dependencies: 6773 + json-buffer: 3.0.1 6774 + 5188 6775 kuler@2.0.0: {} 5189 6776 5190 6777 lambda-local@2.2.0: ··· 5193 6780 dotenv: 16.6.1 5194 6781 winston: 3.19.0 5195 6782 6783 + language-subtag-registry@0.3.23: {} 6784 + 6785 + language-tags@1.0.9: 6786 + dependencies: 6787 + language-subtag-registry: 0.3.23 6788 + 5196 6789 lazystream@1.0.1: 5197 6790 dependencies: 5198 6791 readable-stream: 2.3.8 5199 6792 6793 + levn@0.4.1: 6794 + dependencies: 6795 + prelude-ls: 1.2.1 6796 + type-check: 0.4.0 6797 + 5200 6798 lie@3.3.0: 5201 6799 dependencies: 5202 6800 immediate: 3.0.6 ··· 5209 6807 dependencies: 5210 6808 p-locate: 4.1.0 5211 6809 6810 + locate-path@6.0.0: 6811 + dependencies: 6812 + p-locate: 5.0.0 6813 + 5212 6814 locate-path@7.2.0: 5213 6815 dependencies: 5214 6816 p-locate: 6.0.0 5215 6817 5216 6818 lodash.memoize@4.1.2: {} 6819 + 6820 + lodash.merge@4.6.2: {} 5217 6821 5218 6822 lodash.uniq@4.5.0: {} 5219 6823 ··· 5256 6860 dependencies: 5257 6861 semver: 6.3.1 5258 6862 6863 + math-intrinsics@1.1.0: {} 6864 + 5259 6865 mdn-data@2.0.28: {} 5260 6866 5261 6867 mdn-data@2.12.2: {} ··· 5274 6880 picomatch: 2.3.1 5275 6881 5276 6882 mimic-fn@4.0.0: {} 6883 + 6884 + minimatch@3.1.2: 6885 + dependencies: 6886 + brace-expansion: 1.1.12 5277 6887 5278 6888 minimatch@5.1.6: 5279 6889 dependencies: ··· 5307 6917 thenify-all: 1.6.0 5308 6918 5309 6919 nanoid@3.3.11: {} 6920 + 6921 + natural-compare@1.4.0: {} 5310 6922 5311 6923 no-case@3.0.4: 5312 6924 dependencies: ··· 5353 6965 5354 6966 object-hash@3.0.0: {} 5355 6967 6968 + object-inspect@1.13.4: {} 6969 + 6970 + object-keys@1.1.1: {} 6971 + 6972 + object.assign@4.1.7: 6973 + dependencies: 6974 + call-bind: 1.0.8 6975 + call-bound: 1.0.4 6976 + define-properties: 1.2.1 6977 + es-object-atoms: 1.1.1 6978 + has-symbols: 1.1.0 6979 + object-keys: 1.1.1 6980 + 6981 + object.entries@1.1.9: 6982 + dependencies: 6983 + call-bind: 1.0.8 6984 + call-bound: 1.0.4 6985 + define-properties: 1.2.1 6986 + es-object-atoms: 1.1.1 6987 + 6988 + object.fromentries@2.0.8: 6989 + dependencies: 6990 + call-bind: 1.0.8 6991 + define-properties: 1.2.1 6992 + es-abstract: 1.24.1 6993 + es-object-atoms: 1.1.1 6994 + 6995 + object.values@1.2.1: 6996 + dependencies: 6997 + call-bind: 1.0.8 6998 + call-bound: 1.0.4 6999 + define-properties: 1.2.1 7000 + es-object-atoms: 1.1.1 7001 + 5356 7002 once@1.4.0: 5357 7003 dependencies: 5358 7004 wrappy: 1.0.2 ··· 5365 7011 dependencies: 5366 7012 mimic-fn: 4.0.0 5367 7013 7014 + optionator@0.9.4: 7015 + dependencies: 7016 + deep-is: 0.1.4 7017 + fast-levenshtein: 2.0.6 7018 + levn: 0.4.1 7019 + prelude-ls: 1.2.1 7020 + type-check: 0.4.0 7021 + word-wrap: 1.2.5 7022 + 7023 + own-keys@1.0.1: 7024 + dependencies: 7025 + get-intrinsic: 1.3.0 7026 + object-keys: 1.1.1 7027 + safe-push-apply: 1.0.0 7028 + 5368 7029 p-event@6.0.1: 5369 7030 dependencies: 5370 7031 p-timeout: 6.1.4 ··· 5373 7034 dependencies: 5374 7035 p-try: 2.2.0 5375 7036 7037 + p-limit@3.1.0: 7038 + dependencies: 7039 + yocto-queue: 0.1.0 7040 + 5376 7041 p-limit@4.0.0: 5377 7042 dependencies: 5378 7043 yocto-queue: 1.2.2 ··· 5380 7045 p-locate@4.1.0: 5381 7046 dependencies: 5382 7047 p-limit: 2.3.0 7048 + 7049 + p-locate@5.0.0: 7050 + dependencies: 7051 + p-limit: 3.1.0 5383 7052 5384 7053 p-locate@6.0.0: 5385 7054 dependencies: ··· 5458 7127 pkg-dir@4.2.0: 5459 7128 dependencies: 5460 7129 find-up: 4.1.0 7130 + 7131 + possible-typed-array-names@1.1.0: {} 5461 7132 5462 7133 postcss-calc@10.1.1(postcss@8.5.6): 5463 7134 dependencies: ··· 5687 7358 transitivePeerDependencies: 5688 7359 - supports-color 5689 7360 7361 + prelude-ls@1.2.1: {} 7362 + 7363 + prettier-plugin-tailwindcss@0.6.14(prettier@3.8.0): 7364 + dependencies: 7365 + prettier: 3.8.0 7366 + 7367 + prettier@3.8.0: {} 7368 + 5690 7369 process-nextick-args@2.0.1: {} 5691 7370 5692 7371 process@0.11.10: {} 5693 7372 7373 + prop-types@15.8.1: 7374 + dependencies: 7375 + loose-envify: 1.4.0 7376 + object-assign: 4.1.1 7377 + react-is: 16.13.1 7378 + 5694 7379 pump@3.0.3: 5695 7380 dependencies: 5696 7381 end-of-stream: 1.4.5 5697 7382 once: 1.4.0 5698 7383 7384 + punycode@2.3.1: {} 7385 + 5699 7386 queue-microtask@1.2.3: {} 5700 7387 5701 7388 quote-unquote@1.0.0: {} ··· 5705 7392 loose-envify: 1.4.0 5706 7393 react: 18.3.1 5707 7394 scheduler: 0.23.2 7395 + 7396 + react-is@16.13.1: {} 5708 7397 5709 7398 react-refresh@0.17.0: {} 5710 7399 ··· 5778 7467 5779 7468 readdirp@4.1.2: {} 5780 7469 7470 + reflect.getprototypeof@1.0.10: 7471 + dependencies: 7472 + call-bind: 1.0.8 7473 + define-properties: 1.2.1 7474 + es-abstract: 1.24.1 7475 + es-errors: 1.3.0 7476 + es-object-atoms: 1.1.1 7477 + get-intrinsic: 1.3.0 7478 + get-proto: 1.0.1 7479 + which-builtin-type: 1.2.1 7480 + 7481 + regexp.prototype.flags@1.5.4: 7482 + dependencies: 7483 + call-bind: 1.0.8 7484 + define-properties: 1.2.1 7485 + es-errors: 1.3.0 7486 + get-proto: 1.0.1 7487 + gopd: 1.2.0 7488 + set-function-name: 2.0.2 7489 + 5781 7490 remove-trailing-separator@1.1.0: {} 5782 7491 5783 7492 require-directory@2.1.1: {} ··· 5834 7543 dependencies: 5835 7544 queue-microtask: 1.2.3 5836 7545 7546 + safe-array-concat@1.1.3: 7547 + dependencies: 7548 + call-bind: 1.0.8 7549 + call-bound: 1.0.4 7550 + get-intrinsic: 1.3.0 7551 + has-symbols: 1.1.0 7552 + isarray: 2.0.5 7553 + 5837 7554 safe-buffer@5.1.2: {} 5838 7555 5839 7556 safe-buffer@5.2.1: {} 5840 7557 7558 + safe-push-apply@1.0.0: 7559 + dependencies: 7560 + es-errors: 1.3.0 7561 + isarray: 2.0.5 7562 + 7563 + safe-regex-test@1.1.0: 7564 + dependencies: 7565 + call-bound: 1.0.4 7566 + es-errors: 1.3.0 7567 + is-regex: 1.2.1 7568 + 5841 7569 safe-stable-stringify@2.5.0: {} 5842 7570 5843 7571 sax@1.4.3: {} ··· 5852 7580 5853 7581 set-cookie-parser@2.7.2: {} 5854 7582 7583 + set-function-length@1.2.2: 7584 + dependencies: 7585 + define-data-property: 1.1.4 7586 + es-errors: 1.3.0 7587 + function-bind: 1.1.2 7588 + get-intrinsic: 1.3.0 7589 + gopd: 1.2.0 7590 + has-property-descriptors: 1.0.2 7591 + 7592 + set-function-name@2.0.2: 7593 + dependencies: 7594 + define-data-property: 1.1.4 7595 + es-errors: 1.3.0 7596 + functions-have-names: 1.2.3 7597 + has-property-descriptors: 1.0.2 7598 + 7599 + set-proto@1.0.0: 7600 + dependencies: 7601 + dunder-proto: 1.0.1 7602 + es-errors: 1.3.0 7603 + es-object-atoms: 1.1.1 7604 + 5855 7605 setimmediate@1.0.5: {} 5856 7606 5857 7607 shebang-command@2.0.0: ··· 5860 7610 5861 7611 shebang-regex@3.0.0: {} 5862 7612 7613 + side-channel-list@1.0.0: 7614 + dependencies: 7615 + es-errors: 1.3.0 7616 + object-inspect: 1.13.4 7617 + 7618 + side-channel-map@1.0.1: 7619 + dependencies: 7620 + call-bound: 1.0.4 7621 + es-errors: 1.3.0 7622 + get-intrinsic: 1.3.0 7623 + object-inspect: 1.13.4 7624 + 7625 + side-channel-weakmap@1.0.2: 7626 + dependencies: 7627 + call-bound: 1.0.4 7628 + es-errors: 1.3.0 7629 + get-intrinsic: 1.3.0 7630 + object-inspect: 1.13.4 7631 + side-channel-map: 1.0.1 7632 + 7633 + side-channel@1.1.0: 7634 + dependencies: 7635 + es-errors: 1.3.0 7636 + object-inspect: 1.13.4 7637 + side-channel-list: 1.0.0 7638 + side-channel-map: 1.0.1 7639 + side-channel-weakmap: 1.0.2 7640 + 5863 7641 signal-exit@4.1.0: {} 5864 7642 5865 7643 slash@3.0.0: {} ··· 5894 7672 5895 7673 stack-trace@0.0.10: {} 5896 7674 7675 + stop-iteration-iterator@1.1.0: 7676 + dependencies: 7677 + es-errors: 1.3.0 7678 + internal-slot: 1.1.0 7679 + 5897 7680 streamx@2.23.0: 5898 7681 dependencies: 5899 7682 events-universal: 1.0.1 ··· 5915 7698 emoji-regex: 9.2.2 5916 7699 strip-ansi: 7.1.2 5917 7700 7701 + string.prototype.includes@2.0.1: 7702 + dependencies: 7703 + call-bind: 1.0.8 7704 + define-properties: 1.2.1 7705 + es-abstract: 1.24.1 7706 + 7707 + string.prototype.matchall@4.0.12: 7708 + dependencies: 7709 + call-bind: 1.0.8 7710 + call-bound: 1.0.4 7711 + define-properties: 1.2.1 7712 + es-abstract: 1.24.1 7713 + es-errors: 1.3.0 7714 + es-object-atoms: 1.1.1 7715 + get-intrinsic: 1.3.0 7716 + gopd: 1.2.0 7717 + has-symbols: 1.1.0 7718 + internal-slot: 1.1.0 7719 + regexp.prototype.flags: 1.5.4 7720 + set-function-name: 2.0.2 7721 + side-channel: 1.1.0 7722 + 7723 + string.prototype.repeat@1.0.0: 7724 + dependencies: 7725 + define-properties: 1.2.1 7726 + es-abstract: 1.24.1 7727 + 7728 + string.prototype.trim@1.2.10: 7729 + dependencies: 7730 + call-bind: 1.0.8 7731 + call-bound: 1.0.4 7732 + define-data-property: 1.1.4 7733 + define-properties: 1.2.1 7734 + es-abstract: 1.24.1 7735 + es-object-atoms: 1.1.1 7736 + has-property-descriptors: 1.0.2 7737 + 7738 + string.prototype.trimend@1.0.9: 7739 + dependencies: 7740 + call-bind: 1.0.8 7741 + call-bound: 1.0.4 7742 + define-properties: 1.2.1 7743 + es-object-atoms: 1.1.1 7744 + 7745 + string.prototype.trimstart@1.0.8: 7746 + dependencies: 7747 + call-bind: 1.0.8 7748 + define-properties: 1.2.1 7749 + es-object-atoms: 1.1.1 7750 + 5918 7751 string_decoder@1.1.1: 5919 7752 dependencies: 5920 7753 safe-buffer: 5.1.2 ··· 5933 7766 5934 7767 strip-final-newline@3.0.0: {} 5935 7768 7769 + strip-json-comments@3.1.1: {} 7770 + 5936 7771 strip-outer@1.0.1: 5937 7772 dependencies: 5938 7773 escape-string-regexp: 1.0.5 ··· 5952 7787 pirates: 4.0.7 5953 7788 tinyglobby: 0.2.15 5954 7789 ts-interface-checker: 0.1.13 7790 + 7791 + supports-color@7.2.0: 7792 + dependencies: 7793 + has-flag: 4.0.0 5955 7794 5956 7795 supports-preserve-symlinks-flag@1.0.0: {} 5957 7796 ··· 6059 7898 dependencies: 6060 7899 typescript: 5.9.3 6061 7900 7901 + ts-api-utils@2.4.0(typescript@5.9.3): 7902 + dependencies: 7903 + typescript: 5.9.3 7904 + 6062 7905 ts-interface-checker@0.1.13: {} 6063 7906 6064 7907 tslib@2.8.1: {} 6065 7908 7909 + type-check@0.4.0: 7910 + dependencies: 7911 + prelude-ls: 1.2.1 7912 + 6066 7913 type-fest@4.41.0: {} 6067 7914 7915 + typed-array-buffer@1.0.3: 7916 + dependencies: 7917 + call-bound: 1.0.4 7918 + es-errors: 1.3.0 7919 + is-typed-array: 1.1.15 7920 + 7921 + typed-array-byte-length@1.0.3: 7922 + dependencies: 7923 + call-bind: 1.0.8 7924 + for-each: 0.3.5 7925 + gopd: 1.2.0 7926 + has-proto: 1.2.0 7927 + is-typed-array: 1.1.15 7928 + 7929 + typed-array-byte-offset@1.0.4: 7930 + dependencies: 7931 + available-typed-arrays: 1.0.7 7932 + call-bind: 1.0.8 7933 + for-each: 0.3.5 7934 + gopd: 1.2.0 7935 + has-proto: 1.2.0 7936 + is-typed-array: 1.1.15 7937 + reflect.getprototypeof: 1.0.10 7938 + 7939 + typed-array-length@1.0.7: 7940 + dependencies: 7941 + call-bind: 1.0.8 7942 + for-each: 0.3.5 7943 + gopd: 1.2.0 7944 + is-typed-array: 1.1.15 7945 + possible-typed-array-names: 1.1.0 7946 + reflect.getprototypeof: 1.0.10 7947 + 6068 7948 typescript@5.9.3: {} 6069 7949 6070 7950 uint8arrays@3.0.0: 6071 7951 dependencies: 6072 7952 multiformats: 9.9.0 7953 + 7954 + unbox-primitive@1.1.0: 7955 + dependencies: 7956 + call-bound: 1.0.4 7957 + has-bigints: 1.1.0 7958 + has-symbols: 1.1.0 7959 + which-boxed-primitive: 1.1.1 6073 7960 6074 7961 undici-types@6.21.0: {} 6075 7962 ··· 6092 7979 browserslist: 4.28.1 6093 7980 escalade: 3.2.0 6094 7981 picocolors: 1.1.1 7982 + 7983 + uri-js@4.4.1: 7984 + dependencies: 7985 + punycode: 2.3.1 6095 7986 6096 7987 urlpattern-polyfill@10.1.0: {} 6097 7988 ··· 6135 8026 tr46: 0.0.3 6136 8027 webidl-conversions: 3.0.1 6137 8028 8029 + which-boxed-primitive@1.1.1: 8030 + dependencies: 8031 + is-bigint: 1.1.0 8032 + is-boolean-object: 1.2.2 8033 + is-number-object: 1.1.1 8034 + is-string: 1.1.1 8035 + is-symbol: 1.1.1 8036 + 8037 + which-builtin-type@1.2.1: 8038 + dependencies: 8039 + call-bound: 1.0.4 8040 + function.prototype.name: 1.1.8 8041 + has-tostringtag: 1.0.2 8042 + is-async-function: 2.1.1 8043 + is-date-object: 1.1.0 8044 + is-finalizationregistry: 1.1.1 8045 + is-generator-function: 1.1.2 8046 + is-regex: 1.2.1 8047 + is-weakref: 1.1.1 8048 + isarray: 2.0.5 8049 + which-boxed-primitive: 1.1.1 8050 + which-collection: 1.0.2 8051 + which-typed-array: 1.1.20 8052 + 8053 + which-collection@1.0.2: 8054 + dependencies: 8055 + is-map: 2.0.3 8056 + is-set: 2.0.3 8057 + is-weakmap: 2.0.2 8058 + is-weakset: 2.0.4 8059 + 8060 + which-typed-array@1.1.20: 8061 + dependencies: 8062 + available-typed-arrays: 1.0.7 8063 + call-bind: 1.0.8 8064 + call-bound: 1.0.4 8065 + for-each: 0.3.5 8066 + get-proto: 1.0.1 8067 + gopd: 1.2.0 8068 + has-tostringtag: 1.0.2 8069 + 6138 8070 which@2.0.2: 6139 8071 dependencies: 6140 8072 isexe: 2.0.0 ··· 6158 8090 stack-trace: 0.0.10 6159 8091 triple-beam: 1.4.1 6160 8092 winston-transport: 4.9.0 8093 + 8094 + word-wrap@1.2.5: {} 6161 8095 6162 8096 wrap-ansi@7.0.0: 6163 8097 dependencies: ··· 6202 8136 dependencies: 6203 8137 buffer-crc32: 0.2.13 6204 8138 fd-slicer: 1.1.0 8139 + 8140 + yocto-queue@0.1.0: {} 6205 8141 6206 8142 yocto-queue@1.2.2: {} 6207 8143