This commit is contained in:
reya
2023-11-21 08:28:41 +07:00
parent ee97d461c8
commit daf7a4d466
8 changed files with 377 additions and 179 deletions

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>nos2x</title> <title>Nostr Connect (formerly nos2x)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/build/style.css" rel="stylesheet" /> <link href="/build/style.css" rel="stylesheet" />
</head> </head>

View File

@@ -6,6 +6,7 @@ import QRCode from 'react-qr-code'
import * as Tabs from '@radix-ui/react-tabs' import * as Tabs from '@radix-ui/react-tabs'
import {LogoIcon} from './icons' import {LogoIcon} from './icons'
import {removePermissions} from './common' import {removePermissions} from './common'
import * as Checkbox from '@radix-ui/react-checkbox'
function Options() { function Options() {
let [privKey, setPrivKey] = useState('') let [privKey, setPrivKey] = useState('')
@@ -185,22 +186,76 @@ function Options() {
className="flex-1 h-9 bg-transparent border px-3 py-1 border-primary rounded-lg placeholder:text-muted" className="flex-1 h-9 bg-transparent border px-3 py-1 border-primary rounded-lg placeholder:text-muted"
/> />
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<label className="inline-flex items-center gap-2 text-muted font-medium"> <div className="inline-flex items-center gap-2">
<input <Checkbox.Root
type="checkbox" id={`read-${i}`}
checked={policy.read} checked={policy.read}
onChange={toggleRelayPolicy.bind(null, i, 'read')} onCheckedChange={toggleRelayPolicy.bind(
/> null,
<p>Read</p> i,
</label> 'read'
<label className="inline-flex items-center gap-2 text-muted font-medium"> )}
<input className="flex h-6 w-6 appearance-none items-center justify-center rounded-lg bg-white outline-none border border-primary data-[state=checked]:bg-primary data-[state=checked]:border-secondary"
type="checkbox" >
<Checkbox.Indicator className="text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
</Checkbox.Indicator>
</Checkbox.Root>
<label
htmlFor={`read-${i}`}
className="text-muted font-medium"
>
Read
</label>
</div>
<div className="inline-flex items-center gap-2">
<Checkbox.Root
id={`write-${i}`}
checked={policy.write} checked={policy.write}
onChange={toggleRelayPolicy.bind(null, i, 'write')} onCheckedChange={toggleRelayPolicy.bind(
/> null,
<p>Write</p> i,
</label> 'write'
)}
className="flex h-6 w-6 appearance-none items-center justify-center rounded-lg bg-white outline-none border border-primary data-[state=checked]:bg-primary data-[state=checked]:border-secondary"
>
<Checkbox.Indicator className="text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
</Checkbox.Indicator>
</Checkbox.Root>
<label
htmlFor={`write-${i}`}
className="text-muted font-medium"
>
Write
</label>
</div>
</div> </div>
<button <button
onClick={removeRelay.bind(null, i)} onClick={removeRelay.bind(null, i)}
@@ -317,15 +372,34 @@ function Options() {
</Tabs.Content> </Tabs.Content>
</Tabs.Root> </Tabs.Root>
<div className="mb-3"> <div className="mb-3">
<label className="flex gap-2 items-center"> <div className="flex items-center gap-2">
<input <Checkbox.Root
type="checkbox" id="notification"
className="flex h-6 w-6 appearance-none items-center justify-center rounded-lg bg-white outline-none border border-primary data-[state=checked]:bg-primary data-[state=checked]:border-secondary"
checked={showNotifications} checked={showNotifications}
onChange={handleNotifications} onCheckedChange={handleNotifications}
className="w-6 h-6 rounded-md border border-gray-200 dark:border-gray-800 appearance-none" >
/> <Checkbox.Indicator className="text-white">
Show desktop notifications when a permissions has been used <svg
</label> xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
</Checkbox.Indicator>
</Checkbox.Root>
<label htmlFor="notification">
Show desktop notifications when a permissions has been used
</label>
</div>
</div> </div>
<div> <div>
<details> <details>
@@ -349,47 +423,48 @@ function Options() {
</div> </div>
</summary> </summary>
<div className="mt-3"> <div className="mt-3">
<label className="flex gap-2 items-center"> <div className="flex items-center gap-2">
<input <Checkbox.Root
type="checkbox" id="notification"
className="flex h-6 w-6 appearance-none items-center justify-center rounded-lg bg-white outline-none border border-primary data-[state=checked]:bg-primary data-[state=checked]:border-secondary"
checked={handleNostrLinks} checked={handleNostrLinks}
onChange={changeHandleNostrLinks} onCheckedChange={changeHandleNostrLinks}
className="w-6 h-6 rounded-md border border-gray-200 dark:border-gray-800 appearance-none" >
/> <Checkbox.Indicator className="text-white">
Handle nostr links <svg
</label> xmlns="http://www.w3.org/2000/svg"
</div> fill="none"
</details> viewBox="0 0 24 24"
</div> strokeWidth={1.5}
{/*<div> stroke="currentColor"
<label style={{display: 'flex', alignItems: 'center'}}> className="w-4 h-4"
<div> >
handle{' '} <path
<span style={{padding: '2px', background: 'silver'}}>nostr:</span>{' '} strokeLinecap="round"
links: strokeLinejoin="round"
</div> d="M4.5 12.75l6 6 9-13.5"
<input />
type="checkbox" </svg>
checked={handleNostrLinks} </Checkbox.Indicator>
onChange={changeHandleNostrLinks} </Checkbox.Root>
/> <label htmlFor="notification">Handle nostr links</label>
</label>
<div style={{marginLeft: '10px'}}>
{handleNostrLinks && (
<div>
<div style={{display: 'flex'}}>
<input
placeholder="url template"
value={protocolHandler}
onChange={handleChangeProtocolHandler}
style={{width: '680px', maxWidth: '90%'}}
/>
{!showProtocolHandlerHelp && (
<button onClick={changeShowProtocolHandlerHelp}>?</button>
)}
</div> </div>
{showProtocolHandlerHelp && ( {handleNostrLinks && (
<pre>{` <div className="mt-3">
<div className="flex">
<input
placeholder="url template"
value={protocolHandler}
onChange={handleChangeProtocolHandler}
/>
{!showProtocolHandlerHelp && (
<button onClick={changeShowProtocolHandlerHelp}>
?
</button>
)}
</div>
{showProtocolHandlerHelp && (
<pre className="bg-muted p-2 rounded-xl break-all">{`
{raw} = anything after the colon, i.e. the full nip19 bech32 string {raw} = anything after the colon, i.e. the full nip19 bech32 string
{hex} = hex pubkey for npub or nprofile, hex event id for note or nevent {hex} = hex pubkey for npub or nprofile, hex event id for note or nevent
{p_or_e} = "p" for npub or nprofile, "e" for note or nevent {p_or_e} = "p" for npub or nprofile, "e" for note or nevent
@@ -404,16 +479,12 @@ function Options() {
- https://snort.social/{raw} - https://snort.social/{raw}
- https://nostr.band/{raw} - https://nostr.band/{raw}
`}</pre> `}</pre>
)}
</div>
)} )}
</div> </div>
)} </details>
</div> </div>
</div>*/}
{/*<div style={{fontSize: '120%'}}>
{messages.map((message, i) => (
<div key={i}>{message}</div>
))}
</div>*/}
</div> </div>
<button <button
disabled={!unsavedChanges.length} disabled={!unsavedChanges.length}

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>nos2x</title> <title>Nostr Connect (formerly nos2x)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/build/style.css" rel="stylesheet" /> <link href="/build/style.css" rel="stylesheet" />
</head> </head>

View File

@@ -1,23 +1,21 @@
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
import {render} from 'react-dom' import {render} from 'react-dom'
import {getPublicKey, nip19} from 'nostr-tools' import {getPublicKey, nip19} from 'nostr-tools'
import React, {useState, useMemo, useRef, useEffect} from 'react' import React, {useState, useMemo, useEffect} from 'react'
import QRCode from 'react-qr-code' import QRCode from 'react-qr-code'
import {SettingsIcon} from './icons' import {SettingsIcon} from './icons'
import {minidenticon} from 'minidenticons' import {minidenticon} from 'minidenticons'
import * as Tabs from '@radix-ui/react-tabs' import * as Tabs from '@radix-ui/react-tabs'
function Popup() { function Popup() {
let [pubKey, setPubKey] = useState('') let [keys, setKeys] = useState(null)
let keys = useRef({npub: '', hex: '', nprofile: ''})
let avatarURI = useMemo( let avatarURI = useMemo(
() => () =>
pubKey keys
? 'data:image/svg+xml;utf8,' + ? 'data:image/svg+xml;utf8,' +
encodeURIComponent(minidenticon(pubKey, 90, 30)) encodeURIComponent(minidenticon(keys.npub, 90, 30))
: null, : null,
[pubKey] [keys]
) )
const gotoSettings = () => { const gotoSettings = () => {
@@ -32,10 +30,7 @@ function Popup() {
let hexKey = getPublicKey(results.private_key) let hexKey = getPublicKey(results.private_key)
let npubKey = nip19.npubEncode(hexKey) let npubKey = nip19.npubEncode(hexKey)
setPubKey(npubKey) setKeys({npub: npubKey, hex: hexKey})
keys.current.npub = npubKey
keys.current.hex = hexKey
if (results.relays) { if (results.relays) {
let relaysList = [] let relaysList = []
@@ -50,18 +45,18 @@ function Popup() {
pubkey: hexKey, pubkey: hexKey,
relays: relaysList relays: relaysList
}) })
keys.current.nprofile = nprofileKey setKeys(prev => ({...prev, nprofile: nprofileKey}))
} }
} }
} else { } else {
setPubKey(null) setKeys(null)
} }
}) })
}, []) }, [])
return ( return (
<div className="w-[320px] p-6"> <div className="w-[320px] p-6">
{!pubKey ? ( {!keys ? (
<div className="flex items-center justify-between gap-6"> <div className="flex items-center justify-between gap-6">
<div className="flex-1 flex items-center justify-between"> <div className="flex-1 flex items-center justify-between">
<p className="text-sm font-medium"> <p className="text-sm font-medium">
@@ -129,31 +124,53 @@ function Popup() {
> >
hex hex
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger {keys.nprofile ? (
value="nprofile" <Tabs.Trigger
className="font-medium flex-1 flex items-center justify-center text-muted h-10 data-[state=active]:text-primary data-[state=active]:border-b data-[state=active]:border-secondary" value="nprofile"
> className="font-medium flex-1 flex items-center justify-center text-muted h-10 data-[state=active]:text-primary data-[state=active]:border-b data-[state=active]:border-secondary"
naddr >
</Tabs.Trigger> nprofile
</Tabs.Trigger>
) : null}
</Tabs.List> </Tabs.List>
<Tabs.Content value="npub"> <Tabs.Content value="npub">
<div className="my-4"> <div className="my-4">
<textarea <textarea
value={keys.npub}
readOnly readOnly
className="w-full h-20 resize-none p-3 bg-muted rounded-xl" className="w-full h-20 resize-none p-3 bg-muted rounded-xl"
>
{pubKey}
</textarea>
</div>
<div className="w-full rounded-xl border border-primary p-4 flex items-center justify-center">
<QRCode
size={128}
value={
pubKey.startsWith('n') ? pubKey.toUpperCase() : pubKey
}
/> />
</div> </div>
<div className="w-full rounded-xl border border-primary p-4 flex items-center justify-center">
<QRCode size={128} value={keys.npub} />
</div>
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="hex">
<div className="my-4">
<textarea
value={keys.hex}
readOnly
className="w-full h-20 resize-none p-3 bg-muted rounded-xl"
/>
</div>
<div className="w-full rounded-xl border border-primary p-4 flex items-center justify-center">
<QRCode size={128} value={keys.hex} />
</div>
</Tabs.Content>
{keys.nprofile ? (
<Tabs.Content value="nprofile">
<div className="my-4">
<textarea
value={keys.nprofile}
readOnly
className="w-full h-20 resize-none p-3 bg-muted rounded-xl"
/>
</div>
<div className="w-full rounded-xl border border-primary p-4 flex items-center justify-center">
<QRCode size={128} value={keys.nprofile} />
</div>
</Tabs.Content>
) : null}
</Tabs.Root> </Tabs.Root>
</div> </div>
</div> </div>

View File

@@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>nos2x</title> <title>Nostr Connect (formerly nos2x)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/build/style.css" rel="stylesheet" /> <link href="/build/style.css" rel="stylesheet" />
</head> </head>

View File

@@ -1,15 +1,20 @@
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
import {render} from 'react-dom' import {render} from 'react-dom'
import React from 'react' import React, {useState} from 'react'
import {PERMISSION_NAMES} from './common' import {PERMISSION_NAMES} from './common'
import {LogoIcon} from './icons'
import * as Checkbox from '@radix-ui/react-checkbox'
function Prompt() { function Prompt() {
const [isRemember, setIsRemember] = useState(false)
let qs = new URLSearchParams(location.search) let qs = new URLSearchParams(location.search)
let id = qs.get('id') let id = qs.get('id')
let host = qs.get('host') let host = qs.get('host')
let type = qs.get('type') let type = qs.get('type')
let params, event let params, event
try { try {
params = JSON.parse(qs.get('params')) params = JSON.parse(qs.get('params'))
if (Object.keys(params).length === 0) params = null if (Object.keys(params).length === 0) params = null
@@ -18,83 +23,8 @@ function Prompt() {
params = null params = null
} }
return ( function authorizeHandler(accept) {
<> const conditions = isRemember ? {} : null
<div>
<b style={{display: 'block', textAlign: 'center', fontSize: '200%'}}>
{host}
</b>{' '}
<p>
is requesting your permission to <b>{PERMISSION_NAMES[type]}:</b>
</p>
</div>
{params && (
<>
<p>now acting on</p>
<pre style={{overflow: 'auto', maxHeight: '120px'}}>
<code>{JSON.stringify(event || params, null, 2)}</code>
</pre>
</>
)}
<div
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around'
}}
>
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
true,
{} // store this and answer true forever
)}
>
authorize forever
</button>
{event?.kind !== undefined && (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
true,
{kinds: {[event.kind]: true}} // store and always answer true for all events that match this condition
)}
>
authorize kind {event.kind} forever
</button>
)}
<button style={{marginTop: '5px'}} onClick={authorizeHandler(true)}>
authorize just this
</button>
{event?.kind !== undefined ? (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
false,
{kinds: {[event.kind]: true}} // idem
)}
>
reject kind {event.kind} forever
</button>
) : (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
false,
{} // idem
)}
>
reject forever
</button>
)}
<button style={{marginTop: '5px'}} onClick={authorizeHandler(false)}>
reject
</button>
</div>
</>
)
function authorizeHandler(accept, conditions) {
return function (ev) { return function (ev) {
ev.preventDefault() ev.preventDefault()
browser.runtime.sendMessage({ browser.runtime.sendMessage({
@@ -107,6 +37,129 @@ function Prompt() {
}) })
} }
} }
return (
<div className="w-screen h-screen flex flex-col items-center justify-center">
<div className="p-8 shadow-primary border border-primary rounded-2xl max-w-xl mx-auto flex flex-col gap-5">
<div className="flex flex-col items-center gap-5">
<LogoIcon />
<div className="flex flex-col items-center text-center">
<h1 className="font-semibold text-lg">{host}</h1>
<p>
is requesting your permission to <b>{PERMISSION_NAMES[type]}</b>
</p>
</div>
</div>
{params && (
<div>
<p>Now acting on</p>
<pre className="bg-muted rounded-xl p-3">
<code>{JSON.stringify(event || params, null, 2)}</code>
</pre>
</div>
)}
<div className="flex flex-col gap-4">
<div className="flex items-center justify-center gap-2">
<Checkbox.Root
id="remember"
className="flex h-6 w-6 appearance-none items-center justify-center rounded-lg bg-white outline-none border border-primary data-[state=checked]:bg-primary data-[state=checked]:border-secondary"
onCheckedChange={setIsRemember}
>
<Checkbox.Indicator className="text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
</Checkbox.Indicator>
</Checkbox.Root>
<label htmlFor="remember" className="text-muted">
Remember my preference forever
</label>
</div>
<div className="flex gap-3">
<button
onClick={authorizeHandler(false)}
className="flex-1 h-10 rounded-lg shadow-sm border border-primary inline-flex items-center justify-center font-semibold"
>
Reject
</button>
<button
onClick={authorizeHandler(true)}
className="flex-1 h-10 rounded-lg shadow-sm border border-secondary bg-primary text-white inline-flex items-center justify-center font-semibold"
>
Authorize
</button>
</div>
</div>
{/*
<div
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around'
}}
>
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
true,
{} // store this and answer true forever
)}
>
authorize forever
</button>
{event?.kind !== undefined && (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
true,
{kinds: {[event.kind]: true}} // store and always answer true for all events that match this condition
)}
>
authorize kind {event.kind} forever
</button>
)}
<button style={{marginTop: '5px'}} onClick={authorizeHandler(true)}>
authorize just this
</button>
{event?.kind !== undefined ? (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
false,
{kinds: {[event.kind]: true}} // idem
)}
>
reject kind {event.kind} forever
</button>
) : (
<button
style={{marginTop: '5px'}}
onClick={authorizeHandler(
false,
{} // idem
)}
>
reject forever
</button>
)}
<button style={{marginTop: '5px'}} onClick={authorizeHandler(false)}>
reject
</button>
</div>*/}
</div>
</div>
)
} }
render(<Prompt />, document.getElementById('main')) render(<Prompt />, document.getElementById('main'))

View File

@@ -1,6 +1,7 @@
{ {
"license": "WTFPL", "license": "WTFPL",
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tabs": "^1.0.4",
"async-mutex": "^0.3.2", "async-mutex": "^0.3.2",
"esbuild": "^0.14.54", "esbuild": "^0.14.54",

56
pnpm-lock.yaml generated
View File

@@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@radix-ui/react-checkbox':
specifier: ^1.0.4
version: 1.0.4(react-dom@17.0.2)(react@17.0.2)
'@radix-ui/react-tabs': '@radix-ui/react-tabs':
specifier: ^1.0.4 specifier: ^1.0.4
version: 1.0.4(react-dom@17.0.2)(react@17.0.2) version: 1.0.4(react-dom@17.0.2)(react@17.0.2)
@@ -1730,6 +1733,32 @@ packages:
'@babel/runtime': 7.23.2 '@babel/runtime': 7.23.2
dev: false dev: false
/@radix-ui/react-checkbox@1.0.4(react-dom@17.0.2)(react@17.0.2):
resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==}
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.2
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(react@17.0.2)
'@radix-ui/react-context': 1.0.1(react@17.0.2)
'@radix-ui/react-presence': 1.0.1(react-dom@17.0.2)(react@17.0.2)
'@radix-ui/react-primitive': 1.0.3(react-dom@17.0.2)(react@17.0.2)
'@radix-ui/react-use-controllable-state': 1.0.1(react@17.0.2)
'@radix-ui/react-use-previous': 1.0.1(react@17.0.2)
'@radix-ui/react-use-size': 1.0.1(react@17.0.2)
react: 17.0.2
react-dom: 17.0.2(react@17.0.2)
dev: false
/@radix-ui/react-collection@1.0.3(react-dom@17.0.2)(react@17.0.2): /@radix-ui/react-collection@1.0.3(react-dom@17.0.2)(react@17.0.2):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies: peerDependencies:
@@ -1951,6 +1980,33 @@ packages:
react: 17.0.2 react: 17.0.2
dev: false dev: false
/@radix-ui/react-use-previous@1.0.1(react@17.0.2):
resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.2
react: 17.0.2
dev: false
/@radix-ui/react-use-size@1.0.1(react@17.0.2):
resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.2
'@radix-ui/react-use-layout-effect': 1.0.1(react@17.0.2)
react: 17.0.2
dev: false
/@react-native-community/cli-clean@11.3.10: /@react-native-community/cli-clean@11.3.10:
resolution: {integrity: sha512-g6QjW+DSqoWRHzmIQW3AH22k1AnynWuOdy2YPwYEGgPddTeXZtJphIpEVwDOiC0L4mZv2VmiX33/cGNUwO0cIA==} resolution: {integrity: sha512-g6QjW+DSqoWRHzmIQW3AH22k1AnynWuOdy2YPwYEGgPddTeXZtJphIpEVwDOiC0L4mZv2VmiX33/cGNUwO0cIA==}
dependencies: dependencies: