feat: add instant zap

This commit is contained in:
reya 2024-01-18 13:56:35 +07:00
parent 240fe8bc7c
commit 0e9418949b
15 changed files with 809 additions and 341 deletions

View File

@ -23,8 +23,12 @@ export function ActivitySingleText({ event }: { event: NDKEvent }) {
<div className="max-w-xl mx-auto py-6"> <div className="max-w-xl mx-auto py-6">
{thread ? ( {thread ? (
<div className="flex flex-col gap-3 mb-1"> <div className="flex flex-col gap-3 mb-1">
<ActivityRootNote eventId={thread.rootEventId} /> {thread.rootEventId ? (
<ActivityRootNote eventId={thread.replyEventId} /> <ActivityRootNote eventId={thread.rootEventId} />
) : null}
{thread.replyEventId ? (
<ActivityRootNote eventId={thread.replyEventId} />
) : null}
</div> </div>
) : null} ) : null}
<div className="mt-3 flex flex-col gap-3"> <div className="mt-3 flex flex-col gap-3">

View File

@ -1,9 +1,11 @@
import { useArk } from "@lume/ark";
import { EyeOffIcon } from "@lume/icons"; import { EyeOffIcon } from "@lume/icons";
import { useStorage } from "@lume/storage"; import { useStorage } from "@lume/storage";
import { nip19 } from "nostr-tools"; import { nip19 } from "nostr-tools";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
export function BackupSettingScreen() { export function BackupSettingScreen() {
const ark = useArk();
const storage = useStorage(); const storage = useStorage();
const [privkey, setPrivkey] = useState(null); const [privkey, setPrivkey] = useState(null);
@ -24,14 +26,10 @@ export function BackupSettingScreen() {
return ( return (
<div className="mx-auto w-full max-w-lg"> <div className="mx-auto w-full max-w-lg">
<div className="mb-2 text-sm font-semibold">Private key</div>
<div> <div>
{!privkey ? ( {privkey ? (
<div className="inline-flex h-24 w-full items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-900"> <div>
You&apos;ve stored private key on Lume <div className="mb-2 text-sm font-semibold">Private key</div>
</div>
) : (
<>
<div className="relative"> <div className="relative">
<input <input
readOnly readOnly
@ -50,12 +48,12 @@ export function BackupSettingScreen() {
<button <button
type="button" type="button"
onClick={() => removePrivkey()} onClick={() => removePrivkey()}
className="mt-2 inline-flex h-9 w-full items-center justify-center gap-2 rounded-lg bg-red-200 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:hover:text-white" className="mt-2 inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-red-200 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:hover:text-white"
> >
Remove private key Remove private key
</button> </button>
</> </div>
)} ) : null}
</div> </div>
</div> </div>
); );

View File

@ -151,7 +151,7 @@ export function GeneralSettingScreen() {
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Update Update
</div> </div>
<div className="text-sm">Automatically download new update</div> <div className="text-sm">Automatically download new update</div>
@ -166,7 +166,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Low Power Low Power
</div> </div>
<div className="text-sm"> <div className="text-sm">
@ -183,7 +183,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Startup Startup
</div> </div>
<div className="text-sm">Launch Lume at Login</div> <div className="text-sm">Launch Lume at Login</div>
@ -198,7 +198,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Media Media
</div> </div>
<div className="text-sm">Automatically load media</div> <div className="text-sm">Automatically load media</div>
@ -213,7 +213,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Hashtag Hashtag
</div> </div>
<div className="text-sm">Show all hashtags in content</div> <div className="text-sm">Show all hashtags in content</div>
@ -228,7 +228,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Notification Notification
</div> </div>
<div className="text-sm">Automatically send notification</div> <div className="text-sm">Automatically send notification</div>
@ -244,7 +244,7 @@ export function GeneralSettingScreen() {
</div> </div>
<div className="flex w-full items-center justify-between"> <div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8"> <div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Translation Translation
</div> </div>
<div className="text-sm">Translate text to your language</div> <div className="text-sm">Translate text to your language</div>
@ -259,7 +259,7 @@ export function GeneralSettingScreen() {
</div> </div>
{settings.translation ? ( {settings.translation ? (
<div className="flex w-full items-center gap-8"> <div className="flex w-full items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
API Key API Key
</div> </div>
<div className="relative w-full"> <div className="relative w-full">
@ -283,7 +283,7 @@ export function GeneralSettingScreen() {
</div> </div>
) : null} ) : null}
<div className="flex w-full items-start gap-8"> <div className="flex w-full items-start gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Appearance Appearance
</div> </div>
<div className="flex flex-1 gap-6"> <div className="flex flex-1 gap-6">

View File

@ -1,16 +1,55 @@
import { webln } from "@getalby/sdk"; import { webln } from "@getalby/sdk";
import { useArk } from "@lume/ark"; import { useArk } from "@lume/ark";
import { CheckCircleIcon } from "@lume/icons";
import { useStorage } from "@lume/storage"; import { useStorage } from "@lume/storage";
import * as Switch from "@radix-ui/react-switch";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { NWCForm } from "./components/walletConnectForm"; import { toast } from "sonner";
export function NWCScreen() { export function NWCScreen() {
const ark = useArk(); const ark = useArk();
const storage = useStorage(); const storage = useStorage();
const [settings, setSettings] = useState({
nwc: false,
instantZap: storage.settings.instantZap,
});
const [walletConnectURL, setWalletConnectURL] = useState<null | string>(null); const [walletConnectURL, setWalletConnectURL] = useState<null | string>(null);
const [balance, setBalance] = useState(0); const [amount, setAmount] = useState("21");
const saveNWC = async () => {
try {
if (!walletConnectURL.startsWith("nostr+walletconnect:")) {
return toast.error(
"Connect URI is required and must start with format nostr+walletconnect:, please check again",
);
}
const uriObj = new URL(walletConnectURL);
const params = new URLSearchParams(uriObj.search);
if (params.has("relay") && params.has("secret")) {
await storage.createPrivkey("Nostr Wallet Connect", walletConnectURL);
storage.nwc = walletConnectURL;
setWalletConnectURL(walletConnectURL);
setSettings((state) => ({ ...state, nwc: true }));
} else {
return toast.error("Connect URI is not valid, please check again");
}
} catch (e) {
toast.error(String(e));
}
};
const toggleInstantZap = async () => {
await storage.createSetting("instantZap", String(+!settings.instantZap));
setSettings((state) => ({ ...state, instantZap: !settings.instantZap }));
};
const saveAmount = async () => {
await storage.createSetting("zapAmount", amount);
};
const remove = async () => { const remove = async () => {
await storage.removePrivkey(`${ark.account.pubkey}-nwc`); await storage.removePrivkey(`${ark.account.pubkey}-nwc`);
@ -24,130 +63,101 @@ export function NWCScreen() {
await nwc.enable(); await nwc.enable();
const balanceResponse = await nwc.getBalance(); const balanceResponse = await nwc.getBalance();
setBalance(balanceResponse.balance);
nwc.close(); nwc.close();
}; };
useEffect(() => { useEffect(() => {
if (walletConnectURL) loadBalance(); if (storage.nwc) {
}, [walletConnectURL]); setSettings((state) => ({ ...state, nwc: true }));
setWalletConnectURL(storage.nwc);
useEffect(() => {
async function getNWC() {
const nwc = await storage.loadPrivkey(`${ark.account.pubkey}-nwc`);
if (nwc) setWalletConnectURL(nwc);
} }
getNWC();
}, []); }, []);
return ( return (
<div> <div className="mx-auto w-full max-w-lg">
<div className="flex w-full flex-col gap-5"> <div className="flex flex-col gap-6">
<div className="text-center"> <div className="flex w-full items-center justify-between">
<h3 className="text-xl font-semibold leading-tight"> <div className="flex w-full items-center gap-8">
Nostr Wallet Connect <div className="w-36 shrink-0 text-end text-sm font-semibold">
</h3> Connection String
<p className="font-medium leading-snug text-neutral-600 dark:text-neutral-500"> </div>
Sending zap easily via Bitcoin Lightning. <div className="relative w-full">
</p> <input
type="password"
spellCheck={false}
value={walletConnectURL}
onChange={(e) => setWalletConnectURL(e.target.value)}
className="w-full border-transparent outline-none focus:outline-none focus:ring-0 focus:border-none h-9 rounded-lg ring-0 placeholder:text-neutral-600 bg-neutral-100 dark:bg-neutral-900"
/>
<div className="h-9 absolute right-0 top-0 inline-flex items-center justify-center">
{!settings.nwc ? (
<button
type="button"
onClick={saveNWC}
className="mr-1 h-7 w-16 text-sm font-medium shrink-0 inline-flex items-center justify-center rounded-md bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
Save
</button>
) : (
<button
type="button"
onClick={remove}
className="mr-1 h-7 w-16 text-sm font-medium shrink-0 inline-flex items-center justify-center rounded-md bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
Remove
</button>
)}
</div>
</div>
</div>
</div> </div>
<div className="mx-auto w-full max-w-lg"> {settings.nwc ? (
{!walletConnectURL ? ( <>
<NWCForm setWalletConnectURL={setWalletConnectURL} /> <div className="flex w-full items-center justify-between">
) : ( <div className="flex items-center gap-8">
<div className="flex w-full flex-col gap-3 rounded-xl bg-neutral-100 p-3 dark:bg-neutral-900"> <div className="w-36 shrink-0 text-end text-sm font-semibold">
<div className="flex items-center justify-center gap-1.5 text-sm text-teal-500"> Instant Zap
<CheckCircleIcon className="h-4 w-4" /> </div>
<div>You&apos;re using nostr wallet connect</div> <div className="text-sm">
Zap with default amount, no confirmation
</div>
</div> </div>
<div className="flex flex-col gap-3"> <Switch.Root
<textarea checked={settings.instantZap}
readOnly onClick={() => toggleInstantZap()}
value={`${walletConnectURL.substring(0, 120)}****`} className="relative h-7 w-12 cursor-default rounded-full bg-neutral-200 outline-none data-[state=checked]:bg-blue-500 dark:bg-neutral-800"
className="h-40 w-full resize-none rounded-lg border-transparent bg-neutral-200 px-3 py-3 text-neutral-900 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:focus:ring-blue-800 dark:bg-neutral-800 dark:text-neutral-100 dark:placeholder:text-neutral-400" >
/> <Switch.Thumb className="block h-6 w-6 translate-x-0.5 rounded-full bg-white transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]" />
<button </Switch.Root>
type="button" </div>
onClick={() => remove()} <div className="flex w-full items-center justify-between">
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-neutral-200 px-6 font-medium text-red-500 hover:bg-red-500 hover:text-white focus:outline-none dark:bg-neutral-800 dark:text-neutral-100" <div className="flex w-full items-center gap-8">
> <div className="w-36 shrink-0 text-end text-sm font-semibold">
Remove connection Default amount
</button> </div>
<div className="relative w-full">
<input
type="number"
spellCheck={false}
value={amount}
onChange={(e) => setAmount(e.target.value)}
className="w-full border-transparent outline-none focus:outline-none focus:ring-0 focus:border-none h-9 rounded-lg ring-0 placeholder:text-neutral-600 bg-neutral-100 dark:bg-neutral-900"
/>
<div className="h-9 absolute right-0 top-0 inline-flex items-center justify-center">
<button
type="button"
onClick={saveAmount}
className="mr-1 h-7 w-16 text-sm font-medium shrink-0 inline-flex items-center justify-center rounded-md bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
Save
</button>
</div>
</div>
</div> </div>
</div> </div>
)} </>
{walletConnectURL ? ( ) : null}
<div className="mt-5 flex flex-col">
<h3 className="font-medium text-neutral-600 dark:text-neutral-400">
Current balance
</h3>
<p className="text-2xl font-semibold">
{new Intl.NumberFormat().format(balance)} sats
</p>
</div>
) : (
<div className="mt-5 flex flex-col gap-4">
<div className="flex flex-col gap-1.5">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
Introduction
</h5>
<p className="text-neutral-600 dark:text-neutral-400">
Nostr Wallet Connect (NWC) is a way for applications like
Nostr clients to access a remote Lightning wallet through a
standardized protocol.
</p>
<p className="text-neutral-600 dark:text-neutral-400">
To learn more about the details have a look at{" "}
<a
href="https://github.com/nostr-protocol/nips/blob/master/47.md"
target="_blank"
className="text-blue-500"
rel="noreferrer"
>
the specs (NIP47)
</a>
</p>
</div>
<div className="flex flex-col gap-1.5">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
About zapping
</h5>
<p className="text-neutral-600 dark:text-neutral-400">
Lume doesn&apos;t take any commission or platform fees when
you zap someone. Lume doesn&apos;t hold your Bitcoin
</p>
</div>
<div className="flex flex-col gap-1.5">
<h5 className="font-semibold text-neutral-900 dark:text-neutral-100">
Recommend wallet that support NWC
</h5>
<p className="text-neutral-600 dark:text-neutral-400">
- Mutiny Wallet:{" "}
<a
href="https://www.mutinywallet.com/"
target="_blank"
rel="noreferrer"
className="text-blue-500"
>
website
</a>
</p>
<p className="text-neutral-600 dark:text-neutral-400">
- Self hosted NWC on Umbrel :{" "}
<a
href="https://apps.umbrel.com/app/alby-nostr-wallet-connect"
target="_blank"
rel="noreferrer"
className="text-blue-500"
>
website
</a>
</p>
</div>
</div>
)}
</div>
</div> </div>
</div> </div>
); );

View File

@ -305,9 +305,7 @@ export class Ark {
public async getThreads(id: string) { public async getThreads(id: string) {
const eventId = this.getCleanEventId(id); const eventId = this.getCleanEventId(id);
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk)); const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk));
const relayUrls = [...this.ndk.pool.relays.values()].map( const relayUrls = Array.from(this.ndk.pool.relays.keys());
(item) => item.url,
);
try { try {
const rawEvents = (await fetcher.fetchAllEvents( const rawEvents = (await fetcher.fetchAllEvents(
@ -357,9 +355,7 @@ export class Ark {
public async getAllRelaysFromContacts({ signal }: { signal: AbortSignal }) { public async getAllRelaysFromContacts({ signal }: { signal: AbortSignal }) {
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk)); const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk));
const connectedRelays = this.ndk.pool const connectedRelays = Array.from(this.ndk.pool.relays.keys());
.connectedRelays()
.map((item) => item.url);
try { try {
const relayMap = new Map<string, string[]>(); const relayMap = new Map<string, string[]>();
@ -373,8 +369,6 @@ export class Ark {
{ abortSignal: signal }, { abortSignal: signal },
); );
console.log(relayEvents);
for await (const { author, events } of relayEvents) { for await (const { author, events } of relayEvents) {
if (events.length) { if (events.length) {
const relayTags = events[0].tags.filter((item) => item[0] === "r"); const relayTags = events[0].tags.filter((item) => item[0] === "r");
@ -411,9 +405,7 @@ export class Ark {
dedup?: boolean; dedup?: boolean;
}) { }) {
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk)); const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk));
const relayUrls = [...this.ndk.pool.relays.values()].map( const relayUrls = Array.from(this.ndk.pool.relays.keys());
(item) => item.url,
);
const seenIds = new Set<string>(); const seenIds = new Set<string>();
const dedupQueue = new Set<string>(); const dedupQueue = new Set<string>();

View File

@ -1,16 +1,11 @@
import { webln } from "@getalby/sdk"; import { webln } from "@getalby/sdk";
import { SendPaymentResponse } from "@getalby/sdk/dist/types"; import { type SendPaymentResponse } from "@getalby/sdk/dist/types";
import { CancelIcon, ZapIcon } from "@lume/icons"; import { CancelIcon, LoaderIcon, ZapIcon } from "@lume/icons";
import { import { useStorage } from "@lume/storage";
compactNumber, import { cn, compactNumber, displayNpub } from "@lume/utils";
displayNpub,
sendNativeNotification,
} from "@lume/utils";
import * as Dialog from "@radix-ui/react-dialog"; import * as Dialog from "@radix-ui/react-dialog";
import * as Tooltip from "@radix-ui/react-tooltip"; import * as Tooltip from "@radix-ui/react-tooltip";
import { invoke } from "@tauri-apps/api/core"; import { useState } from "react";
import { QRCodeSVG } from "qrcode.react";
import { useEffect, useState } from "react";
import CurrencyInput from "react-currency-input-field"; import CurrencyInput from "react-currency-input-field";
import { toast } from "sonner"; import { toast } from "sonner";
import { useArk } from "../../../hooks/useArk"; import { useArk } from "../../../hooks/useArk";
@ -18,83 +13,98 @@ import { useProfile } from "../../../hooks/useProfile";
import { useNoteContext } from "../provider"; import { useNoteContext } from "../provider";
export function NoteZap() { export function NoteZap() {
const [walletConnectURL, setWalletConnectURL] = useState<string>(null); const ark = useArk();
const storage = useStorage();
const event = useNoteContext();
const [amount, setAmount] = useState<string>("21"); const [amount, setAmount] = useState<string>("21");
const [zapMessage, setZapMessage] = useState<string>(""); const [zapMessage, setZapMessage] = useState<string>("");
const [invoice, setInvoice] = useState<null | string>(null);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isCompleted, setIsCompleted] = useState(false); const [isCompleted, setIsCompleted] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const ark = useArk();
const event = useNoteContext();
const { user } = useProfile(event.pubkey); const { user } = useProfile(event.pubkey);
const createZapRequest = async () => { const createZapRequest = async (instant?: boolean) => {
if (!storage.nwc) return;
let nwc: webln.NostrWebLNProvider = undefined; let nwc: webln.NostrWebLNProvider = undefined;
try { try {
// start loading
setIsLoading(true);
const zapAmount = parseInt(amount) * 1000; const zapAmount = parseInt(amount) * 1000;
const res = await event.zap(zapAmount, zapMessage); const res = await event.zap(zapAmount, zapMessage);
if (!res) return toast.error("Cannot create zap request");
// user don't connect nwc, create QR Code for invoice
if (!walletConnectURL) return setInvoice(res);
// user connect nwc // user connect nwc
nwc = new webln.NostrWebLNProvider({ nwc = new webln.NostrWebLNProvider({
nostrWalletConnectUrl: walletConnectURL, nostrWalletConnectUrl: storage.nwc,
}); });
await nwc.enable(); await nwc.enable();
// start loading
setIsLoading(true);
// send payment via nwc // send payment via nwc
const send: SendPaymentResponse = await nwc.sendPayment(res); const send: SendPaymentResponse = await nwc.sendPayment(res);
if (send) { if (send) {
await sendNativeNotification( toast.success(
`You've zapped ${compactNumber.format(send.amount)} sats to ${ `You've zapped ${compactNumber.format(send.amount)} sats to ${
user?.name || user?.displayName || "anon" user?.name || user?.displayName || "anon"
}`, }`,
); );
// eose
nwc.close();
setIsCompleted(true);
setIsLoading(false);
// reset after 1.5 secs // reset after 1.5 secs
const timeout = setTimeout(() => setIsCompleted(false), 1500); if (!instant) {
clearTimeout(timeout); const timeout = setTimeout(() => setIsCompleted(false), 1500);
clearTimeout(timeout);
}
} }
} catch (e) {
// eose
nwc.close(); nwc.close();
// update state
setIsCompleted(true);
setIsLoading(false);
} catch (e) {
nwc?.close();
setIsLoading(false); setIsLoading(false);
toast.error(String(e)); toast.error(String(e));
} }
}; };
useEffect(() => { if (storage.settings.instantZap) {
async function getWalletConnectURL() { return (
const uri: string = await invoke("secure_load", { <Tooltip.Provider>
key: `${ark.account.pubkey}-nwc`, <Tooltip.Root delayDuration={150}>
}); <Tooltip.Trigger asChild>
if (uri) setWalletConnectURL(uri); <button
} type="button"
onClick={() => createZapRequest(true)}
if (isOpen) getWalletConnectURL(); className="inline-flex items-center justify-center group size-7 text-neutral-600 dark:text-neutral-400"
>
return () => { {isLoading ? (
setAmount("21"); <LoaderIcon className="size-4 animate-spin" />
setZapMessage(""); ) : (
setIsCompleted(false); <ZapIcon
setIsLoading(false); className={cn(
}; "size-5 group-hover:text-blue-500",
}, [isOpen]); isCompleted ? "text-blue-500" : "",
)}
/>
)}
</button>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Zap
<Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
}
return ( return (
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}> <Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
@ -135,129 +145,99 @@ export function NoteZap() {
</Dialog.Close> </Dialog.Close>
</div> </div>
<div className="px-5 pb-5 overflow-x-hidden overflow-y-auto"> <div className="px-5 pb-5 overflow-x-hidden overflow-y-auto">
{!invoice ? ( <div className="relative flex flex-col h-40">
<> <div className="inline-flex items-center justify-center flex-1 h-full gap-1">
<div className="relative flex flex-col h-40"> <CurrencyInput
<div className="inline-flex items-center justify-center flex-1 h-full gap-1"> placeholder="0"
<CurrencyInput defaultValue={"21"}
placeholder="0" value={amount}
defaultValue={"21"} decimalsLimit={2}
value={amount} min={0} // 0 sats
decimalsLimit={2} max={10000} // 1M sats
min={0} // 0 sats maxLength={10000} // 1M sats
max={10000} // 1M sats onValueChange={(value) => setAmount(value)}
maxLength={10000} // 1M sats className="flex-1 w-full text-4xl font-semibold text-right bg-transparent border-none placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
onValueChange={(value) => setAmount(value)} />
className="flex-1 w-full text-4xl font-semibold text-right bg-transparent border-none placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400" <span className="flex-1 w-full text-4xl font-semibold text-left text-neutral-500 dark:text-neutral-400">
/> sats
<span className="flex-1 w-full text-4xl font-semibold text-left text-neutral-500 dark:text-neutral-400"> </span>
sats
</span>
</div>
<div className="inline-flex items-center justify-center gap-2">
<button
type="button"
onClick={() => setAmount("69")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
69 sats
</button>
<button
type="button"
onClick={() => setAmount("100")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
100 sats
</button>
<button
type="button"
onClick={() => setAmount("200")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
200 sats
</button>
<button
type="button"
onClick={() => setAmount("500")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
500 sats
</button>
<button
type="button"
onClick={() => setAmount("1000")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
1K sats
</button>
</div>
</div>
<div className="flex flex-col w-full gap-2 mt-4">
<input
name="zapMessage"
value={zapMessage}
onChange={(e) => setZapMessage(e.target.value)}
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
placeholder="Enter message (optional)"
className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:text-neutral-400"
/>
<div className="flex flex-col gap-2">
{walletConnectURL ? (
<button
type="button"
onClick={() => createZapRequest()}
className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
>
{isCompleted ? (
<p className="leading-tight">Successfully zapped</p>
) : isLoading ? (
<span className="flex flex-col">
<p className="leading-tight">
Waiting for approval
</p>
<p className="text-xs leading-tight text-neutral-100">
Go to your wallet and approve payment request
</p>
</span>
) : (
<span className="flex flex-col">
<p className="leading-tight">Send zap</p>
<p className="text-xs leading-tight text-neutral-100">
You&apos;re using nostr wallet connect
</p>
</span>
)}
</button>
) : (
<button
type="button"
onClick={() => createZapRequest()}
className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
>
Create Lightning invoice
</button>
)}
</div>
</div>
</>
) : (
<div className="flex flex-col items-center justify-center gap-4 mt-3">
<div className="p-3 rounded-md bg-neutral-100 dark:bg-neutral-900">
<QRCodeSVG value={invoice} size={256} />
</div>
<div className="flex flex-col items-center gap-1">
<h3 className="text-lg font-medium">Scan to zap</h3>
<span className="text-sm text-center text-neutral-600 dark:text-neutral-400">
You must use Bitcoin wallet which support Lightning
<br />
such as: Blue Wallet, Bitkit, Phoenix,...
</span>
</div>
</div> </div>
)} <div className="inline-flex items-center justify-center gap-2">
<button
type="button"
onClick={() => setAmount("69")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
69 sats
</button>
<button
type="button"
onClick={() => setAmount("100")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
100 sats
</button>
<button
type="button"
onClick={() => setAmount("200")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
200 sats
</button>
<button
type="button"
onClick={() => setAmount("500")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
500 sats
</button>
<button
type="button"
onClick={() => setAmount("1000")}
className="w-max rounded-full bg-neutral-100 px-2.5 py-1 text-sm font-medium hover:bg-neutral-200 dark:bg-neutral-900 dark:hover:bg-neutral-800"
>
1K sats
</button>
</div>
</div>
<div className="flex flex-col w-full gap-2 mt-4">
<input
name="zapMessage"
value={zapMessage}
onChange={(e) => setZapMessage(e.target.value)}
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
placeholder="Enter message (optional)"
className="w-full resize-none rounded-lg border-transparent bg-neutral-100 px-3 py-3 !outline-none placeholder:text-neutral-600 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:bg-neutral-900 dark:text-neutral-400"
/>
<div className="flex flex-col gap-2">
<button
type="button"
onClick={() => createZapRequest()}
className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
>
{isCompleted ? (
<p className="leading-tight">Successfully zapped</p>
) : isLoading ? (
<span className="flex flex-col">
<p className="leading-tight">Waiting for approval</p>
<p className="text-xs leading-tight text-neutral-100">
Go to your wallet and approve payment request
</p>
</span>
) : (
<span className="flex flex-col">
<p className="leading-tight">Send zap</p>
<p className="text-xs leading-tight text-neutral-100">
You&apos;re using nostr wallet connect
</p>
</span>
)}
</button>
</div>
</div>
</div> </div>
</div> </div>
</Dialog.Content> </Dialog.Content>

View File

@ -38,7 +38,7 @@ export function NoteContent({
const [content, setContent] = useState(event.content); const [content, setContent] = useState(event.content);
const [translate, setTranslate] = useState({ const [translate, setTranslate] = useState({
translatable: true, translatable: false,
translated: false, translated: false,
}); });

View File

@ -42,7 +42,7 @@ export function LinkPreview({ url }: { url: string }) {
to={url} to={url}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="flex flex-col w-full my-1 overflow-hidden rounded-lg bg-neutral-100 dark:bg-neutral-900" className="flex flex-col w-full my-1 overflow-hidden rounded-lg bg-neutral-100 dark:bg-neutral-900 border border-black/5 dark:border-white/5"
> >
{isImage(data.image) ? ( {isImage(data.image) ? (
<img <img

View File

@ -12,7 +12,13 @@ export function VideoPreview({ url }: { url: string }) {
return ( return (
<div className="my-1 w-full rounded-lg overflow-hidden"> <div className="my-1 w-full rounded-lg overflow-hidden">
<MediaController> <MediaController>
<video slot="media" src={url} preload="auto" muted /> <video
slot="media"
src={url}
preload="auto"
muted
className="w-full h-auto"
/>
<MediaControlBar> <MediaControlBar>
<MediaPlayButton /> <MediaPlayButton />
<MediaTimeRange /> <MediaTimeRange />

View File

@ -6,13 +6,13 @@ export function ChildReply({
}: { event: NDKEvent; rootEventId?: string }) { }: { event: NDKEvent; rootEventId?: string }) {
return ( return (
<Note.Provider event={event}> <Note.Provider event={event}>
<Note.Root className="pl-6"> <Note.Root className="py-2">
<div className="flex items-center justify-between h-14"> <div className="flex items-center justify-between h-14">
<Note.User className="flex-1" /> <Note.User className="flex-1" />
<Note.Menu /> <Note.Menu />
</div> </div>
<Note.Content /> <Note.Content />
<div className="flex items-center justify-end gap-10 mt-4"> <div className="flex items-center justify-end gap-4 mt-2">
<Note.Repost /> <Note.Repost />
<Note.Zap /> <Note.Zap />
</div> </div>

View File

@ -37,14 +37,20 @@ export function Reply({
) : ( ) : (
<div /> <div />
)} )}
<div className="inline-flex items-center gap-10"> <div className="inline-flex items-center gap-4">
<Note.Repost /> <Note.Repost />
<Note.Zap /> <Note.Zap />
</div> </div>
</div> </div>
<div className={cn("", open ? "pb-3" : "")}> <div
className={cn(
open
? "pb-3 border-t border-neutral-100 dark:border-neutral-900"
: "",
)}
>
{event.replies?.length > 0 ? ( {event.replies?.length > 0 ? (
<Collapsible.Content> <Collapsible.Content className="divide-y divide-neutral-100 dark:divide-neutral-900 pl-6">
{event.replies?.map((childEvent) => ( {event.replies?.map((childEvent) => (
<ChildReply key={childEvent.id} event={childEvent} /> <ChildReply key={childEvent.id} event={childEvent} />
))} ))}

View File

@ -32,7 +32,7 @@ export function ThreadNote({ eventId }: { eventId: string }) {
<Note.Content className="min-w-0 px-3" /> <Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 h-14"> <div className="flex items-center justify-between px-3 h-14">
<Note.Pin /> <Note.Pin />
<div className="inline-flex items-center gap-10"> <div className="inline-flex items-center gap-4">
<Note.Repost /> <Note.Repost />
<Note.Zap /> <Note.Zap />
</div> </div>

View File

@ -19,6 +19,7 @@ export class LumeStorage {
readonly platform: Platform; readonly platform: Platform;
readonly locale: string; readonly locale: string;
public currentUser: Account; public currentUser: Account;
public nwc: string;
public settings: { public settings: {
autoupdate: boolean; autoupdate: boolean;
nsecbunker: boolean; nsecbunker: boolean;
@ -29,12 +30,14 @@ export class LumeStorage {
lowPower: boolean; lowPower: boolean;
translation: boolean; translation: boolean;
translateApiKey: string; translateApiKey: string;
instantZap: boolean;
}; };
constructor(db: Database, platform: Platform, locale: string) { constructor(db: Database, platform: Platform, locale: string) {
this.#db = db; this.#db = db;
this.locale = locale; this.locale = locale;
this.platform = platform; this.platform = platform;
this.nwc = null;
this.settings = { this.settings = {
autoupdate: false, autoupdate: false,
nsecbunker: false, nsecbunker: false,
@ -45,6 +48,7 @@ export class LumeStorage {
lowPower: false, lowPower: false,
translation: false, translation: false,
translateApiKey: "", translateApiKey: "",
instantZap: false,
}; };
} }
@ -61,6 +65,8 @@ export class LumeStorage {
const account = await this.getActiveAccount(); const account = await this.getActiveAccount();
if (account) this.currentUser = account; if (account) this.currentUser = account;
this.nwc = await this.loadPrivkey("Nostr Wallet Connect");
} }
async #keyring_save(key: string, value: string) { async #keyring_save(key: string, value: string) {

View File

@ -1,10 +1,10 @@
import { import {
AdvancedSettingsIcon, AdvancedSettingsIcon,
InfoIcon, InfoIcon,
NwcIcon,
SecureIcon, SecureIcon,
SettingsIcon, SettingsIcon,
UserIcon, UserIcon,
ZapIcon,
} from "@lume/icons"; } from "@lume/icons";
import { cn } from "@lume/utils"; import { cn } from "@lume/utils";
import { NavLink, Outlet } from "react-router-dom"; import { NavLink, Outlet } from "react-router-dom";
@ -13,7 +13,7 @@ export function SettingsLayout() {
return ( return (
<div className="flex h-full min-h-0 w-full flex-col rounded-xl overflow-y-auto"> <div className="flex h-full min-h-0 w-full flex-col rounded-xl overflow-y-auto">
<div className="flex h-24 shrink-0 w-full items-center justify-center px-2 bg-white/50 backdrop-blur-xl dark:bg-black/50"> <div className="flex h-24 shrink-0 w-full items-center justify-center px-2 bg-white/50 backdrop-blur-xl dark:bg-black/50">
<div className="flex items-center gap-0.5"> <div className="flex items-center gap-2">
<NavLink <NavLink
end end
to="/settings/" to="/settings/"
@ -55,8 +55,8 @@ export function SettingsLayout() {
) )
} }
> >
<NwcIcon className="size-6" /> <ZapIcon className="size-6" />
<p className="text-sm font-medium">Wallet</p> <p className="text-sm font-medium">Zap</p>
</NavLink> </NavLink>
<NavLink <NavLink
to="/settings/backup" to="/settings/backup"

View File

@ -258,25 +258,25 @@ importers:
version: 0.15.0(@nostr-dev-kit/ndk@2.3.3)(nostr-fetch@0.15.0) version: 0.15.0(@nostr-dev-kit/ndk@2.3.3)(nostr-fetch@0.15.0)
'@radix-ui/react-avatar': '@radix-ui/react-avatar':
specifier: ^1.0.4 specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collapsible': '@radix-ui/react-collapsible':
specifier: ^1.0.3 specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dialog': '@radix-ui/react-dialog':
specifier: ^1.0.5 specifier: ^1.0.5
version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu': '@radix-ui/react-dropdown-menu':
specifier: ^2.0.6 specifier: ^2.0.6
version: 2.0.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 2.0.6(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-hover-card': '@radix-ui/react-hover-card':
specifier: ^1.0.7 specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popover': '@radix-ui/react-popover':
specifier: ^1.0.7 specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tooltip': '@radix-ui/react-tooltip':
specifier: ^1.0.7 specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) version: 1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query': '@tanstack/react-query':
specifier: ^5.17.15 specifier: ^5.17.15
version: 5.17.15(react@18.2.0) version: 5.17.15(react@18.2.0)
@ -1884,6 +1884,26 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-arrow@1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
peerDependencies: peerDependencies:
@ -1908,6 +1928,29 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-avatar@1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies: peerDependencies:
@ -1936,6 +1979,33 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-collapsible@1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies: peerDependencies:
@ -1960,6 +2030,29 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-collection@1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.48)(react@18.2.0): /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies: peerDependencies:
@ -2022,6 +2115,39 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false dev: false
/@radix-ui/react-dialog@1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false
/@radix-ui/react-direction@1.0.1(@types/react@18.2.48)(react@18.2.0): /@radix-ui/react-direction@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
peerDependencies: peerDependencies:
@ -2061,6 +2187,30 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-dismissable-layer@1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies: peerDependencies:
@ -2088,6 +2238,32 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-dropdown-menu@2.0.6(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-menu': 2.0.6(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.48)(react@18.2.0): /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
peerDependencies: peerDependencies:
@ -2125,6 +2301,28 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-focus-scope@1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-hover-card@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==} resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
peerDependencies: peerDependencies:
@ -2154,6 +2352,34 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-hover-card@1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-id@1.0.1(@types/react@18.2.48)(react@18.2.0): /@radix-ui/react-id@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
peerDependencies: peerDependencies:
@ -2207,6 +2433,43 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false dev: false
/@radix-ui/react-menu@2.0.6(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
peerDependencies: peerDependencies:
@ -2242,6 +2505,40 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0) react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false dev: false
/@radix-ui/react-popover@1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
peerDependencies: peerDependencies:
@ -2272,6 +2569,35 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-popper@1.1.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@floating-ui/react-dom': 2.0.6(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
peerDependencies: peerDependencies:
@ -2293,6 +2619,26 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-portal@1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies: peerDependencies:
@ -2315,6 +2661,27 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-presence@1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies: peerDependencies:
@ -2336,6 +2703,26 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-primitive@1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies: peerDependencies:
@ -2365,6 +2752,34 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-select@2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): /@radix-ui/react-select@2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==} resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==}
peerDependencies: peerDependencies:
@ -2480,6 +2895,37 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-tooltip@1.0.7(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.48)(react@18.2.0): /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies: peerDependencies:
@ -2603,6 +3049,26 @@ packages:
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
dev: false dev: false
/@radix-ui/react-visually-hidden@1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.48
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/rect@1.0.1: /@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies: dependencies:
@ -2741,14 +3207,14 @@ packages:
dependencies: dependencies:
'@noble/curves': 1.1.0 '@noble/curves': 1.1.0
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.1
'@scure/base': 1.1.1 '@scure/base': 1.1.5
dev: false dev: false
/@scure/bip39@1.2.1: /@scure/bip39@1.2.1:
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
dependencies: dependencies:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.1
'@scure/base': 1.1.1 '@scure/base': 1.1.5
dev: false dev: false
/@swc/core-darwin-arm64@1.3.103: /@swc/core-darwin-arm64@1.3.103: