fix: video upload

feat: use new video/short kinds
This commit is contained in:
kieran 2025-01-28 15:13:41 +00:00
parent 4bade72dd6
commit de88158b3a
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
17 changed files with 219 additions and 127 deletions

View File

@ -8,11 +8,11 @@
"@noble/hashes": "^1.4.0",
"@scure/base": "^1.1.6",
"@snort/shared": "^1.0.17",
"@snort/system": "^1.5.6",
"@snort/system-react": "^1.5.6",
"@snort/system": "^1.6.1",
"@snort/system-react": "^1.6.1",
"@snort/system-wasm": "^1.0.5",
"@snort/wallet": "^0.2.1",
"@snort/worker-relay": "^1.3.0",
"@snort/wallet": "^0.2.4",
"@snort/worker-relay": "^1.3.1",
"@szhsin/react-menu": "^4.1.0",
"@types/webscopeio__react-textarea-autocomplete": "^4.7.5",
"@webscopeio/react-textarea-autocomplete": "^4.9.2",

View File

@ -8,8 +8,10 @@ export const GOAL = 9041 as EventKind;
export const USER_CARDS = 17_777 as EventKind;
export const CARD = 37_777 as EventKind;
export const VIDEO_KIND = 34_235 as EventKind;
export const SHORTS_KIND = 34_236 as EventKind;
export const VIDEO_KIND = 21 as EventKind;
export const SHORTS_KIND = 22 as EventKind;
export const OLD_VIDEO_KIND = 34_235 as EventKind;
export const OLD_SHORTS_KIND = 34_236 as EventKind;
export const MINUTE = 60;
export const HOUR = 60 * MINUTE;

View File

@ -5,13 +5,15 @@ import { Goal } from "./goal";
import { Note } from "./note";
import { EmojiPack } from "./emoji-pack";
import { BadgeInfo } from "./badge";
import { GOAL, LIVE_STREAM_CLIP, StreamState } from "@/const";
import { GOAL, LIVE_STREAM_CLIP, OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, StreamState, VIDEO_KIND } from "@/const";
import { useEventFeed } from "@snort/system-react";
import LiveStreamClip from "./stream/clip";
import { ExternalLink } from "./external-link";
import { extractStreamInfo } from "@/utils";
import LiveVideoPlayer from "./stream/live-video-player";
import { HTMLProps } from "react";
import { HTMLProps, ReactNode } from "react";
import { ShortPage } from "@/pages/short";
import { VideoPage } from "@/pages/video";
interface EventProps {
link: NostrLink;
@ -31,36 +33,49 @@ export function EventIcon({ kind }: { kind?: EventKind }) {
}
export function NostrEvent({ ev }: { ev: TaggedNostrEvent }) {
const link = NostrLink.fromEvent(ev);
function modalPage(inner: ReactNode) {
return <div className="rounded-2xl px-4 py-3 md:w-[700px] mx-auto w-full">{inner}</div>;
}
switch (ev.kind) {
case GOAL: {
return <Goal ev={ev} />;
return modalPage(<Goal ev={ev} />);
}
case EventKind.EmojiSet: {
return <EmojiPack ev={ev} />;
return modalPage(<EmojiPack ev={ev} />);
}
case EventKind.Badge: {
return <BadgeInfo ev={ev} />;
return modalPage(<BadgeInfo ev={ev} />);
}
case EventKind.TextNote: {
return <Note ev={ev} />;
return modalPage(<Note ev={ev} />);
}
case LIVE_STREAM_CLIP: {
return <LiveStreamClip ev={ev} />;
return modalPage(<LiveStreamClip ev={ev} />);
}
case OLD_SHORTS_KIND:
case SHORTS_KIND: {
return <ShortPage link={link} evPreload={ev} />;
}
case OLD_VIDEO_KIND:
case VIDEO_KIND: {
return <VideoPage link={link} evPreload={ev} />;
}
case EventKind.LiveEvent: {
const info = extractStreamInfo(ev);
return (
return modalPage(
<LiveVideoPlayer
link={link}
title={info.title}
status={info.status}
stream={info.status === StreamState.Live ? info.stream : info.recording}
poster={info.image}
/>
/>,
);
}
default: {
const link = NostrLink.fromEvent(ev);
return <ExternalLink href={`https://snort.social/${link.encode()}`}>{link.encode()}</ExternalLink>;
return modalPage(<ExternalLink href={`https://snort.social/${link.encode()}`}>{link.encode()}</ExternalLink>);
}
}
}

View File

@ -145,7 +145,7 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
}
}
const startsTimestamp = parseInt(start ?? (new Date() / 1000));
const startsTimestamp = Number(start ?? (new Date().getTime() / 1000));
const startsDate = new Date(startsTimestamp * 1000);
return (

View File

@ -1,13 +1,10 @@
import { unwrap } from "@snort/shared";
import { NostrLink, NostrPrefix, RequestBuilder, TaggedNostrEvent } from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { useMemo } from "react";
import { LIVE_STREAM } from "@/const";
import { getHost } from "@/utils";
export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPreload?: TaggedNostrEvent) {
const author = link.type === NostrPrefix.Address ? unwrap(link.author) : link.id;
const sub = useMemo(() => {
const b = new RequestBuilder(`current-event:${link.id}`);
b.withOptions({
@ -16,14 +13,8 @@ export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPrelo
if (link.type === NostrPrefix.PublicKey || link.type === NostrPrefix.Profile) {
b.withFilter().authors([link.id]).kinds([LIVE_STREAM]);
b.withFilter().tag("p", [link.id]).kinds([LIVE_STREAM]);
} else if (link.type === NostrPrefix.Address) {
const f = b.withFilter().tag("d", [link.id]);
if (link.author) {
f.authors([link.author]);
}
if (link.kind) {
f.kinds([link.kind]);
}
} else {
b.withFilter().link(link);
}
return b;
}, [link.id, leaveOpen]);
@ -31,9 +22,7 @@ export function useCurrentStreamFeed(link: NostrLink, leaveOpen = false, evPrelo
const q = useRequestBuilder(sub);
return useMemo(() => {
const hosting = [...q, ...(evPreload ? [evPreload] : [])]
.filter(a => getHost(a) === author || a.pubkey === author)
.sort((a, b) => (b.created_at > a.created_at ? 1 : -1));
const hosting = [...q, ...(evPreload ? [evPreload] : [])].sort((a, b) => (b.created_at > a.created_at ? 1 : -1));
return hosting.at(0);
}, [q]);
}

View File

@ -1,4 +1,4 @@
import { SHORTS_KIND, VIDEO_KIND } from "@/const";
import { OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, VIDEO_KIND } from "@/const";
import { MediaPayload, VideoInfo } from "@/service/video/info";
import { findTag } from "@/utils";
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
@ -58,7 +58,7 @@ export function useDeadLink(ev: TaggedNostrEvent | NostrEvent) {
useEffect(() => {
const links =
ev.kind === VIDEO_KIND || ev.kind === SHORTS_KIND
ev.kind === VIDEO_KIND || ev.kind === SHORTS_KIND || ev.kind == OLD_SHORTS_KIND || ev.kind == OLD_VIDEO_KIND
? VideoInfo.parse(ev)?.sources()
: [
{

View File

@ -4,12 +4,7 @@ import { removeUndefined, sanitizeRelayUrl } from "@snort/shared";
import { Nip96Server } from "@/service/upload/nip96";
import { useMemo } from "react";
export const DefaultMediaServers = [
//"https://media.zap.stream",
new UnknownTag(["server", "https://nostr.build/"]),
new UnknownTag(["server", "https://nostrcheck.me/"]),
new UnknownTag(["server", "https://files.v0l.io/"]),
];
export const DefaultMediaServers = [new UnknownTag(["server", "https://nostr.download/"])];
export function useMediaServerList() {
const login = useLogin();

View File

@ -221,6 +221,9 @@
"BD0vyn": {
"defaultMessage": "{name} created a clip"
},
"BHNL+v": {
"defaultMessage": "Raw Data:"
},
"Bd1yEX": {
"defaultMessage": "New Stream Goal"
},
@ -641,6 +644,9 @@
"ZcgtZo": {
"defaultMessage": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice."
},
"Zfr//4": {
"defaultMessage": "No duration provided, please try another upload server."
},
"ZmqxZs": {
"defaultMessage": "You can change this later"
},
@ -705,6 +711,9 @@
"dkUMIH": {
"defaultMessage": "Clip by {name}"
},
"dqGkI+": {
"defaultMessage": "Video durations vary too much, are you sure each variant is the same video?"
},
"e011kf": {
"defaultMessage": "FAQ",
"description": "Title: FAQ page"
@ -945,6 +954,9 @@
"vP4dFa": {
"defaultMessage": "Visit {link} to get some sweet zap.stream merch!"
},
"vaZKTn": {
"defaultMessage": "Add more content"
},
"vrTOHJ": {
"defaultMessage": "{amount} sats"
},

View File

@ -1,4 +1,4 @@
import { LIVE_STREAM, SHORTS_KIND, VIDEO_KIND } from "@/const";
import { LIVE_STREAM, OLD_SHORTS_KIND, OLD_VIDEO_KIND, SHORTS_KIND, VIDEO_KIND } from "@/const";
import { useStreamLink } from "@/hooks/stream-link";
import { getEventFromLocationState } from "@/utils";
import { NostrPrefix } from "@snort/system";
@ -20,20 +20,16 @@ export function LinkHandler() {
if (!link) return;
if (link.type === NostrPrefix.Event) {
return (
<div className="rounded-2xl px-4 py-3 md:w-[700px] mx-auto w-full">
<NostrEventElement link={link} />
</div>
);
return <NostrEventElement link={link} />;
} else if (link.kind === LIVE_STREAM || link.type === NostrPrefix.PublicKey) {
return (
<div className={classNames(layoutContext.showHeader ? "h-[calc(100dvh-44px)]" : "h-[calc(100dvh)]", "w-full")}>
<StreamPage link={link} evPreload={evPreload} />
</div>
);
} else if (link.kind === VIDEO_KIND) {
} else if (link.kind === VIDEO_KIND || link.kind === OLD_VIDEO_KIND) {
return <VideoPage link={link} evPreload={evPreload} />;
} else if (link.kind === SHORTS_KIND) {
} else if (link.kind === SHORTS_KIND || link.kind === OLD_SHORTS_KIND) {
return <ShortPage link={link} evPreload={evPreload} />;
} else {
return (

View File

@ -1,4 +1,4 @@
import { SHORTS_KIND } from "@/const";
import { OLD_SHORTS_KIND, SHORTS_KIND } from "@/const";
import VideoGrid from "@/element/video-grid";
import { VideoTile } from "@/element/video/video-tile";
import { findTag } from "@/utils";
@ -8,13 +8,12 @@ import { FormattedMessage } from "react-intl";
export function ShortsPage() {
const rb = new RequestBuilder("shorts");
rb.withFilter().kinds([SHORTS_KIND]);
rb.withFilter().kinds([SHORTS_KIND, OLD_SHORTS_KIND]);
const videos = useRequestBuilder(rb);
const sorted = videos.sort((a, b) => {
const pubA = findTag(a, "published_at");
const pubB = findTag(b, "published_at");
const pubA = findTag(a, "published_at") ?? a.created_at;
const pubB = findTag(b, "published_at") ?? b.created_at;
return Number(pubA) > Number(pubB) ? -1 : 1;
});

View File

@ -1,4 +1,4 @@
import { VIDEO_KIND } from "@/const";
import { SHORTS_KIND, VIDEO_KIND } from "@/const";
import { DefaultButton, IconButton, Layer3Button, PrimaryButton, WarningButton } from "@/element/buttons";
import { Icon } from "@/element/icon";
import Modal from "@/element/modal";
@ -9,11 +9,11 @@ import { ServerList } from "@/element/upload/server-list";
import useImgProxy from "@/hooks/img-proxy";
import { useLogin } from "@/hooks/login";
import { useMediaServerList } from "@/hooks/media-servers";
import { Nip94Tags, UploadResult, nip94TagsToIMeta } from "@/service/upload";
import { Nip94Tags, UploadResult, nip94TagsToIMeta, readNip94Tags } from "@/service/upload";
import { Nip96Server } from "@/service/upload/nip96";
import { openFile } from "@/utils";
import { ExternalStore, removeUndefined, unixNow, unwrap } from "@snort/shared";
import { EventPublisher, NostrLink } from "@snort/system";
import { ExternalStore, removeUndefined, unwrap } from "@snort/shared";
import { EventBuilder, EventPublisher, NostrEvent, NostrLink } from "@snort/system";
import { SnortContext } from "@snort/system-react";
import { useContext, useEffect, useState, useSyncExternalStore } from "react";
import { FormattedMessage, useIntl } from "react-intl";
@ -69,6 +69,23 @@ class UploadManager extends ExternalStore<Array<UploadStatus>> {
}
}
addUpload(server: string, file: NostrEvent, meta: Nip94Tags, type: UploadStatus["type"]) {
const name = file.content ?? meta.summary ?? meta.alt ?? "";
const uploadKey = `${name}:${server}:${type}`;
this.#uploads.set(uploadKey, {
type,
name,
size: meta.size ?? 0,
server,
result: {
url: meta.url,
header: file,
metadata: meta,
},
});
this.notifyChange();
}
async uploadTo(server: string, file: File, pub: EventPublisher, type: UploadStatus["type"]) {
let uploader = this.#uploaders.get(server);
if (!uploader) {
@ -130,6 +147,27 @@ class UploadManager extends ExternalStore<Array<UploadStatus>> {
return resGroup;
}
/**
* Gets the [min, max] duration from all variants
*/
duration() {
const uploads = this.snapshot();
return uploads.reduce(
(acc, v) => {
if (v.result?.metadata?.duration) {
if (acc[1] < v.result.metadata.duration) {
acc[1] = v.result.metadata.duration;
}
if (acc[0] > v.result.metadata.duration) {
acc[0] = v.result.metadata.duration;
}
}
return acc;
},
[1_000_000, 0],
);
}
/**
* Create the `imeta` tag for this upload
*/
@ -187,21 +225,24 @@ export function UploadPage() {
return error.length == 0 && uploads.length > 0 && uploads.every(a => a.result !== undefined);
}
function makeEvent() {
const duration = manager.duration();
const eb = new EventBuilder()
.pubKey(login?.pubkey ?? "00".repeat(31))
.kind(duration[1] <= 60 ? SHORTS_KIND : VIDEO_KIND)
.tag(["title", title])
.content(summary);
const imeta = manager.makeIMeta();
imeta.forEach(a => eb.tag(a));
return eb;
}
async function publish() {
const pub = login?.publisher();
if (!pub) return;
const ev = await pub.generic(eb => {
eb.kind(VIDEO_KIND);
eb.tag(["d", manager.id]);
eb.tag(["title", title]);
eb.tag(["published_at", unixNow().toString()]);
eb.content(summary);
const imeta = manager.makeIMeta();
imeta.forEach(a => eb.tag(a));
return eb;
});
const ev = await makeEvent().buildAndSign(pub.signer);
console.debug(ev);
await system.BroadcastEvent(ev);
navigate(`/${NostrLink.fromEvent(ev).encode()}`);
@ -229,7 +270,16 @@ export function UploadPage() {
const data = await rsp.blob();
const pub = login?.publisher();
if (pub) {
servers.servers.forEach(b => manager.uploadTo(b, new File([data], "thumb.jpg"), pub, "thumb"));
servers.servers.forEach(b =>
manager.uploadTo(
b,
new File([data], "thumb.jpg", {
type: "image/jpeg",
}),
pub,
"thumb",
),
);
}
}
}
@ -291,6 +341,17 @@ export function UploadPage() {
}),
);
}
const d = manager.duration();
if (d[0] === 0 || d[1] === 0) {
err.push(formatMessage({ defaultMessage: "No duration provided, please try another upload server." }));
}
if (Math.abs(d[0] - d[1]) >= 0.5) {
err.push(
formatMessage({
defaultMessage: "Video durations vary too much, are you sure each variant is the same video?",
}),
);
}
setError(err);
}
@ -312,6 +373,7 @@ export function UploadPage() {
</>
);
};
return (
<div className="max-xl:w-full xl:w-[1200px] xl:mx-auto grid gap-6 xl:grid-cols-[auto_350px] max-xl:px-4">
<div className="flex flex-col gap-6">
@ -359,8 +421,11 @@ export function UploadPage() {
))}
</div>
{videos > 0 && (
<div onClick={() => uploadFile()} className="cursor-pointer">
{uploadButton()}
<div className="flex flex-col gap-2">
<div className="text-xl">
<FormattedMessage defaultMessage="Add more content" />
</div>
<div className="flex gap-4 items-center">{uploadButton()}</div>
</div>
)}
{uploads.length > 0 && (
@ -377,7 +442,7 @@ export function UploadPage() {
<FormattedMessage defaultMessage="Thumbnail" />
</div>
<div className="border border-layer-3 border-dashed border-2 rounded-xl aspect-video overflow-hidden">
{thumb && <img src={proxy(thumb)} className="w-full h-full" />}
{thumb && <img src={proxy(thumb)} className="w-full h-full object-contain" />}
</div>
<div className="flex gap-4">
<DefaultButton onClick={() => uploadThumb()}>
@ -405,9 +470,13 @@ export function UploadPage() {
</WarningButton>
</div>
</div>
<pre className="text-xs font-mono overflow-wrap text-pretty">
{JSON.stringify(manager.makeIMeta(), undefined, 2)}
</pre>
<div>
<FormattedMessage defaultMessage="Raw Data:" />
<pre className="text-xs font-mono overflow-wrap text-pretty">
{JSON.stringify(makeEvent().build(), undefined, 2)}
</pre>
</div>
{editServers && (
<Modal id="server-list" onClose={() => setEditServers(false)}>
<ServerList />
@ -416,7 +485,19 @@ export function UploadPage() {
{mediaPicker && (
<Modal id="media-picker" onClose={() => setMediaPicker(false)} largeModal={true}>
<MediaServerFileList
onPicked={() => {
onPicked={files => {
files.forEach(f => {
const meta = readNip94Tags(f.tags);
if (meta.url) {
const url = new URL(meta.url);
manager.addUpload(
`${url.protocol}//${url.host}/`,
f,
meta,
meta.mimeType?.startsWith("image/") ?? false ? "thumb" : "video",
);
}
});
setMediaPicker(false);
}}
/>

View File

@ -1,4 +1,4 @@
import { VIDEO_KIND } from "@/const";
import { OLD_VIDEO_KIND, VIDEO_KIND } from "@/const";
import VideoGrid from "@/element/video-grid";
import { findTag, getHost } from "@/utils";
import { NostrLink, RequestBuilder } from "@snort/system";
@ -11,7 +11,7 @@ export function VideosPage() {
const login = useLogin();
const rb = new RequestBuilder("videos");
rb.withFilter().kinds([VIDEO_KIND]);
rb.withFilter().kinds([VIDEO_KIND, OLD_VIDEO_KIND]);
const videos = useRequestBuilder(rb);
@ -22,8 +22,8 @@ export function VideosPage() {
return (login?.state?.muted.length ?? 0) === 0 || !login?.state?.muted.some(a => a.equals(link));
})
.sort((a, b) => {
const pubA = findTag(a, "published_at");
const pubB = findTag(b, "published_at");
const pubA = findTag(a, "published_at") ?? a.created_at;
const pubB = findTag(b, "published_at") ?? b.created_at;
return Number(pubA) > Number(pubB) ? -1 : 1;
});

View File

@ -16,6 +16,8 @@ export interface Nip94Tags {
summary?: string;
alt?: string;
fallback?: Array<string>;
duration?: number;
bitrate?: number;
}
export interface UploadResult {
@ -103,6 +105,14 @@ export function readNip94Tags(tags: Array<Array<string>>) {
res.fallback.push(v);
break;
}
case "duration": {
res.duration = Number(v);
break;
}
case "bitrate": {
res.bitrate = Number(v);
break;
}
}
}
return res;
@ -126,6 +136,8 @@ export function nip94TagsToIMeta(meta: Nip94Tags) {
ifPush("thumb", meta.thumb);
ifPush("summary", meta.summary);
ifPush("alt", meta.alt);
ifPush("duration", meta.duration);
ifPush("bitrate", meta.bitrate);
if (meta.image) {
meta.image.forEach(a => ifPush("image", a));
}

View File

@ -37,7 +37,7 @@ export class Nip96Server {
const fd = new FormData();
fd.append("size", file.size.toString());
fd.append("caption", filename);
fd.append("media_type", file.type);
fd.append("content_type", file.type);
fd.append("file", file);
const rsp = await this.#req("", "POST", fd);

View File

@ -2,7 +2,6 @@ import { NostrEvent } from "@snort/system";
import { GameInfo } from "../game-database";
import { Nip94Tags, readNip94Tags, readNip94TagsFromIMeta } from "../upload";
import { getHost, sortStreamTags, extractGameTag, findTag } from "@/utils";
import { unwrap } from "@snort/shared";
export interface MediaPayload {
url: string;
@ -19,9 +18,12 @@ export class VideoInfo {
goal?: string;
gameId?: string;
gameInfo?: GameInfo;
duration?: number;
publishedAt?: number;
get duration() {
return this.media.find(m => m.duration)?.duration;
}
constructor(
readonly host: string,
readonly id: string,
@ -31,28 +33,13 @@ export class VideoInfo {
static parse(ev: NostrEvent) {
const { regularTags, prefixedTags } = sortStreamTags(ev.tags);
const ret = new VideoInfo(getHost(ev), unwrap(findTag(ev, "d")), regularTags, VideoInfo.#parseMediaTags(ev.tags));
const ret = new VideoInfo(getHost(ev), findTag(ev, "d") ?? ev.id, regularTags, VideoInfo.#parseMediaTags(ev.tags));
const matchInto = <K extends keyof VideoInfo>(
tag: Array<string>,
key: string,
into: K,
fn?: (v: string) => never,
) => {
if (tag[0] === key) {
ret[into] = fn ? fn(tag[1]) : (tag[1] as never);
}
};
for (const t of ev.tags) {
matchInto(t, "d", "id");
matchInto(t, "title", "title");
matchInto(t, "summary", "summary");
matchInto(t, "content-warning", "contentWarning");
matchInto(t, "goal", "goal");
matchInto(t, "duration", "duration");
matchInto(t, "published_at", "publishedAt");
}
ret.title = findTag(ev, "title");
ret.summary = findTag(ev, "summary") ?? ev.content;
ret.contentWarning = findTag(ev, "content-warning");
ret.goal = findTag(ev, "goal");
ret.publishedAt = Number(findTag(ev, "published_at") ?? ev.created_at);
const { gameInfo, gameId } = extractGameTag(prefixedTags);
ret.gameId = gameId;

View File

@ -73,6 +73,7 @@
"Axo/o5": "Science & Technology",
"AyGauy": "Login",
"BD0vyn": "{name} created a clip",
"BHNL+v": "Raw Data:",
"Bd1yEX": "New Stream Goal",
"Bep/gA": "Private key",
"BzQPM+": "Destination",
@ -211,6 +212,7 @@
"ZXp0z1": "Features",
"ZaNcK4": "No goals yet",
"ZcgtZo": "We've put together a list of easy-to-use exchanges that will allow you to buy a small amount of bitcoin (sats) and to transfer them to your wallet of choice.",
"Zfr//4": "No duration provided, please try another upload server.",
"ZmqxZs": "You can change this later",
"ZsYhvh": "Zaps are lightning payments, which are published on nostr as receipts.",
"Zse7yG": "Raid target",
@ -232,6 +234,7 @@
"dOQCL8": "Display name",
"dVD/AR": "Top Zappers",
"dkUMIH": "Clip by {name}",
"dqGkI+": "Video durations vary too much, are you sure each variant is the same video?",
"e011kf": "FAQ",
"ebmhes": "Nostr Extension",
"f6biFA": "Oh, and you have {n} sats of free streaming on us! 💜",
@ -311,6 +314,7 @@
"ug01Mk": "Time",
"uksRSi": "Latest Videos",
"vP4dFa": "Visit {link} to get some sweet zap.stream merch!",
"vaZKTn": "Add more content",
"vrTOHJ": "{amount} sats",
"w+2Vw7": "Shorts",
"w0Xm2F": "Start typing",

View File

@ -2573,14 +2573,14 @@ __metadata:
languageName: node
linkType: hard
"@snort/system-react@npm:^1.5.6":
version: 1.5.6
resolution: "@snort/system-react@npm:1.5.6"
"@snort/system-react@npm:^1.6.1":
version: 1.6.1
resolution: "@snort/system-react@npm:1.6.1"
dependencies:
"@snort/shared": "npm:^1.0.17"
"@snort/system": "npm:^1.5.6"
"@snort/system": "npm:^1.6.1"
react: "npm:^18.2.0"
checksum: 10c0/b7fb8dbc87328603b202dad9ce57dbff2cb8231829128b48346ba10925ee9fa5103652941b6a0456d6221fce8379a7148dc6507ebe8e1d0d35f7efd92738d08a
checksum: 10c0/fe3180c1f4341df4fd585c4031ef632a7d59c7637c6aed16ea254a1c22a66e288516d96939c0080efd0fb73940531855a59a24fae5f82c326f6544362eb0394c
languageName: node
linkType: hard
@ -2591,9 +2591,9 @@ __metadata:
languageName: node
linkType: hard
"@snort/system@npm:^1.5.6":
version: 1.5.6
resolution: "@snort/system@npm:1.5.6"
"@snort/system@npm:^1.6.1":
version: 1.6.1
resolution: "@snort/system@npm:1.6.1"
dependencies:
"@noble/ciphers": "npm:^0.6.0"
"@noble/curves": "npm:^1.4.0"
@ -2608,33 +2608,33 @@ __metadata:
nostr-social-graph: "npm:^1.0.3"
uuid: "npm:^9.0.0"
ws: "npm:^8.14.0"
checksum: 10c0/38fee2d55240f91a5e6ea0684a4bd94e6f4c56fab9b6a20d9ebef26dcdd17ed6c9a42bf1804c7bcf135a89c6882659baecede6d885dcead4a167d5e3337c9764
checksum: 10c0/5da01450970f4a51df98369c4cdd2b8886add3480e7962d11ed17498cf7db421bcd5e585abe532ff754efc0bf26b5734b9b24005f958802357ec390ccb74d281
languageName: node
linkType: hard
"@snort/wallet@npm:^0.2.1":
version: 0.2.1
resolution: "@snort/wallet@npm:0.2.1"
"@snort/wallet@npm:^0.2.4":
version: 0.2.4
resolution: "@snort/wallet@npm:0.2.4"
dependencies:
"@cashu/cashu-ts": "npm:^1.0.0-rc.3"
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
"@scure/base": "npm:^1.1.6"
"@snort/shared": "npm:^1.0.17"
"@snort/system": "npm:^1.5.6"
"@snort/system": "npm:^1.6.1"
debug: "npm:^4.3.4"
eventemitter3: "npm:^5.0.1"
checksum: 10c0/0dcf4b0336029e336bd6abcd7b79cf60d6fed08b2ab2847a8e791bb2399646e86c95aefc4dcfea08365c3dea417c362cdfa93939d7c36f037762de601936b331
checksum: 10c0/7d3e23d1d79595ee99e041b816cec5e7bfc6f34bfce6f0864a556de82cfcf29200f6978744b0717b5f6873c5c498e2dd9005571171f1f533f235cfbadf205a44
languageName: node
linkType: hard
"@snort/worker-relay@npm:^1.3.0":
version: 1.3.0
resolution: "@snort/worker-relay@npm:1.3.0"
"@snort/worker-relay@npm:^1.3.1":
version: 1.3.1
resolution: "@snort/worker-relay@npm:1.3.1"
dependencies:
"@sqlite.org/sqlite-wasm": "npm:^3.46.1-build3"
eventemitter3: "npm:^5.0.1"
uuid: "npm:^9.0.1"
checksum: 10c0/1a0eb175f50787bbcaa585641bf710347b59f3d3426cbf0f83182a5574bf7a63beb3e5d66bb41506e2d50c3ee904d55670c85c7f1542018936dd5a4ce06726e8
checksum: 10c0/19b89e4f96df425d2d73e87fda1f82844bf7f3a1ba114073d0bf4052c9d5fe3eac9e6ca6d88ad8e36b65bae6dfcf69db5cb47828ef1c195419a94bd87ae2ff53
languageName: node
linkType: hard
@ -7354,11 +7354,11 @@ __metadata:
"@noble/hashes": "npm:^1.4.0"
"@scure/base": "npm:^1.1.6"
"@snort/shared": "npm:^1.0.17"
"@snort/system": "npm:^1.5.6"
"@snort/system-react": "npm:^1.5.6"
"@snort/system": "npm:^1.6.1"
"@snort/system-react": "npm:^1.6.1"
"@snort/system-wasm": "npm:^1.0.5"
"@snort/wallet": "npm:^0.2.1"
"@snort/worker-relay": "npm:^1.3.0"
"@snort/wallet": "npm:^0.2.4"
"@snort/worker-relay": "npm:^1.3.1"
"@szhsin/react-menu": "npm:^4.1.0"
"@testing-library/dom": "npm:^9.3.1"
"@types/node": "npm:^20.12.12"