feat: polish

This commit is contained in:
reya 2024-01-14 09:39:56 +07:00
parent ab27bd5f44
commit f908c46a19
34 changed files with 671 additions and 421 deletions

View File

@ -1,5 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@ -50,7 +50,6 @@ export function ActivityList() {
};
}
},
staleTime: 360 * 1000,
refetchOnWindowFocus: false,
refetchOnMount: false,

View File

@ -145,10 +145,11 @@ export function CreateAccountScreen() {
// add account to storage
await storage.createSetting("nsecbunker", "1");
await storage.createAccount({
const dbAccount = await storage.createAccount({
pubkey: account,
privkey: localSigner.privateKey,
});
ark.account = dbAccount;
// get final signer with newly created account
const finalSigner = new NDKNip46Signer(bunker, account, localSigner);
@ -156,7 +157,6 @@ export function CreateAccountScreen() {
// update main ndk instance signer
ark.updateNostrSigner({ signer: finalSigner });
console.log(ark.ndk.signer);
// remove default nsecbunker profile and contact list
await ark.createEvent({ kind: NDKKind.Metadata, content: "", tags: [] });

View File

@ -1,3 +1,4 @@
import { useArk } from "@lume/ark";
import { EyeOffIcon, EyeOnIcon, LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { getPublicKey, nip19 } from "nostr-tools";
@ -7,6 +8,7 @@ import { useNavigate } from "react-router-dom";
import { toast } from "sonner";
export function LoginWithKey() {
const ark = useArk();
const storage = useStorage();
const navigate = useNavigate();
@ -30,10 +32,11 @@ export function LoginWithKey() {
const privkey = nip19.decode(data.nsec).data as string;
const pubkey = getPublicKey(privkey);
await storage.createAccount({
const account = await storage.createAccount({
pubkey: pubkey,
privkey: privkey,
});
ark.account = account;
return navigate("/auth/onboarding", { replace: true });
} catch (e) {

View File

@ -48,10 +48,11 @@ export function LoginWithNsecbunker() {
ark.updateNostrSigner({ signer: remoteSigner });
await storage.createSetting("nsecbunker", "1");
await storage.createAccount({
pubkey,
const account = await storage.createAccount({
pubkey: pubkey,
privkey: localSigner.privateKey,
});
ark.account = account;
return navigate("/auth/onboarding", { replace: true });
} catch (e) {

View File

@ -109,10 +109,11 @@ export function LoginWithOAuth() {
ark.updateNostrSigner({ signer: remoteSigner });
await storage.createSetting("nsecbunker", "1");
await storage.createAccount({
const account = await storage.createAccount({
pubkey,
privkey: localSigner.privateKey,
});
ark.account = account;
return navigate("/auth/onboarding", { replace: true });
}

View File

@ -29,7 +29,7 @@ export function OnboardingScreen() {
const toggleLowPower = async () => {
await storage.createSetting("lowPower", String(+!settings.lowPower));
setSettings((state) => ({ ...state, autoupdate: !settings.lowPower }));
setSettings((state) => ({ ...state, lowPower: !settings.lowPower }));
};
const toggleTranslation = async () => {
@ -58,7 +58,7 @@ export function OnboardingScreen() {
setLoading(true);
// get account contacts
await ark.getUserContacts(ark.account.pubkey);
await ark.getUserContacts();
// refetch newsfeed
await queryClient.prefetchInfiniteQuery({
@ -154,7 +154,7 @@ export function OnboardingScreen() {
<h3 className="font-semibold text-lg">Low Power Mode</h3>
<p className="text-neutral-500">
Limited relay connection and hide all media, sustainable for low
network environment
network environment.
</p>
</div>
</div>
@ -171,7 +171,8 @@ export function OnboardingScreen() {
Translation (nostr.wine)
</h3>
<p className="text-neutral-500">
Translate text to your preferred language, powered by Nostr Wine
Translate text to your preferred language, powered by Nostr
Wine.
</p>
</div>
</div>

View File

@ -87,7 +87,7 @@ export function DepotMembers() {
</div>
</div>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-black/20" />
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-black/10" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-xl overflow-hidden rounded-xl bg-white dark:bg-black">
<div className="inline-flex h-14 w-full shrink-0 items-center justify-between border-b border-neutral-100 px-5 dark:border-neutral-900">

View File

@ -13,14 +13,14 @@ export function AdvancedSettingScreen() {
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
Caches
Cache
</div>
<div className="text-sm">Use for boost up NDK</div>
<div className="text-sm">Use for boost up nostr connection</div>
</div>
<button
type="button"
onClick={() => clearCache()}
className="h-8 w-max rounded-lg bg-blue-500 px-3 text-sm font-medium text-white hover:bg-blue-600"
className="h-8 w-max rounded-lg px-3 text-sm font-semibold text-blue-500 bg-blue-100 hover:bg-blue-200"
>
Clear
</button>

View File

@ -1,12 +1,12 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { fetch } from "@tauri-apps/plugin-http";
import { Link } from "react-router-dom";
export function PostCard() {
const storage = useStorage();
const ark = useArk();
const { status, data } = useQuery({
queryKey: ["user-stats", ark.account.pubkey],
queryFn: async ({ signal }: { signal: AbortSignal }) => {

View File

@ -1,6 +1,5 @@
import { useProfile } from "@lume/ark";
import { useArk, useProfile } from "@lume/ark";
import { EditIcon, LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { displayNpub } from "@lume/utils";
import * as Avatar from "@radix-ui/react-avatar";
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
@ -9,7 +8,7 @@ import { nip19 } from "nostr-tools";
import { Link } from "react-router-dom";
export function ProfileCard() {
const storage = useStorage();
const ark = useArk();
const svgURI = `data:image/svg+xml;utf8,${encodeURIComponent(
minidenticon(ark.account.pubkey, 90, 50),
)}`;

View File

@ -1,13 +1,11 @@
import { useArk } from "@lume/ark";
import { EditIcon, LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { Link } from "react-router-dom";
export function RelayCard() {
const ark = useArk();
const storage = useStorage();
const { status, data } = useQuery({
queryKey: ["relays", ark.account.pubkey],

View File

@ -1,11 +1,11 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { compactNumber } from "@lume/utils";
import { useQuery } from "@tanstack/react-query";
import { fetch } from "@tauri-apps/plugin-http";
export function ZapCard() {
const storage = useStorage();
const ark = useArk();
const { status, data } = useQuery({
queryKey: ["user-stats", ark.account.pubkey],
queryFn: async ({ signal }: { signal: AbortSignal }) => {

View File

@ -15,6 +15,7 @@ export function GeneralSettingScreen() {
const storage = useStorage();
const [settings, setSettings] = useState({
lowPower: false,
autoupdate: false,
autolaunch: false,
outbox: false,
@ -30,6 +31,11 @@ export function GeneralSettingScreen() {
setSettings((prev) => ({ ...prev, appearance: theme }));
};
const toggleLowPower = async () => {
await storage.createSetting("lowPower", String(+!settings.lowPower));
setSettings((state) => ({ ...state, lowPower: !settings.lowPower }));
};
const toggleAutolaunch = async () => {
if (!settings.autolaunch) {
await enable();
@ -42,12 +48,6 @@ export function GeneralSettingScreen() {
}
};
const toggleOutbox = async () => {
await storage.createSetting("outbox", String(+!settings.outbox));
// update state
setSettings((prev) => ({ ...prev, outbox: !settings.outbox }));
};
const toggleMedia = async () => {
await storage.createSetting("media", String(+!settings.media));
storage.settings.media = !settings.media;
@ -98,6 +98,12 @@ export function GeneralSettingScreen() {
autoupdate: !!parseInt(item.value),
}));
if (item.key === "lowPower")
setSettings((prev) => ({
...prev,
lowPower: !!parseInt(item.value),
}));
if (item.key === "outbox")
setSettings((prev) => ({
...prev,
@ -127,9 +133,9 @@ export function GeneralSettingScreen() {
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
Updater
Update
</div>
<div className="text-sm">Auto download new update at Login</div>
<div className="text-sm">Automatically download new update</div>
</div>
<Switch.Root
checked={settings.autoupdate}
@ -139,6 +145,23 @@ export function GeneralSettingScreen() {
<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]" />
</Switch.Root>
</div>
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
Low Power
</div>
<div className="text-sm">
Sustainable for low network environment.
</div>
</div>
<Switch.Root
checked={settings.lowPower}
onClick={() => toggleLowPower()}
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"
>
<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]" />
</Switch.Root>
</div>
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
@ -154,21 +177,6 @@ export function GeneralSettingScreen() {
<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]" />
</Switch.Root>
</div>
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
Gossip
</div>
<div className="text-sm">Use Outbox model</div>
</div>
<Switch.Root
checked={settings.outbox}
onClick={() => toggleOutbox()}
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"
>
<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]" />
</Switch.Root>
</div>
<div className="flex w-full items-center justify-between">
<div className="flex items-center gap-8">
<div className="w-24 shrink-0 text-end text-sm font-semibold">
@ -189,7 +197,7 @@ export function GeneralSettingScreen() {
<div className="w-24 shrink-0 text-end text-sm font-semibold">
Hashtag
</div>
<div className="text-sm">Hide all hashtags in content</div>
<div className="text-sm">Show all hashtags in content</div>
</div>
<Switch.Root
checked={settings.hashtag}

View File

@ -114,7 +114,7 @@ export function NoteZap() {
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
<div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
<div className="inline-flex items-center justify-between w-full px-5 py-3 shrink-0">

View File

@ -5,6 +5,7 @@ import {
NOSTR_EVENTS,
NOSTR_MENTIONS,
VIDEOS,
canPreview,
cn,
regionNames,
} from "@lume/utils";
@ -27,9 +28,11 @@ import { useNoteContext } from "./provider";
export function NoteContent({
className,
mini = false,
isTranslatable = false,
}: {
className?: string;
mini?: boolean;
isTranslatable?: boolean;
}) {
const storage = useStorage();
@ -52,7 +55,7 @@ export function NoteContent({
const words = text.split(/( |\n)/);
const urls = [...getUrls(text)];
if (storage.settings.media && !storage.settings.lowPower) {
if (storage.settings.media && !storage.settings.lowPower && !mini) {
images = urls.filter((word) =>
IMAGES.some((el) => {
const url = new URL(word);
@ -79,9 +82,11 @@ export function NoteContent({
);
}
events = words.filter((word) =>
NOSTR_EVENTS.some((el) => word.startsWith(el)),
);
if (!mini) {
events = words.filter((word) =>
NOSTR_EVENTS.some((el) => word.startsWith(el)),
);
}
const hashtags = words.filter((word) => word.startsWith("#"));
const mentions = words.filter((word) =>
@ -198,7 +203,7 @@ export function NoteContent({
(match, i) => {
const url = new URL(match);
if (!linkPreview) {
if (!linkPreview && canPreview(match)) {
linkPreview = match;
return <LinkPreview key={match + i} url={url.toString()} />;
}
@ -217,9 +222,11 @@ export function NoteContent({
},
);
parsedContent = reactStringReplace(parsedContent, "\n", () => {
return <div key={nanoid()} className="h-3" />;
});
if (!mini) {
parsedContent = reactStringReplace(parsedContent, "\n", () => {
return <div key={nanoid()} className="h-3" />;
});
}
if (typeof parsedContent[0] === "string") {
parsedContent[0] = parsedContent[0].trimStart();
@ -259,7 +266,12 @@ export function NoteContent({
return (
<div className={cn(className)}>
<div className="break-p select-text whitespace-pre-line text-balance leading-normal">
<div
className={cn(
"break-p select-text text-balance leading-normal",
!mini ? "whitespace-pre-line" : "",
)}
>
{richContent}
</div>
{isTranslatable && storage.settings.translation ? (

View File

@ -1,13 +1,17 @@
import { PinIcon } from "@lume/icons";
import { COL_TYPES } from "@lume/utils";
import { memo } from "react";
import { Link } from "react-router-dom";
import { Note } from "../";
import { useEvent } from "../../../hooks/useEvent";
import { useColumnContext } from "../../column/provider";
import { User } from "../../user";
export const MentionNote = memo(function MentionNote({
eventId,
openable = true,
}: { eventId: string; openable?: boolean }) {
const { addColumn } = useColumnContext();
const { isLoading, isError, data } = useEvent(eventId);
if (isLoading) {
@ -34,11 +38,11 @@ export const MentionNote = memo(function MentionNote({
return (
<Note.Provider event={data}>
<Note.Root className="flex flex-col w-full gap-1 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800">
<Note.Root className="flex flex-col w-full my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-100 dark:border-neutral-900">
<User.Provider pubkey={data.pubkey}>
<User.Root className="px-3 mt-3 flex h-6 items-center gap-2">
<User.Root className="flex h-10 px-3 items-center gap-2">
<User.Avatar className="size-6 shrink-0 rounded-md object-cover" />
<div className="flex flex-1 items-baseline gap-2">
<div className="flex-1 inline-flex gap-2">
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
<span className="text-neutral-600 dark:text-neutral-400">·</span>
<User.Time
@ -48,17 +52,30 @@ export const MentionNote = memo(function MentionNote({
</div>
</User.Root>
</User.Provider>
<div className="px-3 pb-3 mt-1">
<Note.Content />
{openable ? (
<Note.Content mini className="px-3" />
{openable ? (
<div className="mt-2 px-3 flex items-center justify-between">
<Link
to={`/events/${data.id}`}
className="mt-2 text-blue-500 hover:text-blue-600"
className="text-sm font-medium text-blue-500 hover:text-blue-600"
>
Show more
</Link>
) : null}
</div>
<button
type="button"
onClick={async () =>
await addColumn({
kind: COL_TYPES.thread,
title: "Thread",
content: data.id,
})
}
className="inline-flex items-center justify-center rounded-md text-neutral-600 dark:text-neutral-400 size-6 bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
<PinIcon className="size-4" />
</button>
</div>
) : null}
</Note.Root>
</Note.Provider>
);

View File

@ -12,7 +12,7 @@ export function NoteUser({
return (
<User.Provider pubkey={event.pubkey}>
<User.Root className={cn("flex items-center gap-3", className)}>
<User.Avatar className="size-9 shrink-0 rounded-lg bg-white object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
<User.Avatar className="size-9 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
<div className="flex h-6 flex-1 items-start justify-between gap-2">
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
<User.Time

View File

@ -1,3 +1,4 @@
import { useStorage } from "@lume/storage";
import { cn } from "@lume/utils";
import * as Avatar from "@radix-ui/react-avatar";
import { minidenticon } from "minidenticons";
@ -7,6 +8,8 @@ import { useUserContext } from "./provider";
export function UserAvatar({ className }: { className?: string }) {
const user = useUserContext();
const storage = useStorage();
const fallbackAvatar = useMemo(
() =>
`data:image/svg+xml;utf8,${encodeURIComponent(
@ -20,7 +23,7 @@ export function UserAvatar({ className }: { className?: string }) {
<div className="shrink-0">
<div
className={cn(
"bg-black/20 dark:bg-white/20 animate-pulse",
"bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>
@ -30,13 +33,23 @@ export function UserAvatar({ className }: { className?: string }) {
return (
<Avatar.Root className="shrink-0">
<Avatar.Image
src={user.image}
alt={user.pubkey}
loading="eager"
decoding="async"
className={className}
/>
{storage.settings.lowPower ? (
<Avatar.Image
src={fallbackAvatar}
alt={user.pubkey}
loading="eager"
decoding="async"
className={cn("bg-black dark:bg-white", className)}
/>
) : (
<Avatar.Image
src={user.image}
alt={user.pubkey}
loading="eager"
decoding="async"
className={className}
/>
)}
<Avatar.Fallback delayMs={120}>
<img
src={fallbackAvatar}

View File

@ -8,7 +8,7 @@ export function UserName({ className }: { className?: string }) {
return (
<div
className={cn(
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>
@ -16,7 +16,7 @@ export function UserName({ className }: { className?: string }) {
}
return (
<div className={cn("w-full max-w-[15rem] truncate", className)}>
<div className={cn("truncate", className)}>
{user.displayName || user.name || "Anon"}
</div>
);

View File

@ -29,7 +29,7 @@ export function UserNip05({
return (
<div
className={cn(
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>

View File

@ -9,6 +9,7 @@ import NDK, {
NDKPrivateKeySigner,
NDKRelay,
NDKRelayAuthPolicies,
NDKUser,
} from "@nostr-dev-kit/ndk";
import { fetch } from "@tauri-apps/plugin-http";
import Linkify from "linkify-react";
@ -19,7 +20,9 @@ import { LumeContext } from "./context";
export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
const storage = useStorage();
const [context, setContext] = useState<Ark>(undefined);
const [ark, setArk] = useState<Ark>(undefined);
const [ndk, setNDK] = useState<NDK>(undefined);
async function initNostrSigner({
nsecbunker,
@ -67,7 +70,7 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
}
}
async function init() {
async function initNDK() {
const explicitRelayUrls = normalizeRelayUrlSet([
"wss://bostr.nokotaro.com/",
"wss://nostr.mutinywallet.com/",
@ -77,14 +80,12 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
const outboxRelayUrls = normalizeRelayUrlSet(["wss://purplepag.es/"]);
// #TODO: user should config blacklist relays
// No need to connect depot tunnel url
const blacklistRelayUrls = storage.settings.tunnelUrl.length
? [
storage.settings.tunnelUrl,
`${storage.settings.tunnelUrl}/`,
"wss://brb.io/",
]
: ["wss://brb.io/"];
// Skip connect depot tunnel url
const blacklistRelayUrls = normalizeRelayUrlSet(
storage.settings.tunnelUrl.length
? [storage.settings.tunnelUrl, "wss://brb.io/"]
: ["wss://brb.io/"],
);
const cacheAdapter = new NDKCacheAdapterTauri(storage);
const ndk = new NDK({
@ -115,29 +116,37 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
// auth
ndk.relayAuthDefaultPolicy = async (relay: NDKRelay, challenge: string) => {
const signIn = NDKRelayAuthPolicies.signIn({ ndk });
const event = await signIn(relay, challenge).catch((e) => console.log(e));
const event = await signIn(relay, challenge).catch((e) =>
console.error(e),
);
if (event) {
sendNativeNotification(
await sendNativeNotification(
`You've sign in sucessfully to relay: ${relay.url}`,
);
return event;
}
};
// update account's metadata
if (signer) {
const user = ndk.getUser({ pubkey: storage.currentUser.pubkey });
setNDK(ndk);
}
async function initArk() {
// ark utils
const ark = new Ark({ ndk, account: storage.currentUser });
if (ndk && storage.currentUser) {
const user = new NDKUser({ pubkey: storage.currentUser.pubkey });
ndk.activeUser = user;
const contacts = await user.follows();
storage.currentUser.contacts = [...contacts].map((user) => user.pubkey);
// update contacts
await ark.getUserContacts();
// subscribe for new activity
const sub = ndk.subscribe(
{
kinds: [NDKKind.Text, NDKKind.Repost, NDKKind.Zap],
since: Math.floor(Date.now() / 1000),
"#p": [storage.currentUser.pubkey],
"#p": [ark.account.pubkey],
},
{ closeOnEose: false, groupable: false },
);
@ -169,18 +178,18 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
});
}
// ark utils
const ark = new Ark({ ndk, account: storage.currentUser });
// update context
setContext(ark);
setArk(ark);
}
useEffect(() => {
if (!context) init();
if (ndk) initArk();
}, [ndk]);
useEffect(() => {
if (!ark && !ndk) initNDK();
}, []);
if (!context) {
if (!ark) {
return (
<div
data-tauri-drag-region
@ -207,7 +216,5 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
);
}
return (
<LumeContext.Provider value={context}>{children}</LumeContext.Provider>
);
return <LumeContext.Provider value={ark}>{children}</LumeContext.Provider>;
};

View File

@ -279,9 +279,6 @@ export class LumeStorage {
}
const account = await this.getActiveAccount();
this.currentUser = account;
this.currentUser.contacts = [];
return account;
}

View File

@ -1,33 +0,0 @@
import { activityAtom } from "@lume/utils";
import { AnimatePresence, motion } from "framer-motion";
import { useAtomValue } from "jotai";
import { ActivityContent } from "./content";
export function Activity() {
const isActivityOpen = useAtomValue(activityAtom);
return (
<AnimatePresence initial={false} mode="wait">
{isActivityOpen ? (
<motion.div
key={isActivityOpen ? "activity-open" : "activity-close"}
layout
initial={{ scale: 0.9, opacity: 0, translateX: -20 }}
animate={{
scale: [0.95, 1],
opacity: [0.5, 1],
translateX: [-10, 0],
}}
exit={{
scale: [0.95, 0.9],
opacity: [0.5, 0],
translateX: [-10, -20],
}}
className="h-full w-[350px] px-1 pb-1 shrink-0"
>
<ActivityContent />
</motion.div>
) : null}
</AnimatePresence>
);
}

View File

@ -1,100 +0,0 @@
import { useArk } from "@lume/ark";
import { LoaderIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { ReplyActivity } from "./reply";
import { RepostActivity } from "./repost";
import { ZapActivity } from "./zap";
export function ActivityContent() {
const ark = useArk();
const storage = useStorage();
const { isLoading, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: ["activity"],
initialPageParam: 0,
queryFn: async ({
signal,
pageParam,
}: {
signal: AbortSignal;
pageParam: number;
}) => {
const events = await ark.getInfiniteEvents({
filter: {
kinds: [NDKKind.Zap],
"#p": [ark.account.pubkey],
},
limit: 100,
pageParam,
signal,
});
return events;
},
getNextPageParam: (lastPage) => {
const lastEvent = lastPage.at(-1);
if (!lastEvent) return;
return lastEvent.created_at - 1;
},
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
});
const allEvents = useMemo(
() => (data ? data.pages.flatMap((page) => page) : []),
[data],
);
const renderEvent = useCallback((event: NDKEvent) => {
if (event.pubkey === ark.account.pubkey) return null;
switch (event.kind) {
case NDKKind.Text:
return <ReplyActivity key={event.id} event={event} />;
case NDKKind.Repost:
return <RepostActivity key={event.id} event={event} />;
case NDKKind.Zap:
return <ZapActivity key={event.id} event={event} />;
default:
return <ReplyActivity key={event.id} event={event} />;
}
}, []);
return (
<div className="w-full h-full flex flex-col justify-between rounded-xl overflow-hidden bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-none dark:ring-1 dark:ring-white/10">
<div className="h-full w-full min-h-0">
{isLoading ? (
<div className="w-[350px] h-full flex items-center justify-center">
<LoaderIcon className="size-5 animate-spin" />
</div>
) : allEvents.length < 1 ? (
<div className="w-full h-full flex flex-col items-center justify-center">
<p className="mb-2 text-2xl">🎉</p>
<p className="text-center font-medium">Yo! Nothing new yet.</p>
</div>
) : (
renderEvent(allEvents[0])
)}
</div>
<div className="h-16 shrink-0 px-3 flex items-center gap-3 bg-neutral-100 dark:bg-neutral-900">
<button
type="button"
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
Previous
</button>
<button
type="button"
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-blue-500 text-white hover:bg-blue-600"
>
Next
</button>
</div>
</div>
);
}

View File

@ -1,51 +0,0 @@
import { Note, useArk } from "@lume/ark";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { ActivityRootNote } from "./rootNote";
export function ReplyActivity({ event }: { event: NDKEvent }) {
const ark = useArk();
const thread = ark.getEventThread({
content: event.content,
tags: event.tags,
});
return (
<div className="h-full pb-3 flex flex-col justify-between">
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
<h3 className="text-center font-semibold leading-tight">
Conversation
</h3>
<p className="text-sm text-blue-500 font-medium leading-tight">
@ Someone has replied to your note
</p>
</div>
<div className="px-3">
{thread ? (
<div className="flex flex-col gap-3 mb-1">
<ActivityRootNote eventId={thread.rootEventId} />
<ActivityRootNote eventId={thread.replyEventId} />
</div>
) : null}
<div className="mt-3 flex flex-col gap-3">
<div className="flex items-center gap-3">
<p className="text-teal-500 font-medium">New reply</p>
<div className="flex-1 h-px bg-teal-300" />
<div className="w-4 shrink-0 h-px bg-teal-300" />
</div>
<Note.Provider event={event}>
<Note.Root>
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
</div>
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10" />
</div>
</Note.Root>
</Note.Provider>
</div>
</div>
</div>
);
}

View File

@ -1,33 +0,0 @@
import { formatCreatedAt } from "@lume/utils";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { User } from "../user";
import { ActivityRootNote } from "./rootNote";
export function RepostActivity({ event }: { event: NDKEvent }) {
const repostId = event.tags.find((el) => el[0] === "e")[1];
const createdAt = formatCreatedAt(event.created_at);
return (
<div className="h-full pb-3 flex flex-col justify-between">
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
<h3 className="text-center font-semibold leading-tight">Boost</h3>
<p className="text-sm text-blue-500 font-medium leading-tight">
@ Someone has reposted to your note
</p>
</div>
<div className="px-3">
<div className="flex flex-col gap-3">
<User pubkey={event.pubkey} variant="notify2" />
<div className="flex items-center gap-3">
<p className="text-teal-500 font-medium">Reposted</p>
<div className="flex-1 h-px bg-teal-300" />
<div className="w-4 shrink-0 h-px bg-teal-300" />
</div>
</div>
<div className="mt-3 flex flex-col gap-3">
<ActivityRootNote eventId={repostId} />
</div>
</div>
</div>
);
}

View File

@ -1,40 +0,0 @@
import { Note, useEvent } from "@lume/ark";
export function ActivityRootNote({ eventId }: { eventId: string }) {
const { isLoading, isError, data } = useEvent(eventId);
if (isLoading) {
return (
<div className="relative flex gap-3">
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
<div className="h-4 w-full animate-pulse bg-neutral-300 dark:bg-neutral-700" />
</div>
</div>
);
}
if (isError) {
return (
<div className="relative flex gap-3">
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
Failed to fetch event
</div>
</div>
);
}
return (
<Note.Provider event={data}>
<Note.Root>
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
</div>
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10" />
</div>
</Note.Root>
</Note.Provider>
);
}

View File

@ -1,37 +0,0 @@
import { compactNumber, formatCreatedAt } from "@lume/utils";
import { NDKEvent, zapInvoiceFromEvent } from "@nostr-dev-kit/ndk";
import { User } from "../user";
import { ActivityRootNote } from "./rootNote";
export function ZapActivity({ event }: { event: NDKEvent }) {
const zapEventId = event.tags.find((tag) => tag[0] === "e")[1];
const invoice = zapInvoiceFromEvent(event);
return (
<div className="h-full pb-3 flex flex-col justify-between">
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
<h3 className="text-center font-semibold leading-tight">Zap</h3>
<p className="text-sm text-blue-500 font-medium leading-tight">
@ Someone love your note
</p>
</div>
<div className="px-3">
<div className="flex flex-col gap-3">
<User
pubkey={event.pubkey}
variant="notify2"
subtext={`${compactNumber.format(invoice.amount)} sats`}
/>
<div className="flex items-center gap-3">
<p className="text-teal-500 font-medium">Zapped</p>
<div className="flex-1 h-px bg-teal-300" />
<div className="w-4 shrink-0 h-px bg-teal-300" />
</div>
</div>
<div className="mt-3 flex flex-col gap-3">
<ActivityRootNote eventId={zapEventId} />
</div>
</div>
</div>
);
}

View File

@ -20,7 +20,7 @@ export function SettingsLayout() {
twMerge(
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
: "",
)
}
@ -34,7 +34,7 @@ export function SettingsLayout() {
twMerge(
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
: "",
)
}
@ -48,7 +48,7 @@ export function SettingsLayout() {
twMerge(
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
: "",
)
}
@ -62,7 +62,7 @@ export function SettingsLayout() {
twMerge(
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
: "",
)
}
@ -76,7 +76,7 @@ export function SettingsLayout() {
twMerge(
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
isActive
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
: "",
)
}

View File

@ -9,7 +9,7 @@ export function OnboardingModal() {
return (
<Dialog.Root open={onboarding}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
<div className="relative w-full max-w-lg bg-white h-[500px] rounded-xl dark:bg-black overflow-hidden">
<OnboardingRouter />

View File

@ -2,6 +2,7 @@ import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import updateLocale from "dayjs/plugin/updateLocale";
import { nip19 } from "nostr-tools";
import { AUDIOS, IMAGES, VIDEOS } from "./constants";
dayjs.extend(relativeTime);
dayjs.extend(updateLocale);
@ -68,3 +69,23 @@ export const compactNumber = Intl.NumberFormat("en", { notation: "compact" });
// country name
export const regionNames = new Intl.DisplayNames(["en"], { type: "language" });
// verify link can be preview
export function canPreview(text: string) {
const url = new URL(text);
const ext = url.pathname.split(".").pop();
const hostname = url.hostname;
if (VIDEOS.includes(ext)) return false;
if (IMAGES.includes(ext)) return false;
if (AUDIOS.includes(ext)) return false;
if (hostname === "youtube.com") return false;
if (hostname === "youtu.be") return false;
if (hostname === "x.com") return false;
if (hostname === "twitter.com") return false;
if (hostname === "facebook.com") return false;
if (hostname === "vimeo.com") return false;
return true;
}

View File

@ -270,25 +270,25 @@ importers:
version: 0.15.0(@nostr-dev-kit/ndk@2.3.2)(nostr-fetch@0.15.0)
'@radix-ui/react-avatar':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collapsible':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dialog':
specifier: ^1.0.5
version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.6
version: 2.0.6(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 2.0.6(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-hover-card':
specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popover':
specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-tooltip':
specifier: ^1.0.7
version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.7(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query':
specifier: ^5.17.9
version: 5.17.9(react@18.2.0)
@ -1927,6 +1927,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-arrow@1.0.3(@types/react@18.2.47)(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.47)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
peerDependencies:
@ -1951,6 +1971,29 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-avatar@1.0.4(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies:
@ -1979,6 +2022,33 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collapsible@1.0.3(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
@ -2003,6 +2073,29 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
@ -2065,6 +2158,39 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
dev: false
/@radix-ui/react-dialog@1.0.5(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0)
dev: false
/@radix-ui/react-direction@1.0.1(@types/react@18.2.47)(react@18.2.0):
resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
peerDependencies:
@ -2104,6 +2230,30 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dismissable-layer@1.0.5(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==}
peerDependencies:
@ -2131,6 +2281,32 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-dropdown-menu@2.0.6(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-menu': 2.0.6(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0):
resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
peerDependencies:
@ -2168,6 +2344,28 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-focus-scope@1.0.4(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A==}
peerDependencies:
@ -2197,6 +2395,34 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-hover-card@1.0.7(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0):
resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
peerDependencies:
@ -2250,6 +2476,43 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
dev: false
/@radix-ui/react-menu@2.0.6(@types/react@18.2.47)(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.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0)
dev: false
/@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
peerDependencies:
@ -2285,6 +2548,40 @@ packages:
react-remove-scroll: 2.5.5(@types/react@18.2.47)(react@18.2.0)
dev: false
/@radix-ui/react-popover@1.0.7(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
peerDependencies:
@ -2315,6 +2612,35 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-popper@1.1.3(@types/react@18.2.47)(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.5(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/rect': 1.0.1
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
peerDependencies:
@ -2336,6 +2662,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-portal@1.0.4(@types/react@18.2.47)(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.47)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
@ -2358,6 +2704,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
@ -2379,6 +2746,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react@18.2.47)(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.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
@ -2408,6 +2795,34 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(@types/react@18.2.47)(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.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==}
peerDependencies:
@ -2523,6 +2938,37 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-tooltip@1.0.7(@types/react@18.2.47)(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.47)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.47)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react@18.2.47)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.47
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.47)(react@18.2.0):
resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
peerDependencies:
@ -2646,6 +3092,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-visually-hidden@1.0.3(@types/react@18.2.47)(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.47)(react-dom@18.2.0)(react@18.2.0)
'@types/react': 18.2.47
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies:

View File

@ -1,5 +1,5 @@
use std::process::Command;
use keyring::Entry;
use std::process::Command;
use std::time::Duration;
use webpage::{Webpage, WebpageOptions};
@ -117,14 +117,14 @@ pub async fn opengraph(url: String) -> OpenGraphResponse {
#[tauri::command]
pub fn secure_save(key: String, value: String) -> Result<(), ()> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
let _ = entry.set_password(&value);
Ok(())
}
#[tauri::command]
pub fn secure_load(key: String) -> Result<String, String> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
if let Ok(password) = entry.get_password() {
Ok(password)
} else {
@ -134,7 +134,7 @@ pub fn secure_load(key: String) -> Result<String, String> {
#[tauri::command]
pub fn secure_remove(key: String) -> Result<(), ()> {
let entry = Entry::new("lume", &key).expect("Failed to create entry");
let entry = Entry::new("Lume", &key).expect("Failed to create entry");
let _ = entry.delete_password();
Ok(())
}