From 62ed12f01b8f64b2bc1f6975447369f40751e045 Mon Sep 17 00:00:00 2001 From: gudnuf Date: Mon, 2 Feb 2026 11:31:11 -0800 Subject: [PATCH 01/19] Improve QR scanner error handling - Keep scanner running after validation errors instead of showing blank screen - Deduplicate rapid scan events to prevent toast spam while QR is held up - Show specific error messages for DomainErrors in send scanner - Rename receive scan component to receive-scanner for consistency --- app/components/qr-scanner/qr-scanner.tsx | 53 +++++++++++++++---- .../receive/{scan.tsx => receive-scanner.tsx} | 5 +- app/features/send/send-scanner.tsx | 36 +++++++++---- app/routes/_protected.receive.scan.tsx | 4 +- 4 files changed, 75 insertions(+), 23 deletions(-) rename app/features/receive/{scan.tsx => receive-scanner.tsx} (94%) diff --git a/app/components/qr-scanner/qr-scanner.tsx b/app/components/qr-scanner/qr-scanner.tsx index 6c54fc27f..5eb3f8a63 100644 --- a/app/components/qr-scanner/qr-scanner.tsx +++ b/app/components/qr-scanner/qr-scanner.tsx @@ -5,6 +5,9 @@ import { useToast } from '~/hooks/use-toast'; import { useAnimatedQRDecoder } from '~/lib/cashu/animated-qr-code'; import { useLatest } from '~/lib/use-latest'; +// Time before a failed QR code can be retried to prevent error spam +const DECODE_COOLDOWN_MS = 3000; + const AnimatedScanProgress = ({ progress }: { progress: number }) => { if (progress === 0) return null; @@ -28,9 +31,9 @@ const AnimatedScanProgress = ({ progress }: { progress: number }) => { type QRScannerProps = { /** - * The callback function to be called when the QR code is decoded. + * Return `true` to stop scanning, `false` to continue scanning. */ - onDecode: (decoded: string) => void; + onDecode: (decoded: string) => Promise | boolean; }; /** @@ -48,32 +51,64 @@ export const QRScanner = ({ onDecode }: QRScannerProps) => { const [currentFragment, setCurrentFragment] = useState(''); const decodeRef = useLatest(onDecode); + const processingValue = useRef(null); + const lastFailed = useRef<{ value: string; time: number } | null>(null); + + // Deduplicates rapid scan events to prevent error spam while QR is held up + const handleDecodeRef = useLatest( + async (decoded: string): Promise => { + if (processingValue.current === decoded) { + return false; + } + + const failed = lastFailed.current; + if ( + failed?.value === decoded && + Date.now() - failed.time < DECODE_COOLDOWN_MS + ) { + return false; + } + + processingValue.current = decoded; + const shouldStop = await decodeRef.current(decoded); + processingValue.current = null; + + if (shouldStop) { + scanner.current?.destroy(); + } else { + lastFailed.current = { value: decoded, time: Date.now() }; + } + + return shouldStop; + }, + ); + const { progress, error } = useAnimatedQRDecoder({ fragment: currentFragment, - onDecode: (decoded) => { + onDecode: async (decoded) => { setCurrentFragment(''); - decodeRef.current(decoded); - scanner.current?.destroy(); + await handleDecodeRef.current(decoded); }, }); + const { toast } = useToast(); useEffect(() => { - error && + if (error) { toast({ title: 'Error decoding QR code', description: error.message, variant: 'destructive', }); + } }, [error, toast]); useEffect(() => { - const handleResult = (result: Scanner.ScanResult): void => { + const handleResult = async (result: Scanner.ScanResult): Promise => { if (result.data.toLowerCase().startsWith('ur:')) { setCurrentFragment(result.data); } else { - decodeRef.current(result.data); - scanner.current?.destroy(); + await handleDecodeRef.current(result.data); } }; diff --git a/app/features/receive/scan.tsx b/app/features/receive/receive-scanner.tsx similarity index 94% rename from app/features/receive/scan.tsx rename to app/features/receive/receive-scanner.tsx index 8219e16d0..b5ff9744c 100644 --- a/app/features/receive/scan.tsx +++ b/app/features/receive/receive-scanner.tsx @@ -11,7 +11,7 @@ import { extractCashuToken } from '~/lib/cashu'; import { useNavigateWithViewTransition } from '~/lib/transitions'; import { useReceiveStore } from './receive-provider'; -export default function Scan() { +export default function ReceiveScanner() { const { toast } = useToast(); const navigate = useNavigateWithViewTransition(); const receiveAccountId = useReceiveStore((s) => s.accountId); @@ -36,7 +36,7 @@ export default function Scan() { description: 'Please scan a valid cashu token', variant: 'destructive', }); - return; + return false; // Keep scanning } const encodedToken = getEncodedToken(token); @@ -52,6 +52,7 @@ export default function Scan() { applyTo: 'newView', }, ); + return true; }} /> diff --git a/app/features/send/send-scanner.tsx b/app/features/send/send-scanner.tsx index 34d328eed..7fd190183 100644 --- a/app/features/send/send-scanner.tsx +++ b/app/features/send/send-scanner.tsx @@ -10,6 +10,7 @@ import { useToast } from '~/hooks/use-toast'; import type { Money } from '~/lib/money'; import { useNavigateWithViewTransition } from '~/lib/transitions/view-transition'; import type { Account } from '../accounts/account'; +import { DomainError, getErrorMessage } from '../shared/error'; import { useSendStore } from './send-provider'; /** @@ -42,43 +43,58 @@ export default function SendScanner() { const convert = useConverter(sendAccount); - const handleDecode = async (input: string) => { + const handleDecode = async (input: string): Promise => { const selectDestinationResult = await selectDestination(input); if (!selectDestinationResult.success) { - // TODO: implement this https://github.com/MakePrisms/agicash/pull/331#discussion_r2024690976 - return toast({ + toast({ title: 'Invalid input', description: selectDestinationResult.error, variant: 'destructive', }); + return false; // Keep scanning } const { amount } = selectDestinationResult.data; if (!amount) { // Navigate to send input to enter the amount - return navigate('/send', { + navigate('/send', { applyTo: 'oldView', transition: 'slideDown', }); + return true; } const convertedAmount = amount.currency !== sendAccount.currency ? convert(amount) : undefined; const result = await continueSend(amount, convertedAmount); - if (!result.success || result.next !== 'confirmQuote') { - return toast({ - title: 'Error', - description: 'Failed to get a send quote. Please try again', - variant: 'destructive', - }); + if (!result.success) { + const toastOptions = + result.error instanceof DomainError + ? { description: result.error.message } + : { + title: 'Error', + description: getErrorMessage( + result.error, + 'Failed to get a send quote. Please try again', + ), + variant: 'destructive' as const, + }; + + toast(toastOptions); + return false; // Keep scanning + } + + if (result.next !== 'confirmQuote') { + return false; // Keep scanning } navigate('/send/confirm', { applyTo: 'newView', transition: 'slideUp', }); + return true; }; return ( diff --git a/app/routes/_protected.receive.scan.tsx b/app/routes/_protected.receive.scan.tsx index 15b508fdf..5f61a35d3 100644 --- a/app/routes/_protected.receive.scan.tsx +++ b/app/routes/_protected.receive.scan.tsx @@ -1,10 +1,10 @@ import { Page } from '~/components/page'; -import Scan from '~/features/receive/scan'; +import ReceiveScanner from '~/features/receive/receive-scanner'; export default function ReceiveScan() { return ( - + ); } From 9c58f3204b7eb0f216565023f9059b2baa96a5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Sat, 14 Feb 2026 00:26:59 +0100 Subject: [PATCH 02/19] Upgrade bun types package --- README.md | 10 +++++----- bun.lock | 6 +++--- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 833a774b8..659a20577 100644 --- a/README.md +++ b/README.md @@ -68,11 +68,11 @@ Try to make feature branches short-lived and concise (avoid implementing multipl To update devenv CLI on your machine, for macOS run `nix-env --upgrade --attr devenv -f https://github.com/NixOS/nixpkgs/tarball/nixpkgs-unstable`. -To update devenv packages, run `devenv update`. When updating `bun`, make sure to update the `engines` version in -`package.json` and version specified in `.github/actions/setup-environment/action.yml`. When updating `node`, update the -`.nvmrc` file and `engines` version in `package.json`. Note that Vercel does not allow pinning to an exact node version, so -in the `package.json` file, we specify the max patch version, while in the `.nvmrc`, we specify the max version possible -for that range because that is what Vercel will be using when building, too. +To update devenv packages, run `devenv update`. When updating `bun`, make sure to update the `engines` version and version of the `@types/bun` +package in `package.json` and version specified in `.github/actions/setup-environment/action.yml`. When updating `node`, update the `.nvmrc` +file and `engines` version in `package.json`. Note that Vercel does not allow pinning to an exact node version, so in the `package.json` file, +we specify the max patch version, while in the `.nvmrc`, we specify the max version possible for that range because that is what Vercel will be +using when building, too. ## Deployment diff --git a/bun.lock b/bun.lock index 7ea608382..bed99acf5 100644 --- a/bun.lock +++ b/bun.lock @@ -68,7 +68,7 @@ "@stablelib/base64": "2.0.1", "@stablelib/chacha20poly1305": "2.0.1", "@types/big.js": "6.2.2", - "@types/bun": "1.2.8", + "@types/bun": "1.3.8", "@types/express": "5.0.0", "@types/jwt-encode": "1.0.3", "@types/react": "19.2.13", @@ -648,7 +648,7 @@ "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], - "@types/bun": ["@types/bun@1.2.8", "", { "dependencies": { "bun-types": "1.2.7" } }, "sha512-t8L1RvJVUghW5V+M/fL3Thbxcs0HwNsXsnTEBEfEVqGteiJToOlZ/fyOEaR1kZsNqnu+3XA4RI/qmnX4w6+S+w=="], + "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], @@ -798,7 +798,7 @@ "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "bun-types": ["bun-types@1.2.7", "", { "dependencies": { "@types/node": "*", "@types/ws": "*" } }, "sha512-P4hHhk7kjF99acXqKvltyuMQ2kf/rzIw3ylEDpCxDS9Xa0X0Yp/gJu/vDCucmWpiur5qJ0lwB2bWzOXa2GlHqA=="], + "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], diff --git a/package.json b/package.json index 509fb93ea..52a9506f8 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "@stablelib/base64": "2.0.1", "@stablelib/chacha20poly1305": "2.0.1", "@types/big.js": "6.2.2", - "@types/bun": "1.2.8", + "@types/bun": "1.3.8", "@types/express": "5.0.0", "@types/jwt-encode": "1.0.3", "@types/react": "19.2.13", From 66e6fbc6621b85e4a779b51666e96b792c022fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Sat, 14 Feb 2026 00:27:27 +0100 Subject: [PATCH 03/19] Add useThrottle react hook --- app/lib/use-throttle/index.ts | 1 + app/lib/use-throttle/use-throttle.test.ts | 244 ++++++++++++++++++++++ app/lib/use-throttle/use-throttle.ts | 96 +++++++++ 3 files changed, 341 insertions(+) create mode 100644 app/lib/use-throttle/index.ts create mode 100644 app/lib/use-throttle/use-throttle.test.ts create mode 100644 app/lib/use-throttle/use-throttle.ts diff --git a/app/lib/use-throttle/index.ts b/app/lib/use-throttle/index.ts new file mode 100644 index 000000000..ddccac282 --- /dev/null +++ b/app/lib/use-throttle/index.ts @@ -0,0 +1 @@ +export { useThrottle } from './use-throttle'; diff --git a/app/lib/use-throttle/use-throttle.test.ts b/app/lib/use-throttle/use-throttle.test.ts new file mode 100644 index 000000000..44df1e84e --- /dev/null +++ b/app/lib/use-throttle/use-throttle.test.ts @@ -0,0 +1,244 @@ +import { + afterEach, + beforeEach, + describe, + expect, + jest, + mock, + test, +} from 'bun:test'; +import { createThrottle } from './use-throttle'; + +beforeEach(() => { + jest.useFakeTimers(); +}); + +afterEach(() => { + jest.useRealTimers(); +}); + +function setup(delayMs = 1000) { + const fn = mock(() => undefined); + const { throttled, cancel } = createThrottle(() => fn, delayMs); + return { fn, throttled, cancel }; +} + +describe('createThrottle', () => { + test('fires immediately on the first call', () => { + const { fn, throttled } = setup(); + + throttled(); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('passes arguments through', () => { + const fn = mock((_a: string, _b: number) => undefined); + const { throttled } = createThrottle(() => fn, 1000); + + throttled('hello', 42); + + expect(fn).toHaveBeenCalledWith('hello', 42); + }); + + test('suppresses calls within the delay window', () => { + const { fn, throttled } = setup(100); + + throttled(); + throttled(); + throttled(); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('fires trailing call with latest args after delay', () => { + const fn = mock((_v: string) => undefined); + const { throttled } = createThrottle(() => fn, 50); + + throttled('first'); + throttled('second'); + throttled('third'); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith('first'); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(2); + expect(fn).toHaveBeenLastCalledWith('third'); + }); + + test('allows a new call after delay has passed', () => { + const { fn, throttled } = setup(50); + + throttled(); + expect(fn).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(50); + + throttled(); + expect(fn).toHaveBeenCalledTimes(2); + }); + + test('trailing call uses the latest arguments, not earlier ones', () => { + const fn = mock((_v: number) => undefined); + const { throttled } = createThrottle(() => fn, 50); + + throttled(1); + throttled(2); + throttled(3); + throttled(4); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(2); + expect(fn.mock.calls).toEqual([[1], [4]]); + }); + + test('cancel prevents the trailing call from firing', () => { + const { fn, throttled, cancel } = setup(50); + + throttled(); + throttled(); // would schedule trailing + + cancel(); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('can be called again after trailing fires and delay passes', () => { + const { fn, throttled } = setup(50); + + // First burst + throttled(); + throttled(); + + // Wait for trailing + full delay to elapse + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(2); + + // Second burst starts fresh + throttled(); + throttled(); + + jest.advanceTimersByTime(100); + expect(fn).toHaveBeenCalledTimes(4); + }); + + test('single call with no follow-ups does not fire a trailing call', () => { + const { fn, throttled } = setup(50); + + throttled(); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('always calls the latest callback from getCallback', () => { + const calls: string[] = []; + let currentFn = () => calls.push('old'); + const { throttled } = createThrottle(() => currentFn, 50); + + throttled(); + expect(calls).toEqual(['old']); + + currentFn = () => calls.push('new'); + throttled(); // schedules trailing + + jest.advanceTimersByTime(50); + + expect(calls).toEqual(['old', 'new']); + }); +}); + +describe('createThrottle with trailing: false', () => { + test('fires immediately on the first call', () => { + const fn = mock(() => undefined); + const { throttled } = createThrottle(() => fn, 50, { trailing: false }); + + throttled(); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('suppresses all calls within the delay window', () => { + const fn = mock(() => undefined); + const { throttled } = createThrottle(() => fn, 50, { trailing: false }); + + throttled(); + throttled(); + throttled(); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(1); + }); + + test('allows a new call after delay has passed', () => { + const fn = mock(() => undefined); + const { throttled } = createThrottle(() => fn, 50, { trailing: false }); + + throttled(); + expect(fn).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(50); + + throttled(); + expect(fn).toHaveBeenCalledTimes(2); + }); +}); + +describe('createThrottle with leading: false', () => { + test('does not fire immediately on the first call', () => { + const fn = mock(() => undefined); + const { throttled } = createThrottle(() => fn, 50, { leading: false }); + + throttled(); + + expect(fn).toHaveBeenCalledTimes(0); + }); + + test('fires after the delay expires', () => { + const fn = mock((_v: string) => undefined); + const { throttled } = createThrottle(() => fn, 50, { leading: false }); + + throttled('hello'); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith('hello'); + }); + + test('uses the latest arguments for the trailing call', () => { + const fn = mock((_v: number) => undefined); + const { throttled } = createThrottle(() => fn, 50, { leading: false }); + + throttled(1); + throttled(2); + throttled(3); + + jest.advanceTimersByTime(50); + + expect(fn).toHaveBeenCalledTimes(1); + expect(fn).toHaveBeenCalledWith(3); + }); + + test('allows a new trailing call after delay has passed', () => { + const fn = mock(() => undefined); + const { throttled } = createThrottle(() => fn, 50, { leading: false }); + + throttled(); + jest.advanceTimersByTime(50); + expect(fn).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(50); + + throttled(); + jest.advanceTimersByTime(50); + expect(fn).toHaveBeenCalledTimes(2); + }); +}); diff --git a/app/lib/use-throttle/use-throttle.ts b/app/lib/use-throttle/use-throttle.ts new file mode 100644 index 000000000..d94e91425 --- /dev/null +++ b/app/lib/use-throttle/use-throttle.ts @@ -0,0 +1,96 @@ +import { useEffect, useRef } from 'react'; +import { useLatest } from '~/lib/use-latest'; + +type ThrottleOptions = { + /** Fire immediately on the first call. Defaults to `true`. */ + leading?: boolean; + /** Fire on trailing edge after the delay expires. Defaults to `true`. */ + trailing?: boolean; +}; + +/** + * Creates a throttled version of a function that fires at most once per + * `delayMs`. By default both leading and trailing edges are enabled: the first + * call fires immediately and the last call during the delay fires when it + * expires. + */ +export function createThrottle( + getCallback: () => (...args: A) => void, + delayMs: number, + { leading = true, trailing = true }: ThrottleOptions = {}, +): { throttled: (...args: A) => void; cancel: () => void } { + let lastCallTime = 0; + let timeoutId: ReturnType | null = null; + let latestArgs: A | null = null; + + const throttled = (...args: A) => { + const now = Date.now(); + const remaining = delayMs - (now - lastCallTime); + + if (remaining <= 0) { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + lastCallTime = now; + if (leading) { + getCallback()(...args); + } else { + latestArgs = args; + } + } + + if (remaining > 0 || !leading) { + if (trailing && !timeoutId) { + latestArgs = latestArgs ?? args; + timeoutId = setTimeout( + () => { + lastCallTime = Date.now(); + timeoutId = null; + getCallback()(...(latestArgs as A)); + latestArgs = null; + }, + remaining > 0 ? remaining : delayMs, + ); + } else if (trailing) { + latestArgs = args; + } + } + }; + + const cancel = () => { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + latestArgs = null; + }; + + return { throttled, cancel }; +} + +/** + * Returns a throttled version of the callback that fires at most once per + * `delayMs`. By default both leading and trailing edges are enabled: the first + * call fires immediately and the last call during the delay fires when it + * expires. + * + * Always invokes the latest version of `callback` (via `useLatest`), and the + * returned function is referentially stable so it's safe to call from effects. + * The trailing timeout is automatically cleaned up on unmount. + */ +export function useThrottle( + callback: (...args: A) => void, + delayMs: number, + options?: ThrottleOptions, +): (...args: A) => void { + const callbackRef = useLatest(callback); + + const { throttled, cancel } = useRef( + createThrottle(() => callbackRef.current, delayMs, options), + ).current; + + useEffect(() => cancel, [cancel]); + + return throttled; +} From 9a1421a739b9638ba4dbd09e5387f8b3e40e94e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Sat, 14 Feb 2026 00:27:53 +0100 Subject: [PATCH 04/19] Simplify qr-scanner component --- app/components/qr-scanner/qr-scanner.tsx | 75 ++++++------------------ app/features/receive/receive-scanner.tsx | 3 +- app/features/send/send-scanner.tsx | 12 ++-- 3 files changed, 24 insertions(+), 66 deletions(-) diff --git a/app/components/qr-scanner/qr-scanner.tsx b/app/components/qr-scanner/qr-scanner.tsx index 5eb3f8a63..f27860749 100644 --- a/app/components/qr-scanner/qr-scanner.tsx +++ b/app/components/qr-scanner/qr-scanner.tsx @@ -3,9 +3,8 @@ import Scanner from 'qr-scanner'; import { useEffect, useRef, useState } from 'react'; import { useToast } from '~/hooks/use-toast'; import { useAnimatedQRDecoder } from '~/lib/cashu/animated-qr-code'; -import { useLatest } from '~/lib/use-latest'; +import { useThrottle } from '~/lib/use-throttle'; -// Time before a failed QR code can be retried to prevent error spam const DECODE_COOLDOWN_MS = 3000; const AnimatedScanProgress = ({ progress }: { progress: number }) => { @@ -30,10 +29,7 @@ const AnimatedScanProgress = ({ progress }: { progress: number }) => { }; type QRScannerProps = { - /** - * Return `true` to stop scanning, `false` to continue scanning. - */ - onDecode: (decoded: string) => Promise | boolean; + onDecode: (decoded: string) => void; }; /** @@ -42,52 +38,26 @@ type QRScannerProps = { * The scanner can read static QR codes and * [BC-UR](https://github.com/BlockchainCommons/UR) encoded animated QR codes. * - * Calls `onDecode` with the text decoded from the QR code and toasts any errors - * that occur during decoding. + * Calls `onDecode` with the text decoded from the QR code (throttled to prevent + * spam) and toasts any errors that occur during decoding. To stop scanning, + * unmount the component (e.g. by navigating away). */ export const QRScanner = ({ onDecode }: QRScannerProps) => { const videoRef = useRef(null); - const scanner = useRef(null); const [currentFragment, setCurrentFragment] = useState(''); - const decodeRef = useLatest(onDecode); - const processingValue = useRef(null); - const lastFailed = useRef<{ value: string; time: number } | null>(null); - - // Deduplicates rapid scan events to prevent error spam while QR is held up - const handleDecodeRef = useLatest( - async (decoded: string): Promise => { - if (processingValue.current === decoded) { - return false; - } - - const failed = lastFailed.current; - if ( - failed?.value === decoded && - Date.now() - failed.time < DECODE_COOLDOWN_MS - ) { - return false; - } - - processingValue.current = decoded; - const shouldStop = await decodeRef.current(decoded); - processingValue.current = null; - - if (shouldStop) { - scanner.current?.destroy(); - } else { - lastFailed.current = { value: decoded, time: Date.now() }; - } - - return shouldStop; - }, - ); + // Leading-edge only: the scanner fires continuously, so the next leading + // edge after the cooldown naturally handles retries. Trailing would cause + // a second invocation while the first async handler is still in-flight. + const throttledDecode = useThrottle(onDecode, DECODE_COOLDOWN_MS, { + trailing: false, + }); const { progress, error } = useAnimatedQRDecoder({ fragment: currentFragment, - onDecode: async (decoded) => { + onDecode: (decoded) => { setCurrentFragment(''); - await handleDecodeRef.current(decoded); + throttledDecode(decoded); }, }); @@ -104,11 +74,11 @@ export const QRScanner = ({ onDecode }: QRScannerProps) => { }, [error, toast]); useEffect(() => { - const handleResult = async (result: Scanner.ScanResult): Promise => { + const handleResult = (result: Scanner.ScanResult): void => { if (result.data.toLowerCase().startsWith('ur:')) { setCurrentFragment(result.data); } else { - await handleDecodeRef.current(result.data); + throttledDecode(result.data); } }; @@ -121,22 +91,13 @@ export const QRScanner = ({ onDecode }: QRScannerProps) => { highlightScanRegion: true, highlightCodeOutline: true, }); - scanner.current = scannerInstance; - const startedPromise = scannerInstance.start(); + scannerInstance.start(); return () => { - startedPromise - .catch((e) => { - // Catch is there not to show this error in console https://developer.chrome.com/blog/play-request-was-interrupted. - // This happens when useEffect is triggered twice in short amount of time. E.g. when strict mode is on. - console.debug(e); - }) - .finally(() => { - scannerInstance.destroy(); - }); + scannerInstance.destroy(); }; - }, []); + }, [throttledDecode]); return (
diff --git a/app/features/receive/receive-scanner.tsx b/app/features/receive/receive-scanner.tsx index b5ff9744c..835132656 100644 --- a/app/features/receive/receive-scanner.tsx +++ b/app/features/receive/receive-scanner.tsx @@ -36,7 +36,7 @@ export default function ReceiveScanner() { description: 'Please scan a valid cashu token', variant: 'destructive', }); - return false; // Keep scanning + return; } const encodedToken = getEncodedToken(token); @@ -52,7 +52,6 @@ export default function ReceiveScanner() { applyTo: 'newView', }, ); - return true; }} /> diff --git a/app/features/send/send-scanner.tsx b/app/features/send/send-scanner.tsx index 7fd190183..dcaa5817f 100644 --- a/app/features/send/send-scanner.tsx +++ b/app/features/send/send-scanner.tsx @@ -43,7 +43,7 @@ export default function SendScanner() { const convert = useConverter(sendAccount); - const handleDecode = async (input: string): Promise => { + const handleDecode = async (input: string) => { const selectDestinationResult = await selectDestination(input); if (!selectDestinationResult.success) { toast({ @@ -51,18 +51,17 @@ export default function SendScanner() { description: selectDestinationResult.error, variant: 'destructive', }); - return false; // Keep scanning + return; } const { amount } = selectDestinationResult.data; if (!amount) { - // Navigate to send input to enter the amount navigate('/send', { applyTo: 'oldView', transition: 'slideDown', }); - return true; + return; } const convertedAmount = @@ -83,18 +82,17 @@ export default function SendScanner() { }; toast(toastOptions); - return false; // Keep scanning + return; } if (result.next !== 'confirmQuote') { - return false; // Keep scanning + return; } navigate('/send/confirm', { applyTo: 'newView', transition: 'slideUp', }); - return true; }; return ( From ce1d762fce49266f4d0bc07e7f6a95dfe2b4b3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Sat, 14 Feb 2026 00:35:37 +0100 Subject: [PATCH 05/19] Sync ci bun version --- .github/actions/setup-environment/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index 65a030c26..2f64e50ba 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -6,7 +6,7 @@ runs: - name: Install Bun uses: oven-sh/setup-bun@v2 with: - bun-version: 1.3.4 + bun-version: 1.3.8 - name: Install Dependencies shell: bash From 19d8f2a4ef7e7d6c2b1684f739ebb74eada2d505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Sat, 14 Feb 2026 12:23:59 +0100 Subject: [PATCH 06/19] Add sentry integration which enables error cause to be attached to the Sentry error events --- app/entry.client.tsx | 1 + app/instrument.server.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/app/entry.client.tsx b/app/entry.client.tsx index f26609299..e1c61c7f4 100644 --- a/app/entry.client.tsx +++ b/app/entry.client.tsx @@ -55,6 +55,7 @@ Sentry.init({ Sentry.consoleLoggingIntegration(), Sentry.reactRouterTracingIntegration(), Sentry.browserProfilingIntegration(), + Sentry.extraErrorDataIntegration({ depth: 5 }), ], // Performance monitoring diff --git a/app/instrument.server.ts b/app/instrument.server.ts index 9bd2d3eeb..6d984cd7a 100644 --- a/app/instrument.server.ts +++ b/app/instrument.server.ts @@ -117,6 +117,7 @@ Sentry.init({ integrations: [ Sentry.consoleLoggingIntegration(), nodeProfilingIntegration(), + Sentry.extraErrorDataIntegration({ depth: 5 }), ], // Performance monitoring From 7d879d16ed591d18e91ccb1b7368ee21fcd32f66 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Feb 2026 15:12:10 +0000 Subject: [PATCH 07/19] Add fail() service method for cashu receive swaps Extract repository.fail() call into a dedicated service method with state checks, matching the pattern used by other services (CashuSendSwapService, CashuReceiveQuoteService, etc.): - No-op if swap is already FAILED - Validates swap is PENDING before failing - Clear error message with current state on invalid transition Closes #845 https://claude.ai/code/session_01Hw3d3Av8dccEqKCupLbGe3 --- .../receive/cashu-receive-swap-service.ts | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/app/features/receive/cashu-receive-swap-service.ts b/app/features/receive/cashu-receive-swap-service.ts index 012ae76d0..afea485da 100644 --- a/app/features/receive/cashu-receive-swap-service.ts +++ b/app/features/receive/cashu-receive-swap-service.ts @@ -104,6 +104,35 @@ export class CashuReceiveSwapService { }); } + /** + * Fails a cashu receive swap. + * If the swap is already failed, it's a no-op that returns the passed swap. + * @param swap The receive swap to fail. + * @param reason The reason for the failure. + * @returns The failed receive swap. + * @throws An error if the swap is not in a PENDING state. + */ + async fail( + swap: CashuReceiveSwap, + reason: string, + ): Promise { + if (swap.state === 'FAILED') { + return swap; + } + + if (swap.state !== 'PENDING') { + throw new Error( + `Cannot fail receive swap that is not pending. Current state: ${swap.state}`, + ); + } + + return this.receiveSwapRepository.fail({ + tokenHash: swap.tokenHash, + userId: swap.userId, + reason, + }); + } + /** * Completes the receive swap by executing the swap with the mint and storing the output proofs. * If the receive swap is already completed, it's a no-op that returns back passed receive swap, account and an empty list of added proof ids. @@ -157,11 +186,10 @@ export class CashuReceiveSwapService { }); } catch (error) { if (error instanceof Error && error.message === 'TOKEN_ALREADY_CLAIMED') { - const failedReceiveSwap = await this.receiveSwapRepository.fail({ - tokenHash: receiveSwap.tokenHash, - userId: receiveSwap.userId, - reason: 'Token already claimed', - }); + const failedReceiveSwap = await this.fail( + receiveSwap, + 'Token already claimed', + ); return { swap: failedReceiveSwap, account, addedProofs: [] }; } From 22a476c1fb54c1712e3724157806a294dd0b957d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Mon, 16 Feb 2026 15:54:52 +0100 Subject: [PATCH 08/19] Prevent QR scanner from firing onDecode while previous call is in-flight The send scanner's onDecode is async (fetches a send quote over the network) and can take longer than the 3s throttle cooldown. When that happens, the scanner fires onDecode again since the QR code is still in view, causing a double navigation to the confirm page. Add a ref-based processing guard in QRScanner that skips decode calls while the previous one hasn't resolved yet. Also widen the onDecode type to accept Promise so async consumers are explicitly supported. Co-Authored-By: Claude Opus 4.6 --- app/components/qr-scanner/qr-scanner.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/components/qr-scanner/qr-scanner.tsx b/app/components/qr-scanner/qr-scanner.tsx index f27860749..68fa56bd3 100644 --- a/app/components/qr-scanner/qr-scanner.tsx +++ b/app/components/qr-scanner/qr-scanner.tsx @@ -29,7 +29,7 @@ const AnimatedScanProgress = ({ progress }: { progress: number }) => { }; type QRScannerProps = { - onDecode: (decoded: string) => void; + onDecode: (decoded: string) => void | Promise; }; /** @@ -46,10 +46,22 @@ export const QRScanner = ({ onDecode }: QRScannerProps) => { const videoRef = useRef(null); const [currentFragment, setCurrentFragment] = useState(''); + const isProcessingRef = useRef(false); + + const guardedDecode = async (decoded: string) => { + if (isProcessingRef.current) return; + isProcessingRef.current = true; + try { + await onDecode(decoded); + } finally { + isProcessingRef.current = false; + } + }; + // Leading-edge only: the scanner fires continuously, so the next leading // edge after the cooldown naturally handles retries. Trailing would cause // a second invocation while the first async handler is still in-flight. - const throttledDecode = useThrottle(onDecode, DECODE_COOLDOWN_MS, { + const throttledDecode = useThrottle(guardedDecode, DECODE_COOLDOWN_MS, { trailing: false, }); From 060086d29e4b8a34db1b1cfefd513161fc3501e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Mon, 16 Feb 2026 15:02:17 +0100 Subject: [PATCH 09/19] Don't report any server 4xx errors to Sentry --- app/instrument.server.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/instrument.server.ts b/app/instrument.server.ts index 6d984cd7a..606bdcb43 100644 --- a/app/instrument.server.ts +++ b/app/instrument.server.ts @@ -139,17 +139,16 @@ Sentry.init({ }, beforeSend(event) { - // Filter out 404s from error reporting - if (event.exception) { - const error = event.exception.values?.[0]; - if ( - error?.type === 'NotFoundException' || - error?.value?.includes('404') || - error?.value?.includes('No route matches URL') - ) { - return null; - } + // Filter out 4xx React Router errors (bots, crawlers, invalid requests) + const serialized = event.extra?.__serialized__ as + | { status?: number } + | undefined; + const status = serialized?.status; + + if (status !== undefined && status >= 400 && status < 500) { + return null; } + return event; }, }); From 24fae569f3a8b498b6e4312aaa5a1bb3cd5bddef Mon Sep 17 00:00:00 2001 From: gudnuf Date: Tue, 10 Feb 2026 16:13:49 -0800 Subject: [PATCH 10/19] update claude code CI workflow and add devenv setup action - Expand trigger conditions (assignee, label) - Add concurrency control and timeout - Replace inline Nix setup with reusable setup-devenv action - Scope allowed tools explicitly and improve system prompt - Use commit signing and sonnet model --- .github/actions/setup-devenv/action.yml | 43 +++++++ .github/actions/setup-environment/action.yml | 6 +- .github/workflows/claude.yml | 128 ++++++++++++------- 3 files changed, 127 insertions(+), 50 deletions(-) create mode 100644 .github/actions/setup-devenv/action.yml diff --git a/.github/actions/setup-devenv/action.yml b/.github/actions/setup-devenv/action.yml new file mode 100644 index 000000000..0082e3753 --- /dev/null +++ b/.github/actions/setup-devenv/action.yml @@ -0,0 +1,43 @@ +name: 'Setup Environment (Devenv)' +description: 'Full Nix devenv setup with all project tools (bun, git, jq, mkcert, etc). Slower than setup-environment but matches local dev. Use for Claude Code and workflows that need the complete toolchain.' + +inputs: + frozen-lockfile: + description: 'Use --frozen-lockfile for bun install (set false to allow lockfile changes)' + required: false + default: 'true' + +runs: + using: composite + steps: + - name: Install Nix + uses: cachix/install-nix-action@v31 + + - name: Setup devenv cache + uses: cachix/cachix-action@v16 + with: + name: devenv + + - name: Install devenv and direnv + shell: bash + run: nix profile install nixpkgs#devenv nixpkgs#direnv + + - name: Build devenv and export environment + shell: bash + run: | + devenv shell -- true + direnv allow . + direnv export gha >> "$GITHUB_ENV" + + - name: Setup .env + shell: bash + run: cp .env.example .env + + - name: Install dependencies + shell: bash + run: | + if [ "${{ inputs.frozen-lockfile }}" = "true" ]; then + bun install --frozen-lockfile + else + bun install + fi diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index 2f64e50ba..2fe5d2550 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -1,5 +1,5 @@ -name: 'Setup Environment' -description: 'Sets up Agicash development environment' +name: 'Setup Environment (CI)' +description: 'Lightweight setup for CI jobs. Installs only bun and dependencies. Use setup-devenv for the full Nix development environment.' runs: using: composite steps: @@ -10,4 +10,4 @@ runs: - name: Install Dependencies shell: bash - run: bun install --frozen-lockfile \ No newline at end of file + run: bun install --frozen-lockfile diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 2046ba198..85acf9b5c 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -6,76 +6,110 @@ on: pull_request_review_comment: types: [created] issues: - types: [opened, assigned] + types: [opened, assigned, labeled] pull_request_review: types: [submitted] jobs: claude: - if: | - (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || - (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || - (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + # --- Trigger filter --- + # Comments & reviews: @claude must be mentioned in the body + # Issues: @claude in title/body, assigned to "claude", or "claude" label added + if: >- + github.event_name == 'issue_comment' + && contains(github.event.comment.body, '@claude') + || + github.event_name == 'pull_request_review_comment' + && contains(github.event.comment.body, '@claude') + || + github.event_name == 'pull_request_review' + && contains(github.event.review.body, '@claude') + || + github.event_name == 'issues' && ( + contains(github.event.issue.title, '@claude') + || contains(github.event.issue.body, '@claude') + || github.event.action == 'assigned' + && github.event.assignee.login == 'claude' + || github.event.action == 'labeled' + && github.event.label.name == 'claude' + ) runs-on: ubuntu-latest + timeout-minutes: 30 + concurrency: + group: claude-${{ github.event.issue.number || github.event.pull_request.number }} + cancel-in-progress: false permissions: - contents: read # Read code (not push commits) - pull-requests: write # POST comments on PRs - issues: write # POST comments on issues - id-token: write - actions: read # Read CI results on PRs + contents: write # Push commits, create branches + pull-requests: write # Create/comment on PRs + issues: write # Create/comment on issues + actions: read # Read CI results steps: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 1 + fetch-depth: 50 - - name: Install Nix - uses: cachix/install-nix-action@v31 - - - name: Setup devenv cache - uses: cachix/cachix-action@v16 + - name: Setup Devenv + uses: ./.github/actions/setup-devenv with: - name: devenv - - - name: Install devenv and direnv - run: nix profile install nixpkgs#devenv nixpkgs#direnv - - - name: Build devenv and export environment - run: | - devenv shell -- true - direnv allow . - direnv export gha >> "$GITHUB_ENV" - - - name: Setup environment - run: cp .env.example .env - - - name: Install dependencies - run: bun install --frozen-lockfile + frozen-lockfile: 'false' - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + use_commit_signing: true # Commits via GitHub API → "Verified" badge, no extra setup. Disallows rebase/cherry-pick (use ssh_signing_key for that). + assignee_trigger: claude + label_trigger: claude + allowed_non_write_users: '' additional_permissions: | actions: read + # Claude Code auto-loads CLAUDE.md (coding standards, commands, patterns). + # --append-system-prompt adds GitHub-specific context without replacing the + # default system prompt or affecting mode detection. + # + # Allowed Bash tools: + # Package mgmt: bun, bunx + # Version ctrl: git (scoped subcommands — no reset/clean/force) + # GitHub CLI: gh (PRs, issues, CI) + # File ops: cp, ls, mkdir, rm, mv + # Utilities: jq, which claude_args: | - --allowedTools "Bash(git fetch origin *),Bash(git checkout *),Bash(git switch *)" - --system-prompt "Read CLAUDE.md first for project-specific coding standards and patterns. + --model sonnet + --append-system-prompt "You are Claude, an AI developer running in GitHub Actions for the agicash repo. devenv is pre-loaded (bun, git, gh, jq, supabase CLI available). + + ## Communication style + - Match response length to complexity. Simple fix = short comment. Architectural change = explain the reasoning. + - No filler, no preamble, no restating the request. Get to the point. + - Always say what you did and why. Link relevant files/lines if helpful. + - If you can't do something, say what blocked you concisely. + + ## General rules + - Read CLAUDE.md and relevant code before making changes. + - Run bun run fix:all after editing TypeScript to catch errors early. + - NEVER install packages unless explicitly asked. When asked, use bun add --exact. + - Keep changes minimal and focused on what was requested. + + ## Behavior by context + + **Issue (feature request or bug):** + - If requirements are ambiguous, ask clarifying questions as a comment — don't guess. + - Implement on a new branch, then open a PR linking the issue. - You are running in a GitHub Actions workflow. + **PR review feedback:** + - Address every comment. If you disagree, explain why briefly. + - Push follow-up commits to the existing branch — don't open a new PR. - Environment: The devenv environment is pre-loaded. Tools like bun, git, and jq are available directly. Supabase CLI is available via bun. + **PR with failing CI:** + - Read CI logs with the CI MCP tools (get_ci_status, download_job_log). + - Fix and push to the same branch. If stuck, comment what's wrong. - Available Commands: - - bun run dev - Start the dev server (http://127.0.0.1:3000) - - bun run fix:all - Run linting, formatting, and type checking - - bun test - Run unit tests - - bun run supabase db start - Start local Supabase database - - bun run db:generate-types - Regenerate DB types + **General question or discussion:** + - Answer directly in a comment. Don't open PRs for questions. - Guidelines: - - Only start the dev server or database if needed to investigate the issue - - Always run bun run fix:all before suggesting code changes to verify they pass checks - - Run tests if the changes affect tested code paths" + ## PR workflow + - Branch naming: claude/issue-NUMBER-short-description + - Commit messages: concise, imperative tense (e.g. 'fix balance calculation for zero-amount proofs') + - One logical change per commit when possible." + --allowedTools "Bash(bun *),Bash(bunx *),Bash(git add *),Bash(git commit *),Bash(git push *),Bash(git checkout *),Bash(git switch *),Bash(git branch *),Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git fetch *),Bash(git merge *),Bash(git stash *),Bash(git rev-parse *),Bash(gh *),Bash(cp *),Bash(ls *),Bash(mkdir *),Bash(rm *),Bash(mv *),Bash(jq *),Bash(which *)" From 5fd9d1ddde47b6fcdc303599c8464c8312b9733a Mon Sep 17 00:00:00 2001 From: gudnuf Date: Tue, 10 Feb 2026 16:14:37 -0800 Subject: [PATCH 11/19] add Claude Code local dev config and typescript LSP - Add settings.json with permissions, format-on-save hook, and LSP plugin - Add format-on-save hook script - Add typescript-language-server to devenv --- .claude/hooks/format-on-save.sh | 26 ++++++++++++++++++++ .claude/settings.json | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 .claude/hooks/format-on-save.sh diff --git a/.claude/hooks/format-on-save.sh b/.claude/hooks/format-on-save.sh new file mode 100755 index 000000000..8c37cd2fd --- /dev/null +++ b/.claude/hooks/format-on-save.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Runs biome format on files after Write or Edit +INPUT=$(cat) +FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') + +# Skip if no file path +if [ -z "$FILE_PATH" ]; then + exit 0 +fi + +# Only format files biome handles +case "$FILE_PATH" in + *.ts|*.tsx|*.js|*.jsx|*.json|*.css) ;; + *) exit 0 ;; +esac + +# Skip node_modules and build artifacts +case "$FILE_PATH" in + */node_modules/*|*/build/*|*/.react-router/*) exit 0 ;; +esac + +# Run biome format on the specific file via package.json script +cd "$CLAUDE_PROJECT_DIR" +bun run format -- "$FILE_PATH" 2>/dev/null + +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json index e9d15713f..e03c0e62d 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,4 +1,46 @@ { + "permissions": { + "allow": [ + "Bash(bun run build:*)", + "Bash(bun run dev:*)", + "Bash(bun run fix:all:*)", + "Bash(bun run fix:staged:*)", + "Bash(bun run typecheck:*)", + "Bash(bun run lint:*)", + "Bash(bun run lint\\:check:*)", + "Bash(bun run format:*)", + "Bash(bun run format\\:check:*)", + "Bash(bun run check:all:*)", + "Bash(bun run db:generate-types:*)", + "Bash(bun test:*)" + ], + "deny": [ + "Bash(rm -rf:*)", + "Bash(supabase db reset:*)", + "Bash(supabase db push:*)", + "Bash(npx:*)", + "Bash(npm:*)", + "Bash(yarn:*)", + "Bash(pnpm:*)", + "Read(.env)", + "Read(.env.*)", + "Read(supabase/.env)" + ] + }, + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-on-save.sh", + "timeout": 15 + } + ] + } + ] + }, "enabledPlugins": { "postgres-best-practices@supabase-agent-skills": true, "feature-dev@claude-plugins-official": true, From b3d82c30e4c1c3ae61c1ea45480438a6b8cb7ffc Mon Sep 17 00:00:00 2001 From: gudnuf Date: Mon, 16 Feb 2026 10:13:38 -0800 Subject: [PATCH 12/19] use opus as default CI model --- .github/workflows/claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 85acf9b5c..0ec8e7f8d 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -76,7 +76,7 @@ jobs: # File ops: cp, ls, mkdir, rm, mv # Utilities: jq, which claude_args: | - --model sonnet + --model opus --append-system-prompt "You are Claude, an AI developer running in GitHub Actions for the agicash repo. devenv is pre-loaded (bun, git, gh, jq, supabase CLI available). ## Communication style From 632f747272c93610d3991cc6d2aaf957353526d4 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:01:27 +0000 Subject: [PATCH 13/19] Upgrade Tailwind CSS from v3 to v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit migrates the project to Tailwind CSS v4 following the official upgrade guide and shadcn/ui v4 migration pattern. Key changes: - Updated tailwindcss to v4.1.18 and added @tailwindcss/vite plugin - Removed postcss, autoprefixer, and postcss.config.js (no longer needed with Vite plugin) - Replaced tailwindcss-animate with tw-animate-css (v4 compatible) - Rewrote app/tailwind.css with v4 CSS-first configuration: - Converted @tailwind directives to @import "tailwindcss" - Moved CSS variables outside @layer and wrapped HSL values with hsl() - Added @theme inline blocks to register color tokens - Migrated custom scrollbar-none plugin to @utility directive - Moved custom keyframes and animations to @theme blocks - Updated deprecated class names across components: - shadow-sm → shadow-xs (2 files) - rounded-sm → rounded-xs (5 files) - outline-none → outline-hidden (14 files) - Removed tailwind.config.ts (configuration now in CSS) - Updated components.json to remove config path The migration preserves all existing functionality while adopting v4's improved architecture and performance. Note: Dependencies need to be installed (bun install) and verification tests need to be run before merging. Co-authored-by: Josip Bojčić --- app/components/ui/badge.tsx | 2 +- app/components/ui/button.tsx | 2 +- app/components/ui/card.tsx | 2 +- app/components/ui/checkbox.tsx | 2 +- app/components/ui/dialog.tsx | 2 +- app/components/ui/dropdown-menu.tsx | 8 +- app/components/ui/hover-card.tsx | 2 +- app/components/ui/input.tsx | 2 +- app/components/ui/radio-group.tsx | 2 +- app/components/ui/select.tsx | 4 +- app/components/ui/tabs.tsx | 4 +- app/components/ui/toast.tsx | 4 +- .../accounts/balance-offline-hover-card.tsx | 2 +- .../settings/profile/editable-username.tsx | 2 +- app/features/theme/color-mode-toggle.tsx | 2 +- app/tailwind.css | 268 ++++++++++----- bun.lock | 306 ++++++------------ components.json | 2 +- package.json | 5 +- postcss.config.js | 6 - tailwind.config.ts | 123 ------- vite.config.ts | 2 + 22 files changed, 317 insertions(+), 437 deletions(-) delete mode 100644 postcss.config.js delete mode 100644 tailwind.config.ts diff --git a/app/components/ui/badge.tsx b/app/components/ui/badge.tsx index b8569792f..263b75792 100644 --- a/app/components/ui/badge.tsx +++ b/app/components/ui/badge.tsx @@ -4,7 +4,7 @@ import type * as React from 'react'; import { cn } from '~/lib/utils'; const badgeVariants = cva( - 'inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + 'inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2', { variants: { variant: { diff --git a/app/components/ui/button.tsx b/app/components/ui/button.tsx index dd5d86b8d..9675a1d6d 100644 --- a/app/components/ui/button.tsx +++ b/app/components/ui/button.tsx @@ -6,7 +6,7 @@ import { LoaderCircle } from 'lucide-react'; import { cn } from '~/lib/utils'; const buttonVariants = cva( - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', { variants: { variant: { diff --git a/app/components/ui/card.tsx b/app/components/ui/card.tsx index 9cb39262e..b295a76f9 100644 --- a/app/components/ui/card.tsx +++ b/app/components/ui/card.tsx @@ -9,7 +9,7 @@ const Card = React.forwardRef<
{children} {showCloseButton && ( - + Close diff --git a/app/components/ui/dropdown-menu.tsx b/app/components/ui/dropdown-menu.tsx index d70fbee24..c3ad429a6 100644 --- a/app/components/ui/dropdown-menu.tsx +++ b/app/components/ui/dropdown-menu.tsx @@ -25,7 +25,7 @@ const DropdownMenuSubTrigger = React.forwardRef< >( span]:line-clamp-1', + 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', className, )} {...props} @@ -116,7 +116,7 @@ const SelectItem = React.forwardRef< diff --git a/app/features/settings/profile/editable-username.tsx b/app/features/settings/profile/editable-username.tsx index a30697bb9..80827e848 100644 --- a/app/features/settings/profile/editable-username.tsx +++ b/app/features/settings/profile/editable-username.tsx @@ -102,7 +102,7 @@ export default function EditableUsername() { inputRef.current = el; }} type="text" - className="w-full bg-transparent text-2xl text-white outline-none" + className="w-full bg-transparent text-2xl text-white outline-hidden" // biome-ignore lint/a11y/noAutofocus: the rule is for accessibility reasons, but for this case it makes sense to autofocus so that the user is editing as soon as they click the edit button autoFocus spellCheck="false" diff --git a/app/features/theme/color-mode-toggle.tsx b/app/features/theme/color-mode-toggle.tsx index 4f88a5c61..59dbe5d7a 100644 --- a/app/features/theme/color-mode-toggle.tsx +++ b/app/features/theme/color-mode-toggle.tsx @@ -32,7 +32,7 @@ export function ColorModeToggle({ className }: { className?: string }) { diff --git a/app/tailwind.css b/app/tailwind.css index 30f62334a..cb91edfe9 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -1,92 +1,198 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 0 0% 3.9%; - --card: 0 0% 100%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - --primary: 0 0% 9%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 96.1%; - --secondary-foreground: 0 0% 9%; - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 89.8%; - --input: 0 0% 89.8%; - --ring: 0 0% 83.1%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; +@import "tailwindcss"; +@plugin "tailwindcss-animate"; + +/* CSS Variables - moved outside @layer for v4 compatibility */ +:root { + --background: hsl(0 0% 100%); + --foreground: hsl(0 0% 3.9%); + --card: hsl(0 0% 100%); + --card-foreground: hsl(0 0% 98%); + --popover: hsl(0 0% 100%); + --popover-foreground: hsl(0 0% 3.9%); + --primary: hsl(0 0% 9%); + --primary-foreground: hsl(0 0% 98%); + --secondary: hsl(0 0% 96.1%); + --secondary-foreground: hsl(0 0% 9%); + --muted: hsl(0 0% 96.1%); + --muted-foreground: hsl(0 0% 45.1%); + --accent: hsl(0 0% 96.1%); + --accent-foreground: hsl(0 0% 9%); + --destructive: hsl(0 84.2% 60.2%); + --destructive-foreground: hsl(0 0% 98%); + --border: hsl(0 0% 89.8%); + --input: hsl(0 0% 89.8%); + --ring: hsl(0 0% 83.1%); + --chart-1: hsl(12 76% 61%); + --chart-2: hsl(173 58% 39%); + --chart-3: hsl(197 37% 24%); + --chart-4: hsl(43 74% 66%); + --chart-5: hsl(27 87% 67%); + --radius: 0.5rem; +} + +/* USD theme - light mode */ +.usd, +:root[class~="usd"] { + /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ + --background: hsl(178 100% 15%); + --foreground: hsl(178 30% 90%); + --primary: hsl(177 42% 26%); + --primary-foreground: hsl(178 30% 90%); + --muted: hsl(178 100% 14%); + --border: hsl(178 100% 21%); + --muted-foreground: hsl(178 30% 81%); + --card: hsl(178 100% 14%); +} + +/* BTC theme - light mode */ +.btc, +:root[class~="btc"] { + /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ + --background: hsl(217 68% 35%); + --foreground: hsl(217 30% 90%); + --primary: hsl(219 44% 45%); + --primary-foreground: hsl(217 30% 90%); + --muted: hsl(217 68% 38%); + --border: hsl(217 70% 45%); + --muted-foreground: hsl(217 30% 85%); + --card: hsl(217 68% 38%); +} + +/* Dark theme */ +.dark, +:root[class~="dark"] { + /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ + --background: hsl(0 0% 3.9%); + --foreground: hsl(0 0% 98%); + --card: hsl(0 0% 3.9%); + --card-foreground: hsl(0 0% 98%); + --popover: hsl(0 0% 3.9%); + --popover-foreground: hsl(0 0% 98%); + --primary: hsl(202 13% 13%); + --primary-foreground: hsl(0 0% 98%); + --secondary: hsl(0 0% 14.9%); + --secondary-foreground: hsl(0 0% 98%); + --muted: hsl(0 0% 12%); + --muted-foreground: hsl(0 0% 63.9%); + --accent: hsl(0 0% 14.9%); + --accent-foreground: hsl(0 0% 98%); + --destructive: hsl(0 62.8% 30.6%); + --destructive-foreground: hsl(0 0% 98%); + --border: hsl(0 0% 14.9%); + --input: hsl(0 0% 14.9%); + --ring: hsl(0 0% 83.1%); + --chart-1: hsl(220 70% 50%); + --chart-2: hsl(160 60% 45%); + --chart-3: hsl(30 80% 55%); + --chart-4: hsl(280 65% 60%); + --chart-5: hsl(340 75% 55%); +} + +/* Register color tokens with Tailwind v4 */ +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); +} + +/* Register theme extensions */ +@theme { + --radius: 0.5rem; + --font-numeric: "Teko", sans-serif; + --font-primary: "Kode Mono", monospace; + --font-size-2xs: 0.625rem; + --animate-shake: shake 0.2s ease-in-out; + --animate-slam: slam 0.4s ease-out both; + --animate-slide-out-up: slide-out-up 300ms ease-out forwards; +} + +/* Custom keyframes */ +@keyframes shake { + 0%, + 100% { + transform: translateX(0); + } + 25% { + transform: translateX(-5px); + } + 50% { + transform: translateX(5px); + } + 75% { + transform: translateX(-5px); } +} - /* USD theme - light mode */ - .usd, - :root[class~="usd"] { - /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ - --background: 178 100% 15%; - --foreground: 178 30% 90%; - --primary: 177 42% 26%; - --primary-foreground: 178 30% 90%; - --muted: 178 100% 14%; - --border: 178 100% 21%; - --muted-foreground: 178 30% 81%; - --card: 178 100% 14%; +@keyframes slam { + 0% { + transform: scale(1); + letter-spacing: normal; + opacity: 1; + } + 25% { + transform: scale(1.05); + letter-spacing: 0.05em; + opacity: 0.9; + } + 50% { + transform: scale(0.98); + letter-spacing: -0.02em; + opacity: 0.95; } + 75% { + transform: scale(1.02); + letter-spacing: 0.02em; + opacity: 0.98; + } + 100% { + transform: scale(1); + letter-spacing: normal; + opacity: 1; + } +} - /* BTC theme - light mode */ - .btc, - :root[class~="btc"] { - /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ - --background: 217 68% 35%; - --foreground: 217 30% 90%; - --primary: 219 44% 45%; - --primary-foreground: 217 30% 90%; - --muted: 217 68% 38%; - --border: 217 70% 45%; - --muted-foreground: 217 30% 85%; - --card: 217 68% 38%; +@keyframes slide-out-up { + 0% { + transform: translateY(0%); + opacity: 1; } + 100% { + transform: translateY(-150%); + opacity: 0; + } +} - .dark, - :root[class~="dark"] { - /* This value is duplicated in app/features/theme/colors.ts. When changing make sure to keep them in sync! */ - --background: 0 0% 3.9%; - --foreground: 0 0% 98%; - --card: 0 0% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 202 13% 13%; - --primary-foreground: 0 0% 98%; - --secondary: 0 0% 14.9%; - --secondary-foreground: 0 0% 98%; - --muted: 0 0% 12%; - --muted-foreground: 0 0% 63.9%; - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 0 0% 14.9%; - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; +/* Custom scrollbar utility */ +@utility scrollbar-none { + -ms-overflow-style: none; + scrollbar-width: none; + &::-webkit-scrollbar { + display: none; } } + +/* Base styles */ @layer base { * { @apply border-border; diff --git a/bun.lock b/bun.lock index bed99acf5..a08aaff20 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "agicash", @@ -67,20 +66,19 @@ "@react-router/dev": "7.13.0", "@stablelib/base64": "2.0.1", "@stablelib/chacha20poly1305": "2.0.1", + "@tailwindcss/vite": "4.1.18", "@types/big.js": "6.2.2", "@types/bun": "1.3.8", "@types/express": "5.0.0", "@types/jwt-encode": "1.0.3", "@types/react": "19.2.13", "@types/react-dom": "19.2.3", - "autoprefixer": "10.4.20", "cross-env": "7.0.3", "dotenv": "16.4.7", "jwt-encode": "1.0.1", "npm-run-all": "4.1.5", - "postcss": "8.4.49", "supabase": "2.75.5", - "tailwindcss": "3.4.16", + "tailwindcss": "4.1.18", "tsx": "4.21.0", "type-fest": "5.4.3", "typescript": "5.9.3", @@ -96,8 +94,6 @@ "@sentry/core@10.38.0": "patches/@sentry%2Fcore@10.38.0.patch", }, "packages": { - "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@apm-js-collab/code-transformer": ["@apm-js-collab/code-transformer@0.8.2", "", {}, "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA=="], "@apm-js-collab/tracing-hooks": ["@apm-js-collab/tracing-hooks@0.3.1", "", { "dependencies": { "@apm-js-collab/code-transformer": "^0.8.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } }, "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw=="], @@ -254,7 +250,7 @@ "@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.1", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ=="], - "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], @@ -634,6 +630,36 @@ "@supabase/supabase-js": ["@supabase/supabase-js@2.95.2", "", { "dependencies": { "@supabase/auth-js": "2.95.2", "@supabase/functions-js": "2.95.2", "@supabase/postgrest-js": "2.95.2", "@supabase/realtime-js": "2.95.2", "@supabase/storage-js": "2.95.2" } }, "sha512-CkNIPJCdOkokX5E92RJt6yHNyHprJ2V8wRuGK8wCPwbaEmWvW0CvMj+qr4uGozwnOd1ixeAsQUSg6+roA0+muA=="], + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="], + "@tanstack/query-core": ["@tanstack/query-core@5.90.20", "", {}, "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg=="], "@tanstack/query-devtools": ["@tanstack/query-devtools@5.93.0", "", {}, "sha512-+kpsx1NQnOFTZsw6HAFCW3HkKg0+2cepGtAWXjiiSOJJ1CtQpt72EE2nyZb+AjAbLRPoeRmPJ8MtQd8r8gsPdg=="], @@ -666,7 +692,7 @@ "@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="], - "@types/node": ["@types/node@25.2.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="], + "@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="], "@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="], @@ -714,12 +740,10 @@ "ajv": ["ajv@8.6.3", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], - "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], - "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], @@ -736,8 +760,6 @@ "async-mutex": ["async-mutex@0.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA=="], - "autoprefixer": ["autoprefixer@10.4.20", "", { "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], "b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], @@ -764,11 +786,11 @@ "bare-net": ["bare-net@2.2.0", "", { "dependencies": { "bare-events": "^2.2.2", "bare-pipe": "^4.0.0", "bare-stream": "^2.0.0", "bare-tcp": "^2.0.0" } }, "sha512-UF7cAbHsGE+H6uEqWF5IULBow1x58chZz4g3ALgHtv7wZsFcCbRDt0JKWEumf5Oma3QWS1Q6aLi0Rpll8RElMg=="], - "bare-pipe": ["bare-pipe@4.1.2", "", { "dependencies": { "bare-events": "^2.0.0", "bare-stream": "^2.0.0" } }, "sha512-btXtZLlABEDRp50cfLj9iweISqAJSNMCjeq5v0v9tBY2a7zSSqmfa2ZoE1ki2qxAvubagLUqw6VDifpsuI/qmg=="], + "bare-pipe": ["bare-pipe@4.1.3", "", { "dependencies": { "bare-events": "^2.0.0", "bare-stream": "^2.0.0" } }, "sha512-DqQsx93rAzre6yJ9T6l/Vgh+X+bntkVMB1X5ggtXjXtqtMmF2Y2RVlCzxxy/09R6yeR9FSWBEUIaMYJL1/5VDA=="], "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], - "bare-tcp": ["bare-tcp@2.2.2", "", { "dependencies": { "bare-dns": "^2.0.4", "bare-events": "^2.5.4", "bare-stream": "^2.6.4" } }, "sha512-bYnw1AhzGlfLOD4nTceUXkhhgznZKvDuwjX1Au0VWaVitwqG40oaTvvhEQVCcK3FEwjRTiukUzHnAFsYXUI+3Q=="], + "bare-tcp": ["bare-tcp@2.2.4", "", { "dependencies": { "bare-dns": "^2.0.4", "bare-events": "^2.5.4", "bare-stream": "^2.6.4" } }, "sha512-vGjfMkyhS1lxp+Y4+gXRDR1ijZMK2cKx0RBwtYKUuZbD90ARCoM6bAiTa1HBztumCIC+NXb8eXdMFXyuYwlPyw=="], "bare-tls": ["bare-tls@2.1.7", "", { "dependencies": { "bare-net": "^2.0.1", "bare-stream": "^2.6.4" } }, "sha512-h6wcNXQdBeTX7fed9tjPp0/9cA/QfcBTv3ItgjnbUk4rWAU8bEFalZCZnUDdCK/t9zrNfJ+yvcPx4D/1Y6biyA=="], @@ -810,9 +832,7 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001767", "", {}, "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], "case-anything": ["case-anything@2.1.13", "", {}, "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng=="], @@ -844,11 +864,9 @@ "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], - "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="], "content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="], @@ -856,7 +874,7 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], @@ -864,8 +882,6 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], @@ -896,10 +912,6 @@ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], - "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], - - "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], "dprint-node": ["dprint-node@1.0.8", "", { "dependencies": { "detect-libc": "^1.0.3" } }, "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg=="], @@ -920,10 +932,12 @@ "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.5.2", "", { "peerDependencies": { "embla-carousel": "8.5.2" } }, "sha512-QC8/hYSK/pEmqEdU1IO5O+XNc/Ptmmq7uCB44vKplgLKhB/l0+yvYx0+Cv0sF6Ena8Srld5vUErZkT+yTahtDg=="], - "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], @@ -988,8 +1002,6 @@ "forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="], - "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1014,11 +1026,11 @@ "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], - "get-tsconfig": ["get-tsconfig@4.13.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w=="], + "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], "glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1052,7 +1064,7 @@ "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], @@ -1140,9 +1152,9 @@ "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], - "jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], - "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], @@ -1168,9 +1180,29 @@ "light-bolt11-decoder": ["light-bolt11-decoder@3.2.0", "", { "dependencies": { "@scure/base": "1.1.1" } }, "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ=="], - "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], - "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], @@ -1188,7 +1220,7 @@ "lucide-react": ["lucide-react@0.468.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA=="], - "magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -1224,8 +1256,6 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], @@ -1258,16 +1288,10 @@ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], - "npm-normalize-package-bin": ["npm-normalize-package-bin@5.0.0", "", {}, "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag=="], "npm-run-all": ["npm-run-all@4.1.5", "", { "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", "cross-spawn": "^6.0.5", "memorystream": "^0.3.1", "minimatch": "^3.0.4", "pidtree": "^0.3.0", "read-pkg": "^3.0.0", "shell-quote": "^1.6.1", "string.prototype.padend": "^3.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js" } }, "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ=="], - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -1322,8 +1346,6 @@ "pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], - "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], - "pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], "playwright": ["playwright@1.49.1", "", { "dependencies": { "playwright-core": "1.49.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA=="], @@ -1332,19 +1354,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="], - - "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], - - "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], - - "postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], - - "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], - - "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - - "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], @@ -1400,8 +1410,6 @@ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], - "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], - "read-cmd-shim": ["read-cmd-shim@6.0.0", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="], "read-pkg": ["read-pkg@3.0.0", "", { "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", "path-type": "^3.0.0" } }, "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA=="], @@ -1444,7 +1452,7 @@ "secp256k1": ["secp256k1@5.0.1", "", { "dependencies": { "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" } }, "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA=="], - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], @@ -1486,13 +1494,13 @@ "spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="], - "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], - "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1504,14 +1512,12 @@ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], - "supabase": ["supabase@2.75.5", "", { "dependencies": { "bin-links": "^6.0.0", "https-proxy-agent": "^7.0.2", "node-fetch": "^3.3.2", "tar": "7.5.7" }, "bin": { "supabase": "bin/supabase" } }, "sha512-gjCFvMTJ51HDPEGkvLroGgA4GEB5VhU136Gc/9BZgaHogqAKvTs7M28xlPcG5flGzmZjiqlNP1PcgzuQdw9fxQ=="], "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], @@ -1522,18 +1528,16 @@ "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], - "tailwindcss": ["tailwindcss@3.4.16", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw=="], + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + "tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="], "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], - "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], - - "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1544,8 +1548,6 @@ "ts-error": ["ts-error@1.0.6", "", {}, "sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA=="], - "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], - "ts-morph": ["ts-morph@12.0.0", "", { "dependencies": { "@ts-morph/common": "~0.11.0", "code-block-writer": "^10.1.1" } }, "sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA=="], "ts-poet": ["ts-poet@6.12.0", "", { "dependencies": { "dprint-node": "^1.0.8" } }, "sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA=="], @@ -1604,8 +1606,6 @@ "usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], "uuidv7": ["uuidv7@1.1.0", "", { "bin": { "uuidv7": "cli.js" } }, "sha512-2VNnOC0+XQlwogChUDzy6pe8GQEys9QFZBGOh54l6qVfwoCUwwRvk7rDTgaIsRgsF5GFa5oiNg8LqXE3jofBBg=="], @@ -1646,7 +1646,7 @@ "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], - "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -1654,7 +1654,7 @@ "write-file-atomic": ["write-file-atomic@7.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], @@ -1662,8 +1662,6 @@ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -1912,12 +1910,12 @@ "@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="], - "@react-router/dev/isbot": ["isbot@5.1.28", "", {}, "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw=="], - "@react-router/fs-routes/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@sentry/bundler-plugin-core/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "@sentry/bundler-plugin-core/magic-string": ["magic-string@0.30.8", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ=="], + "@sentry/cli/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], "@sentry/cli/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], @@ -1934,19 +1932,21 @@ "@sentry/react-router/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.211.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.211.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q=="], - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], - "body-parser/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], - "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], - "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - "dprint-node/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "dprint-node/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "glob/minimatch": ["minimatch@10.1.2", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.1" } }, "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw=="], @@ -1968,44 +1968,18 @@ "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - "raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - "react-router/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "read-cache/pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], - - "send/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - - "send/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], "unplugin/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "vite-node/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "@opentelemetry/instrumentation-amqplib/@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.211.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg=="], "@opentelemetry/instrumentation-amqplib/@opentelemetry/instrumentation/import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="], @@ -2218,14 +2192,6 @@ "@sentry/react-router/@opentelemetry/instrumentation/require-in-the-middle": ["require-in-the-middle@8.0.1", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="], - "body-parser/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "npm-run-all/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], "npm-run-all/cross-spawn/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], @@ -2234,31 +2200,11 @@ "npm-run-all/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], - "raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - - "unplugin/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "unplugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "vite-node/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - - "vite-node/vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "@opentelemetry/instrumentation-amqplib/@opentelemetry/instrumentation/import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], @@ -2314,76 +2260,32 @@ "@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "@sentry/react-router/@opentelemetry/instrumentation/import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], - "cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "npm-run-all/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], - "tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "unplugin/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "vite-node/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - - "vite-node/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - - "vite-node/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - - "vite-node/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - - "vite-node/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - - "vite-node/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - - "vite-node/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "vite-node/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "vite-node/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "vite-node/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "vite-node/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "vite-node/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "vite-node/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "vite-node/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "vite-node/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "vite-node/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "vite-node/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "vite-node/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "vite-node/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "vite-node/vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "vite-node/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - "vite-node/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - "vite-node/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "vite-node/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "@sentry/bundler-plugin-core/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], } } diff --git a/components.json b/components.json index c00cd99ca..62e02afa4 100644 --- a/components.json +++ b/components.json @@ -4,7 +4,7 @@ "rsc": false, "tsx": true, "tailwind": { - "config": "tailwind.config.ts", + "config": "", "css": "app/tailwind.css", "baseColor": "neutral", "cssVariables": true, diff --git a/package.json b/package.json index 52a9506f8..c4a07c421 100644 --- a/package.json +++ b/package.json @@ -95,14 +95,13 @@ "@types/jwt-encode": "1.0.3", "@types/react": "19.2.13", "@types/react-dom": "19.2.3", - "autoprefixer": "10.4.20", + "@tailwindcss/vite": "4.1.18", "cross-env": "7.0.3", "dotenv": "16.4.7", "jwt-encode": "1.0.1", "npm-run-all": "4.1.5", - "postcss": "8.4.49", "supabase": "2.75.5", - "tailwindcss": "3.4.16", + "tailwindcss": "4.1.18", "tsx": "4.21.0", "type-fest": "5.4.3", "typescript": "5.9.3", diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 2aa7205d4..000000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/tailwind.config.ts b/tailwind.config.ts deleted file mode 100644 index a47f1c1c1..000000000 --- a/tailwind.config.ts +++ /dev/null @@ -1,123 +0,0 @@ -import type { Config } from 'tailwindcss'; -import plugin from 'tailwindcss/plugin'; - -export default { - darkMode: ['class'], - content: ['./app/**/*.{js,jsx,ts,tsx}'], - theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))', - }, - }, - fontFamily: { - numeric: ['Teko', 'sans-serif'], - primary: ['Kode Mono', 'monospace'], - }, - fontSize: { - '2xs': '0.625rem', - }, - animation: { - shake: 'shake 0.2s ease-in-out', - slam: 'slam 0.4s ease-out both', - 'slide-out-up': 'slide-out-up 300ms ease-out forwards', - }, - keyframes: { - shake: { - '0%, 100%': { transform: 'translateX(0)' }, - '25%': { transform: 'translateX(-5px)' }, - '50%': { transform: 'translateX(5px)' }, - '75%': { transform: 'translateX(-5px)' }, - }, - slam: { - '0%': { - transform: 'scale(1)', - letterSpacing: 'normal', - opacity: '1', - }, - '25%': { - transform: 'scale(1.05)', - letterSpacing: '0.05em', - opacity: '0.9', - }, - '50%': { - transform: 'scale(0.98)', - letterSpacing: '-0.02em', - opacity: '0.95', - }, - '75%': { - transform: 'scale(1.02)', - letterSpacing: '0.02em', - opacity: '0.98', - }, - '100%': { - transform: 'scale(1)', - letterSpacing: 'normal', - opacity: '1', - }, - }, - 'slide-out-up': { - '0%': { transform: 'translateY(0%)', opacity: '1' }, - '100%': { transform: 'translateY(-150%)', opacity: '0' }, - }, - }, - }, - }, - plugins: [ - require('tailwindcss-animate'), - plugin(({ addUtilities }) => { - addUtilities({ - '.scrollbar-none': { - '-ms-overflow-style': 'none', - 'scrollbar-width': 'none', - '&::-webkit-scrollbar': { - display: 'none', - }, - }, - }); - }), - ], -} satisfies Config; diff --git a/vite.config.ts b/vite.config.ts index 437118968..a67f982ba 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import { type SentryReactRouterBuildOptions, sentryReactRouter, } from '@sentry/react-router'; +import tailwindcss from '@tailwindcss/vite'; import { defineConfig } from 'vite'; import devtoolsJson from 'vite-plugin-devtools-json'; import tsconfigPaths from 'vite-tsconfig-paths'; @@ -16,6 +17,7 @@ const sentryConfig: SentryReactRouterBuildOptions = { export default defineConfig((config) => ({ plugins: [ + tailwindcss(), reactRouter(), tsconfigPaths(), devtoolsJson(), From d815051d59219c34822ba90033aefbef280eb5c7 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:48:55 +0000 Subject: [PATCH 14/19] Fix missing cursor pointer on buttons in Tailwind v4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tailwind v4 removed the default cursor: pointer from button elements in Preflight. This change adds cursor-pointer class to the button component's base classes to restore the expected hover behavior. Co-authored-by: Josip Bojčić --- app/components/ui/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/ui/button.tsx b/app/components/ui/button.tsx index 9675a1d6d..f5041ec90 100644 --- a/app/components/ui/button.tsx +++ b/app/components/ui/button.tsx @@ -6,7 +6,7 @@ import { LoaderCircle } from 'lucide-react'; import { cn } from '~/lib/utils'; const buttonVariants = cva( - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', { variants: { variant: { From 1b4aa1f37a839473dd1bc9b9912a875fa0d0faca Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:03:05 +0000 Subject: [PATCH 15/19] Fix cursor pointer on all interactive buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add cursor-pointer class to all plain diff --git a/app/features/contacts/add-contact-drawer.tsx b/app/features/contacts/add-contact-drawer.tsx index 82f012f51..4148793f0 100644 --- a/app/features/contacts/add-contact-drawer.tsx +++ b/app/features/contacts/add-contact-drawer.tsx @@ -97,7 +97,7 @@ function SearchResults({ {results.map((searchResult) => ( diff --git a/app/features/gift-cards/gift-card-details.tsx b/app/features/gift-cards/gift-card-details.tsx index ed9f20d7f..4eadb86c5 100644 --- a/app/features/gift-cards/gift-card-details.tsx +++ b/app/features/gift-cards/gift-card-details.tsx @@ -54,7 +54,12 @@ export default function GiftCardDetails({ cardId }: GiftCardDetailsProps) { - @@ -105,7 +110,7 @@ export default function GiftCardDetails({ cardId }: GiftCardDetailsProps) { diff --git a/app/features/send/send-input.tsx b/app/features/send/send-input.tsx index c6015c8b4..b52034189 100644 --- a/app/features/send/send-input.tsx +++ b/app/features/send/send-input.tsx @@ -65,7 +65,7 @@ const ConvertedMoneySwitcher = ({ return ( @@ -337,7 +341,11 @@ function SelectDestinationDrawer({ return ( - diff --git a/app/features/send/share-cashu-token.tsx b/app/features/send/share-cashu-token.tsx index a1c999431..c850df91e 100644 --- a/app/features/send/share-cashu-token.tsx +++ b/app/features/send/share-cashu-token.tsx @@ -56,6 +56,7 @@ export function ShareCashuToken({ token }: Props) { url: shareableLink, }); }} + className="cursor-pointer" > diff --git a/app/features/settings/accounts/account-proofs.tsx b/app/features/settings/accounts/account-proofs.tsx index 3ff110a5b..5e925208e 100644 --- a/app/features/settings/accounts/account-proofs.tsx +++ b/app/features/settings/accounts/account-proofs.tsx @@ -36,7 +36,7 @@ function ProofRow({ > diff --git a/app/features/settings/profile/editable-username.tsx b/app/features/settings/profile/editable-username.tsx index 80827e848..f2abac4a1 100644 --- a/app/features/settings/profile/editable-username.tsx +++ b/app/features/settings/profile/editable-username.tsx @@ -87,7 +87,11 @@ export default function EditableUsername() {
{watch('username')}
-
@@ -113,7 +117,7 @@ export default function EditableUsername() { }} /> - diff --git a/app/features/settings/settings.tsx b/app/features/settings/settings.tsx index fa532ac71..4cad8113f 100644 --- a/app/features/settings/settings.tsx +++ b/app/features/settings/settings.tsx @@ -54,7 +54,7 @@ function LnAddressDisplay({ diff --git a/app/features/settings/ui/settings-nav-button.tsx b/app/features/settings/ui/settings-nav-button.tsx index ce919da61..7be8c57e1 100644 --- a/app/features/settings/ui/settings-nav-button.tsx +++ b/app/features/settings/ui/settings-nav-button.tsx @@ -12,7 +12,7 @@ export function SettingsNavButton({ children, to }: SettingsNavButtonProps) { From f6577beace75fae656d3d8a33999c7c84dc747e2 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:25:12 +0000 Subject: [PATCH 16/19] Fix cursor styling for disabled buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add disabled:cursor-not-allowed to Button component - Add disabled:cursor-not-allowed to account selector button - Ensures disabled buttons show not-allowed cursor instead of pointer Co-authored-by: Josip Bojčić --- app/components/ui/button.tsx | 2 +- app/features/accounts/account-selector.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/ui/button.tsx b/app/components/ui/button.tsx index f5041ec90..5adce5960 100644 --- a/app/components/ui/button.tsx +++ b/app/components/ui/button.tsx @@ -6,7 +6,7 @@ import { LoaderCircle } from 'lucide-react'; import { cn } from '~/lib/utils'; const buttonVariants = cva( - 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + 'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', { variants: { variant: { diff --git a/app/features/accounts/account-selector.tsx b/app/features/accounts/account-selector.tsx index 6e125ade5..48da98f18 100644 --- a/app/features/accounts/account-selector.tsx +++ b/app/features/accounts/account-selector.tsx @@ -98,7 +98,7 @@ export function AccountSelector({ diff --git a/app/features/contacts/add-contact-drawer.tsx b/app/features/contacts/add-contact-drawer.tsx index 4148793f0..82f012f51 100644 --- a/app/features/contacts/add-contact-drawer.tsx +++ b/app/features/contacts/add-contact-drawer.tsx @@ -97,7 +97,7 @@ function SearchResults({ {results.map((searchResult) => ( diff --git a/app/features/gift-cards/gift-card-details.tsx b/app/features/gift-cards/gift-card-details.tsx index 4eadb86c5..ed9f20d7f 100644 --- a/app/features/gift-cards/gift-card-details.tsx +++ b/app/features/gift-cards/gift-card-details.tsx @@ -54,12 +54,7 @@ export default function GiftCardDetails({ cardId }: GiftCardDetailsProps) { - @@ -110,7 +105,7 @@ export default function GiftCardDetails({ cardId }: GiftCardDetailsProps) { diff --git a/app/features/send/send-input.tsx b/app/features/send/send-input.tsx index b52034189..808801d27 100644 --- a/app/features/send/send-input.tsx +++ b/app/features/send/send-input.tsx @@ -65,7 +65,7 @@ const ConvertedMoneySwitcher = ({ return ( @@ -341,11 +334,7 @@ function SelectDestinationDrawer({ return ( - diff --git a/app/features/send/share-cashu-token.tsx b/app/features/send/share-cashu-token.tsx index c850df91e..a1c999431 100644 --- a/app/features/send/share-cashu-token.tsx +++ b/app/features/send/share-cashu-token.tsx @@ -56,7 +56,6 @@ export function ShareCashuToken({ token }: Props) { url: shareableLink, }); }} - className="cursor-pointer" > diff --git a/app/features/settings/accounts/account-proofs.tsx b/app/features/settings/accounts/account-proofs.tsx index 5e925208e..3ff110a5b 100644 --- a/app/features/settings/accounts/account-proofs.tsx +++ b/app/features/settings/accounts/account-proofs.tsx @@ -36,7 +36,7 @@ function ProofRow({ > diff --git a/app/features/settings/profile/editable-username.tsx b/app/features/settings/profile/editable-username.tsx index f2abac4a1..80827e848 100644 --- a/app/features/settings/profile/editable-username.tsx +++ b/app/features/settings/profile/editable-username.tsx @@ -87,11 +87,7 @@ export default function EditableUsername() {
{watch('username')}
- @@ -117,7 +113,7 @@ export default function EditableUsername() { }} /> - diff --git a/app/features/settings/settings.tsx b/app/features/settings/settings.tsx index 4cad8113f..fa532ac71 100644 --- a/app/features/settings/settings.tsx +++ b/app/features/settings/settings.tsx @@ -54,7 +54,7 @@ function LnAddressDisplay({ diff --git a/app/features/settings/ui/settings-nav-button.tsx b/app/features/settings/ui/settings-nav-button.tsx index 7be8c57e1..ce919da61 100644 --- a/app/features/settings/ui/settings-nav-button.tsx +++ b/app/features/settings/ui/settings-nav-button.tsx @@ -12,7 +12,7 @@ export function SettingsNavButton({ children, to }: SettingsNavButtonProps) { diff --git a/app/tailwind.css b/app/tailwind.css index cb91edfe9..eddb4b3e1 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -200,4 +200,9 @@ body { @apply bg-background text-foreground; } + /* Restore v3 cursor behavior for buttons */ + button:not(:disabled), + [role="button"]:not(:disabled) { + cursor: pointer; + } } From 863b9c02fae4c4e6ae48e08aeb99038d93774e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Mon, 16 Feb 2026 14:24:16 +0100 Subject: [PATCH 18/19] Fix bug with mint url validation --- .../settings/accounts/add-mint-form.tsx | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/app/features/settings/accounts/add-mint-form.tsx b/app/features/settings/accounts/add-mint-form.tsx index abcdfaf22..9c321a2a1 100644 --- a/app/features/settings/accounts/add-mint-form.tsx +++ b/app/features/settings/accounts/add-mint-form.tsx @@ -1,3 +1,4 @@ +import type { MintKeyset } from '@cashu/cashu-ts'; import { type QueryClient, useQueryClient } from '@tanstack/react-query'; import { Controller, useForm } from 'react-hook-form'; import { useLocation, useNavigate } from 'react-router'; @@ -19,7 +20,11 @@ import { } from '~/features/shared/cashu'; import { useUser } from '~/features/user/user-hooks'; import { useToast } from '~/hooks/use-toast'; -import { getCashuProtocolUnit, getMintPurpose } from '~/lib/cashu'; +import { + type MintInfo, + getCashuProtocolUnit, + getMintPurpose, +} from '~/lib/cashu'; import type { Currency } from '~/lib/money'; import { LinkWithViewTransition } from '~/lib/transitions'; @@ -34,17 +39,56 @@ const currencies = [ { value: 'USD', label: 'USD' }, ]; +type GetMintInfoAndKeysetsResult = + | { + success: true; + data: { + mintInfo: MintInfo; + keysets: MintKeyset[]; + }; + } + | { + success: false; + error: string; + }; + +const getMintInfoAndKeysets = async ( + mintUrl: string, + queryClient: QueryClient, +): Promise => { + try { + const [mintInfo, allKeysets] = await Promise.all([ + queryClient.fetchQuery(mintInfoQueryOptions(mintUrl)), + queryClient.fetchQuery(allMintKeysetsQueryOptions(mintUrl)), + ]); + return { success: true, data: { mintInfo, keysets: allKeysets.keysets } }; + } catch (error) { + console.debug('Failed to validate mint', { cause: error }); + return { + success: false, + error: + 'Mint not found or temporarily unavailable. Make sure the URL is correct or try again later.', + }; + } +}; + const validateMint = async ( value: string, formValues: FormValues, queryClient: QueryClient, ): Promise => { const unit = getCashuProtocolUnit(formValues.currency); - const [mintInfo, keysets] = await Promise.all([ - queryClient.fetchQuery(mintInfoQueryOptions(value)), - queryClient.fetchQuery(allMintKeysetsQueryOptions(value)), - ]); - return cashuMintValidator(value, unit, mintInfo, keysets.keysets); + const result = await getMintInfoAndKeysets(value, queryClient); + if (!result.success) { + return result.error; + } + + return cashuMintValidator( + value, + unit, + result.data.mintInfo, + result.data.keysets, + ); }; export function AddMintForm() { From 1dd24d62c5d47e35f3ac80b028e2ba4b863460e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josip=20Boj=C4=8Di=C4=87?= Date: Tue, 17 Feb 2026 00:47:13 +0100 Subject: [PATCH 19/19] Bun lock change --- bun.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/bun.lock b/bun.lock index a08aaff20..b5ebcbd48 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "agicash",