Merge pull request 'feat: cards' (#44) from cards into main
Reviewed-on: Kieran/stream#44
This commit is contained in:
commit
ba40302b5a
@ -34,6 +34,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-intersection-observer": "^9.5.1",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-router-dom": "^6.13.0",
|
||||
"react-tag-input-component": "^2.0.2",
|
||||
"semantic-sdp": "^3.26.2",
|
||||
|
@ -54,5 +54,8 @@
|
||||
<path d="M17.7084 10.1607C18.1683 13.3466 14.8705 14.0207 12.9733 13.9618C12.8515 13.958 12.7366 14.0173 12.6647 14.1157C12.4684 14.384 12.1547 14.7309 11.9125 14.7309C11.6405 14.7309 11.3957 15.254 11.284 15.5795C11.2723 15.6137 11.3059 15.6452 11.3403 15.634C14.345 14.6584 15.5241 14.3238 16.032 14.4178C16.4421 14.4937 17.209 15.8665 17.5413 16.5434C16.7155 16.5909 16.4402 15.8507 16.2503 15.7178C16.0985 15.6116 16.0415 16.0974 16.032 16.3536C15.8517 16.2587 15.6239 16.1259 15.6049 15.7178C15.5859 15.3098 15.3771 15.4142 15.2157 15.4332C15.0544 15.4521 12.5769 16.2493 12.2067 16.3536C11.8366 16.458 11.4094 16.6004 11.0582 16.8471C10.4697 17.1318 10.09 16.9325 9.98561 16.4485C9.90208 16.0614 10.4444 14.8701 10.726 14.3229C10.3779 14.4526 9.65529 14.7158 9.54898 14.7309C9.44588 14.7457 8.13815 15.7552 7.43879 16.3038C7.398 16.3358 7.37174 16.3827 7.36236 16.4336C7.25047 17.0416 6.89335 17.2118 6.27423 17.5303C5.77602 17.7867 4.036 20.4606 3.14127 21.9041C3.0794 22.0039 2.9886 22.0806 2.8911 22.1461C2.32279 22.5276 1.74399 23.4985 1.50923 23.9737C1.17511 23.0095 1.61048 22.1802 1.86993 21.886C1.75602 21.7873 1.49341 21.8449 1.37634 21.886C1.69907 20.7757 2.82862 20.7757 2.79066 20.7757C2.99948 20.5954 5.44842 17.0938 5.50538 16.9325C5.56187 16.7725 5.46892 16.0242 6.69975 15.6139C6.7193 15.6073 6.73868 15.5984 6.75601 15.5873C7.71493 14.971 8.43427 13.9774 8.67571 13.5542C7.39547 13.4662 5.92943 12.7525 5.16289 12.294C4.99765 12.1952 4.8224 12.1092 4.63108 12.0875C3.58154 11.9687 2.53067 12.6401 2.10723 13.0228C1.93258 12.7799 2.12938 12.0739 2.24961 11.7513C1.82437 11.6905 1.19916 12.308 0.939711 12.6243C0.658747 12.184 0.904907 11.397 1.06311 11.0585C0.501179 11.0737 0.120232 11.3306 0 11.4571C0.465109 7.99343 4.02275 9.00076 4.06259 9.04675C3.87275 8.84937 3.88857 8.59126 3.92021 8.48688C6.0749 8.54381 7.08105 8.18321 7.71702 7.81313C12.7288 5.01374 14.8882 6.73133 15.6856 7.1631C16.4829 7.59487 17.9304 7.77042 18.9318 7.37187C20.1278 6.83097 19.9478 5.43673 19.7054 4.90461C19.4397 4.32101 17.9399 3.51438 17.4084 2.49428C16.8768 1.47418 17.34 0.233672 17.9558 0.0607684C18.5425 -0.103972 18.9615 0.0876835 19.2831 0.378128C19.4974 0.571763 20.0994 0.710259 20.3509 0.800409C20.6024 0.890558 21.0201 1.00918 20.9964 1.08035C20.9726 1.15152 20.5699 1.14202 20.5075 1.14202C20.3794 1.14202 20.2275 1.161 20.3794 1.23217C20.5575 1.30439 20.8263 1.40936 20.955 1.47846C20.9717 1.48744 20.9683 1.51084 20.95 1.51577C20.0765 1.75085 19.2966 1.26578 18.7183 1.82526C18.1298 2.39463 19.3827 2.83114 20.0282 3.51438C20.6736 4.19762 21.3381 5.01372 20.8065 6.87365C20.395 8.31355 18.6703 9.53781 17.7795 10.0167C17.7282 10.0442 17.7001 10.1031 17.7084 10.1607Z" fill="currentColor"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="plus" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 5V19M5 12H19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@ -2,4 +2,8 @@ import { EventKind } from "@snort/system";
|
||||
|
||||
export const LIVE_STREAM = 30_311 as EventKind;
|
||||
export const LIVE_STREAM_CHAT = 1_311 as EventKind;
|
||||
export const EMOJI_PACK = 30_030 as EventKind;
|
||||
export const USER_EMOJIS = 10_030 as EventKind;
|
||||
export const GOAL = 9041 as EventKind;
|
||||
export const USER_CARDS = 17_777 as EventKind;
|
||||
export const CARD = 37_777 as EventKind;
|
||||
|
19
src/element/Address.tsx
Normal file
19
src/element/Address.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { type NostrLink } from "@snort/system";
|
||||
|
||||
import { useEvent } from "hooks/event";
|
||||
import { EMOJI_PACK } from "const";
|
||||
import { EmojiPack } from "element/emoji-pack";
|
||||
|
||||
interface AddressProps {
|
||||
link: NostrLink;
|
||||
}
|
||||
|
||||
export function Address({ link }: AddressProps) {
|
||||
const event = useEvent(link);
|
||||
|
||||
if (event?.kind === EMOJI_PACK) {
|
||||
return <EmojiPack ev={event} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
33
src/element/Event.tsx
Normal file
33
src/element/Event.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import "./event.css";
|
||||
|
||||
import { type NostrLink, EventKind } from "@snort/system";
|
||||
import { useEvent } from "hooks/event";
|
||||
import { GOAL } from "const";
|
||||
import { Goal } from "element/goal";
|
||||
import { Note } from "element/note";
|
||||
|
||||
interface EventProps {
|
||||
link: NostrLink;
|
||||
}
|
||||
|
||||
export function Event({ link }: EventProps) {
|
||||
const event = useEvent(link);
|
||||
|
||||
if (event && event.kind === GOAL) {
|
||||
return (
|
||||
<div className="event-container">
|
||||
<Goal ev={event} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (event && event.kind === EventKind.TextNote) {
|
||||
return (
|
||||
<div className="event-container">
|
||||
<Note ev={event} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <code>{link.id}</code>;
|
||||
}
|
0
src/element/address.css
Normal file
0
src/element/address.css
Normal file
@ -58,7 +58,7 @@ export function ChatMessage({
|
||||
const login = useLogin();
|
||||
const profile = useUserProfile(
|
||||
System,
|
||||
inView?.isIntersecting ? ev.pubkey : undefined
|
||||
inView?.isIntersecting ? ev.pubkey : undefined,
|
||||
);
|
||||
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
||||
const zaps = useMemo(() => {
|
||||
@ -178,16 +178,16 @@ export function ChatMessage({
|
||||
style={
|
||||
isTablet
|
||||
? {
|
||||
display: showZapDialog || isHovering ? "flex" : "none",
|
||||
}
|
||||
display: showZapDialog || isHovering ? "flex" : "none",
|
||||
}
|
||||
: {
|
||||
position: "fixed",
|
||||
top: topOffset ? topOffset - 12 : 0,
|
||||
left: leftOffset ? leftOffset - 32 : 0,
|
||||
opacity: showZapDialog || isHovering ? 1 : 0,
|
||||
pointerEvents:
|
||||
showZapDialog || isHovering ? "auto" : "none",
|
||||
}
|
||||
position: "fixed",
|
||||
top: topOffset ? topOffset - 12 : 0,
|
||||
left: leftOffset ? leftOffset - 32 : 0,
|
||||
opacity: showZapDialog || isHovering ? 1 : 0,
|
||||
pointerEvents:
|
||||
showZapDialog || isHovering ? "auto" : "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
{zapTarget && (
|
||||
|
32
src/element/emoji-pack.css
Normal file
32
src/element/emoji-pack.css
Normal file
@ -0,0 +1,32 @@
|
||||
.emoji-pack-title {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.emoji-pack-title a {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.emoji-pack-emojis {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.emoji-definition {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.emoji-name {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.emoji-pack h4 {
|
||||
margin: 0;
|
||||
}
|
29
src/element/emoji-pack.tsx
Normal file
29
src/element/emoji-pack.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import "./emoji-pack.css";
|
||||
import { type NostrEvent } from "@snort/system";
|
||||
|
||||
import { Mention } from "element/mention";
|
||||
import { findTag } from "utils";
|
||||
|
||||
export function EmojiPack({ ev }: { ev: NostrEvent }) {
|
||||
const name = findTag(ev, "d");
|
||||
const emoji = ev.tags.filter((e) => e.at(0) === "emoji");
|
||||
return (
|
||||
<div className="emoji-pack">
|
||||
<div className="emoji-pack-title">
|
||||
<h4>{name}</h4>
|
||||
<Mention pubkey={ev.pubkey} />
|
||||
</div>
|
||||
<div className="emoji-pack-emojis">
|
||||
{emoji.map((e) => {
|
||||
const [, name, image] = e;
|
||||
return (
|
||||
<div className="emoji-definition">
|
||||
<img alt={name} className="emoji" src={image} />
|
||||
<span className="emoji-name">{name}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
8
src/element/event.css
Normal file
8
src/element/event.css
Normal file
@ -0,0 +1,8 @@
|
||||
.event-container .goal {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.event-container .goal .amount {
|
||||
top: -8px;
|
||||
font-size: 10px;
|
||||
}
|
7
src/element/external-link.tsx
Normal file
7
src/element/external-link.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export function ExternalLink({ children, href }) {
|
||||
return (
|
||||
<a href={href} rel="noopener noreferrer" target="_blank">
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}
|
27
src/element/file-uploader.css
Normal file
27
src/element/file-uploader.css
Normal file
@ -0,0 +1,27 @@
|
||||
.file-uploader-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.file-uploader input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-uploader {
|
||||
align-self: flex-start;
|
||||
background: white;
|
||||
color: black;
|
||||
max-width: 100px;
|
||||
border-radius: 10px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
width: 82px;
|
||||
height: 60px;
|
||||
border-radius: 10px;
|
||||
}
|
75
src/element/file-uploader.tsx
Normal file
75
src/element/file-uploader.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import "./file-uploader.css";
|
||||
import { VoidApi } from "@void-cat/api";
|
||||
import { useState } from "react";
|
||||
|
||||
const voidCatHost = "https://void.cat";
|
||||
const fileExtensionRegex = /\.([\w]{1,7})$/i;
|
||||
const voidCatApi = new VoidApi(voidCatHost);
|
||||
|
||||
type UploadResult = {
|
||||
url?: string;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
async function voidCatUpload(file: File | Blob): Promise<UploadResult> {
|
||||
const uploader = voidCatApi.getUploader(file);
|
||||
|
||||
const rsp = await uploader.upload({
|
||||
"V-Strip-Metadata": "true",
|
||||
});
|
||||
if (rsp.ok) {
|
||||
let ext = file.name.match(fileExtensionRegex);
|
||||
if (rsp.file?.metadata?.mimeType === "image/webp") {
|
||||
ext = ["", "webp"];
|
||||
}
|
||||
const resultUrl =
|
||||
rsp.file?.metadata?.url ??
|
||||
`${voidCatHost}/d/${rsp.file?.id}${ext ? `.${ext[1]}` : ""}`;
|
||||
|
||||
const ret = {
|
||||
url: resultUrl,
|
||||
} as UploadResult;
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
return {
|
||||
error: rsp.errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function FileUploader({ onFileUpload }) {
|
||||
const [img, setImg] = useState();
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
async function onFileChange(ev) {
|
||||
const file = ev.target.files[0];
|
||||
if (file) {
|
||||
try {
|
||||
setIsUploading(true);
|
||||
const upload = await voidCatUpload(file);
|
||||
if (upload.url) {
|
||||
setImg(upload.url);
|
||||
onFileUpload(upload.url);
|
||||
}
|
||||
if (upload.error) {
|
||||
console.error(upload.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="file-uploader-container">
|
||||
<label className="file-uploader">
|
||||
<input type="file" onChange={onFileChange} />
|
||||
{isUploading ? "Uploading..." : "Add File"}
|
||||
</label>
|
||||
{img && <img className="image-preview" src={img} />}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -13,8 +13,8 @@ export function LoggedInFollowButton({
|
||||
}) {
|
||||
const login = useLogin();
|
||||
const following = useFollows(loggedIn, true);
|
||||
const { tags, relays } = following ? following : { tags: [], relays: {} }
|
||||
const follows = tags.filter((t) => t.at(0) === "p")
|
||||
const { tags, relays } = following ? following : { tags: [], relays: {} };
|
||||
const follows = tags.filter((t) => t.at(0) === "p");
|
||||
const isFollowing = follows.find((t) => t.at(1) === pubkey);
|
||||
|
||||
async function unfollow() {
|
||||
@ -23,7 +23,7 @@ export function LoggedInFollowButton({
|
||||
const ev = await pub.generic((eb) => {
|
||||
eb.kind(EventKind.ContactList).content(JSON.stringify(relays));
|
||||
for (const t of tags) {
|
||||
const isFollow = t.at(0) === "p" && t.at(1) === pubkey
|
||||
const isFollow = t.at(0) === "p" && t.at(1) === pubkey;
|
||||
if (!isFollow) {
|
||||
eb.tag(t);
|
||||
}
|
||||
|
@ -2,19 +2,23 @@ import "./goal.css";
|
||||
import { useMemo } from "react";
|
||||
import * as Progress from "@radix-ui/react-progress";
|
||||
import Confetti from "react-confetti";
|
||||
import { ParsedZap, NostrEvent } from "@snort/system";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
import { type NostrEvent } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { findTag } from "utils";
|
||||
import { formatSats } from "number";
|
||||
import usePreviousValue from "hooks/usePreviousValue";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { useZaps } from "hooks/goals";
|
||||
import { getName } from "element/profile";
|
||||
import { System } from "index";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
export function Goal({
|
||||
ev,
|
||||
zaps,
|
||||
}: {
|
||||
ev: NostrEvent;
|
||||
zaps: ParsedZap[];
|
||||
}) {
|
||||
export function Goal({ ev }: { ev: NostrEvent }) {
|
||||
const profile = useUserProfile(System, ev.pubkey);
|
||||
const zapTarget = profile?.lud16 ?? profile?.lud06;
|
||||
const zaps = useZaps(ev, true);
|
||||
const goalAmount = useMemo(() => {
|
||||
const amount = findTag(ev, "amount");
|
||||
return amount ? Number(amount) / 1000 : null;
|
||||
@ -34,8 +38,8 @@ export function Goal({
|
||||
const isFinished = progress >= 100;
|
||||
const previousValue = usePreviousValue(isFinished);
|
||||
|
||||
return (
|
||||
<div className="goal">
|
||||
const goalContent = (
|
||||
<div className="goal" style={{ cursor: zapTarget ? "pointer" : "auto" }}>
|
||||
{ev.content.length > 0 && <p>{ev.content}</p>}
|
||||
<div className={`progress-container ${isFinished ? "finished" : ""}`}>
|
||||
<Progress.Root className="progress-root" value={progress}>
|
||||
@ -61,4 +65,16 @@ export function Goal({
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return zapTarget ? (
|
||||
<SendZapsDialog
|
||||
lnurl={zapTarget}
|
||||
pubkey={ev.pubkey}
|
||||
eTag={ev?.id}
|
||||
targetName={getName(ev.pubkey, profile)}
|
||||
button={goalContent}
|
||||
/>
|
||||
) : (
|
||||
goalContent
|
||||
);
|
||||
}
|
||||
|
@ -1,25 +1,63 @@
|
||||
import { NostrLink } from "./nostr-link";
|
||||
|
||||
const FileExtensionRegex = /\.([\w]+)$/i;
|
||||
|
||||
interface HyperTextProps {
|
||||
link: string;
|
||||
}
|
||||
|
||||
export function HyperText({ link }: HyperTextProps) {
|
||||
export function HyperText({ link, children }: HyperTextProps) {
|
||||
try {
|
||||
const url = new URL(link);
|
||||
if (url.protocol === "nostr:" || url.protocol === "web+nostr:") {
|
||||
const extension =
|
||||
FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
|
||||
|
||||
if (extension) {
|
||||
switch (extension) {
|
||||
case "gif":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "webp": {
|
||||
return (
|
||||
<img
|
||||
src={url.toString()}
|
||||
alt={url.toString()}
|
||||
objectFit="contain"
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "wav":
|
||||
case "mp3":
|
||||
case "ogg": {
|
||||
return <audio key={url.toString()} src={url.toString()} controls />;
|
||||
}
|
||||
case "mp4":
|
||||
case "mov":
|
||||
case "mkv":
|
||||
case "avi":
|
||||
case "m4v":
|
||||
case "webm": {
|
||||
return <video key={url.toString()} src={url.toString()} controls />;
|
||||
}
|
||||
default:
|
||||
return <a href={url.toString()}>{children || url.toString()}</a>;
|
||||
}
|
||||
} else if (url.protocol === "nostr:" || url.protocol === "web+nostr:") {
|
||||
return <NostrLink link={link} />;
|
||||
} else {
|
||||
<a href={link} target="_blank" rel="noreferrer">
|
||||
{link}
|
||||
{children}
|
||||
</a>;
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
// Ignore the error.
|
||||
}
|
||||
return (
|
||||
<a href={link} target="_blank" rel="noreferrer">
|
||||
{link}
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -309,12 +309,6 @@
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.message-reaction .emoji {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.zap-pill-amount {
|
||||
text-transform: lowercase;
|
||||
color: #FFF;
|
||||
@ -347,4 +341,4 @@
|
||||
|
||||
.write-emoji-button:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
@ -88,20 +88,9 @@ export function LiveChat({
|
||||
const zaps = feed.zaps
|
||||
.map((ev) => parseZap(ev, System.ProfileLoader.Cache))
|
||||
.filter((z) => z && z.valid);
|
||||
|
||||
const goalZaps = feed.zaps
|
||||
.filter((ev) =>
|
||||
goal
|
||||
? ev.created_at > goal.created_at &&
|
||||
ev.tags.some((t) => t[0] === "e" && t[1] === goal.id)
|
||||
: false
|
||||
)
|
||||
.map((ev) => parseZap(ev, System.ProfileLoader.Cache))
|
||||
.filter((z) => z && z.valid);
|
||||
|
||||
const events = useMemo(() => {
|
||||
return [...feed.messages, ...feed.zaps].sort(
|
||||
(a, b) => b.created_at - a.created_at
|
||||
(a, b) => b.created_at - a.created_at,
|
||||
);
|
||||
}, [feed.messages, feed.zaps]);
|
||||
const streamer = getHost(ev);
|
||||
@ -112,7 +101,7 @@ export function LiveChat({
|
||||
findTag(ev, "d") ?? "",
|
||||
undefined,
|
||||
ev.kind,
|
||||
ev.pubkey
|
||||
ev.pubkey,
|
||||
);
|
||||
}
|
||||
}, [ev]);
|
||||
@ -125,7 +114,13 @@ export function LiveChat({
|
||||
<Icon
|
||||
name="link"
|
||||
size={32}
|
||||
onClick={() => window.open(`/chat/${naddr}?chat=true`, "_blank", "popup,width=400,height=800")}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`/chat/${naddr}?chat=true`,
|
||||
"_blank",
|
||||
"popup,width=400,height=800",
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -135,7 +130,7 @@ export function LiveChat({
|
||||
<div className="top-zappers-container">
|
||||
<TopZappers zaps={zaps} />
|
||||
</div>
|
||||
{goal && <Goal ev={goal} zaps={goalZaps} />}
|
||||
{goal && <Goal ev={goal} />}
|
||||
{login?.pubkey === streamer && <NewGoalDialog link={link} />}
|
||||
</div>
|
||||
)}
|
||||
@ -155,7 +150,7 @@ export function LiveChat({
|
||||
}
|
||||
case EventKind.ZapReceipt: {
|
||||
const zap = zaps.find(
|
||||
(b) => b.id === a.id && b.receiver === streamer
|
||||
(b) => b.id === a.id && b.receiver === streamer,
|
||||
);
|
||||
if (zap) {
|
||||
return <ChatZap zap={zap} key={a.id} />;
|
||||
@ -179,7 +174,7 @@ export function LiveChat({
|
||||
);
|
||||
}
|
||||
|
||||
const BIG_ZAP_THRESHOLD = 100_000;
|
||||
const BIG_ZAP_THRESHOLD = 50_000;
|
||||
|
||||
function ChatZap({ zap }: { zap: ParsedZap }) {
|
||||
if (!zap.valid) {
|
||||
@ -202,7 +197,7 @@ function ChatZap({ zap }: { zap: ParsedZap }) {
|
||||
<span className="zap-amount">{formatSats(zap.amount)}</span>
|
||||
sats
|
||||
</div>
|
||||
{zap.content && (
|
||||
{zap.content && (
|
||||
<div className="zap-content">
|
||||
<Text content={zap.content} tags={[]} />
|
||||
</div>
|
||||
|
24
src/element/markdown.css
Normal file
24
src/element/markdown.css
Normal file
@ -0,0 +1,24 @@
|
||||
.markdown a {
|
||||
color: var(--text-link);
|
||||
}
|
||||
|
||||
.markdown > ul, .markdown > ol {
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 29px;
|
||||
}
|
||||
|
||||
.markdown > p {
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
overflow-wrap: break-word;
|
||||
font-weight: 400;
|
||||
line-height: 29px; /* 161.111% */
|
||||
}
|
||||
|
||||
.markdown > img {
|
||||
max-height: 230px;
|
||||
width: 100%;
|
||||
}
|
216
src/element/markdown.tsx
Normal file
216
src/element/markdown.tsx
Normal file
@ -0,0 +1,216 @@
|
||||
import "./markdown.css";
|
||||
|
||||
import { parseNostrLink } from "@snort/system";
|
||||
import type { ReactNode } from "react";
|
||||
import { useMemo } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
||||
import { Address } from "element/Address";
|
||||
import { Event } from "element/Event";
|
||||
import { Mention } from "element/mention";
|
||||
import { Emoji } from "element/emoji";
|
||||
import { HyperText } from "element/hypertext";
|
||||
|
||||
const MentionRegex = /(#\[\d+\])/gi;
|
||||
const NostrPrefixRegex = /^nostr:/;
|
||||
const EmojiRegex = /:([\w-]+):/g;
|
||||
|
||||
function extractEmoji(fragments: Fragment[], tags: string[][]) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(EmojiRegex).map((i) => {
|
||||
const t = tags.find((a) => a[0] === "emoji" && a[1] === i);
|
||||
if (t) {
|
||||
return <Emoji name={t[1]} url={t[2]} />;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractMentions(fragments, tags) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(MentionRegex).map((match) => {
|
||||
const matchTag = match.match(/#\[(\d+)\]/);
|
||||
if (matchTag && matchTag.length === 2) {
|
||||
const idx = parseInt(matchTag[1]);
|
||||
const ref = tags?.find((a, i) => i === idx);
|
||||
if (ref) {
|
||||
switch (ref[0]) {
|
||||
case "p": {
|
||||
return <Mention key={ref[1]} pubkey={ref[1]} />;
|
||||
}
|
||||
case "a": {
|
||||
return <Address link={parseNostrLink(ref[1])} />;
|
||||
}
|
||||
default:
|
||||
// todo: e and t mentions
|
||||
return ref[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractNprofiles(fragments) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(/(nostr:nprofile1[a-z0-9]+)/g).map((i) => {
|
||||
if (i.startsWith("nostr:nprofile1")) {
|
||||
try {
|
||||
const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
|
||||
return <Mention key={link.id} pubkey={link.id} />;
|
||||
} catch (error) {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractNpubs(fragments) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(/(nostr:npub1[a-z0-9]+)/g).map((i) => {
|
||||
if (i.startsWith("nostr:npub1")) {
|
||||
try {
|
||||
const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
|
||||
return <Mention key={link.id} pubkey={link.id} />;
|
||||
} catch (error) {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractNevents(fragments) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(/(nostr:nevent1[a-z0-9]+)/g).map((i) => {
|
||||
if (i.startsWith("nostr:nevent1")) {
|
||||
try {
|
||||
const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
|
||||
return <Event link={link} />;
|
||||
} catch (error) {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractNaddrs(fragments) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(/(nostr:naddr1[a-z0-9]+)/g).map((i) => {
|
||||
if (i.startsWith("nostr:naddr1")) {
|
||||
try {
|
||||
const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
|
||||
return <Address key={i} link={link} />;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractNoteIds(fragments) {
|
||||
return fragments
|
||||
.map((f) => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(/(nostr:note1[a-z0-9]+)/g).map((i) => {
|
||||
if (i.startsWith("nostr:note1")) {
|
||||
try {
|
||||
const link = parseNostrLink(i.replace(NostrPrefixRegex, ""));
|
||||
return <Event link={link} />;
|
||||
} catch (error) {
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function transformText(ps, tags) {
|
||||
let fragments = extractMentions(ps, tags);
|
||||
fragments = extractNprofiles(fragments);
|
||||
fragments = extractNevents(fragments);
|
||||
fragments = extractNaddrs(fragments);
|
||||
fragments = extractNoteIds(fragments);
|
||||
fragments = extractNpubs(fragments);
|
||||
fragments = extractEmoji(fragments, tags);
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
interface MarkdownProps {
|
||||
children: ReactNode;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export function Markdown({ children, tags = [] }: MarkdownProps) {
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
li: ({ children, ...props }) => {
|
||||
return children && <li {...props}>{transformText(children, tags)}</li>;
|
||||
},
|
||||
td: ({ children }) =>
|
||||
children && <td>{transformText(children, tags)}</td>,
|
||||
p: ({ children }) => children && <p>{transformText(children, tags)}</p>,
|
||||
a: (props) => {
|
||||
return <HyperText link={props.href}>{props.children}</HyperText>;
|
||||
},
|
||||
};
|
||||
}, [tags]);
|
||||
return (
|
||||
<div className="markdown">
|
||||
<ReactMarkdown children={children} components={components} />
|
||||
</div>
|
||||
);
|
||||
}
|
22
src/element/note.css
Normal file
22
src/element/note.css
Normal file
@ -0,0 +1,22 @@
|
||||
.note {
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.note .note-header .profile {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.note .note-avatar {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.note .note-content {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.note .note-content .markdown > p {
|
||||
font-size: 14px;
|
||||
}
|
18
src/element/note.tsx
Normal file
18
src/element/note.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import "./note.css";
|
||||
import { type NostrEvent } from "@snort/system";
|
||||
|
||||
import { Markdown } from "element/markdown";
|
||||
import { Profile } from "element/profile";
|
||||
|
||||
export function Note({ ev }: { ev: NostrEvent }) {
|
||||
return (
|
||||
<div className="note">
|
||||
<div className="note-header">
|
||||
<Profile avatarClassname="note-avatar" pubkey={ev.pubkey} />
|
||||
</div>
|
||||
<div className="note-content">
|
||||
<Markdown tags={ev.tags}>{ev.content}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
128
src/element/stream-cards.css
Normal file
128
src/element/stream-cards.css
Normal file
@ -0,0 +1,128 @@
|
||||
.stream-cards {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1020px) {
|
||||
.stream-cards {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 24px;
|
||||
margin-top: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.card-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.editor-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stream-card {
|
||||
display: flex;
|
||||
align-self: flex-start;
|
||||
flex-direction: column;
|
||||
padding: 20px 24px;
|
||||
gap: 16px;
|
||||
border-radius: 24px;
|
||||
background: #111;
|
||||
width: 210px;
|
||||
}
|
||||
|
||||
.stream-card .card-title {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
@media (min-width: 1900px) {
|
||||
.stream-card {
|
||||
width: 342px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-card {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.add-card .add-icon {
|
||||
color: #797979;
|
||||
cursor: pointer;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.new-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.new-card h3 {
|
||||
margin: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.new-card input[type="text"] {
|
||||
background: #262626;
|
||||
padding: 8px 16px;
|
||||
border-radius: 16px;
|
||||
width: unset;
|
||||
margin-bottom: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
|
||||
.new-card textarea {
|
||||
width: unset;
|
||||
background: #262626;
|
||||
padding: 8px 16px;
|
||||
border-radius: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-control label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.new-card-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.help-text a {
|
||||
color: var(--text-link);
|
||||
}
|
||||
|
||||
.add-button {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
background: transparent;
|
||||
color: var(--text-danger);
|
||||
}
|
305
src/element/stream-cards.tsx
Normal file
305
src/element/stream-cards.tsx
Normal file
@ -0,0 +1,305 @@
|
||||
import "./stream-cards.css";
|
||||
|
||||
import { useState } from "react";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
|
||||
import type { NostrEvent } from "@snort/system";
|
||||
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useCards } from "hooks/cards";
|
||||
import { CARD, USER_CARDS } from "const";
|
||||
import { toTag } from "utils";
|
||||
import { System } from "index";
|
||||
import { findTag } from "utils";
|
||||
import { Icon } from "./icon";
|
||||
import { ExternalLink } from "./external-link";
|
||||
import { FileUploader } from "./file-uploader";
|
||||
import { Markdown } from "./markdown";
|
||||
|
||||
interface CardType {
|
||||
identifier?: string;
|
||||
title?: string;
|
||||
image?: string;
|
||||
link?: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface CardProps {
|
||||
canEdit?: boolean;
|
||||
ev: NostrEvent;
|
||||
cards: NostrEvent[];
|
||||
}
|
||||
|
||||
function Card({ canEdit, ev, cards }: CardProps) {
|
||||
const identifier = findTag(ev, "d");
|
||||
const title = findTag(ev, "title") || findTag(ev, "subject");
|
||||
const image = findTag(ev, "image");
|
||||
const link = findTag(ev, "r");
|
||||
const evCard = { title, image, link, content: ev.content, identifier };
|
||||
|
||||
const card = (
|
||||
<>
|
||||
<div className="stream-card">
|
||||
{title && <h1 className="card-title">{title}</h1>}
|
||||
{image && <img src={image} alt={title} />}
|
||||
<Markdown children={ev.content} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
const editor = canEdit && (
|
||||
<div className="editor-buttons">
|
||||
<EditCard card={evCard} />
|
||||
<DeleteCard card={ev} cards={cards} />
|
||||
</div>
|
||||
);
|
||||
return link && !canEdit ? (
|
||||
<div className="card-container">
|
||||
<ExternalLink href={link}>{card}</ExternalLink>
|
||||
{editor}
|
||||
</div>
|
||||
) : (
|
||||
<div className="card-container">
|
||||
{card}
|
||||
{editor}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CardDialogProps {
|
||||
header?: string;
|
||||
cta?: string;
|
||||
card?: CardType;
|
||||
onSave(ev: CardType): void;
|
||||
onCancel(): void;
|
||||
}
|
||||
|
||||
function CardDialog({ header, cta, card, onSave, onCancel }: CardDialogProps) {
|
||||
const [title, setTitle] = useState(card?.title ?? "");
|
||||
const [image, setImage] = useState(card?.image ?? "");
|
||||
const [content, setContent] = useState(card?.content ?? "");
|
||||
const [link, setLink] = useState(card?.link ?? "");
|
||||
|
||||
return (
|
||||
<div className="new-card">
|
||||
<h3>{header || "Add card"}</h3>
|
||||
<div className="form-control">
|
||||
<label for="card-title">Title</label>
|
||||
<input
|
||||
id="card-title"
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="e.g. about me"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label for="card-image">Image</label>
|
||||
<FileUploader onFileUpload={setImage} />
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label for="card-image-link">Link</label>
|
||||
<input
|
||||
id="card-image-link"
|
||||
type="text"
|
||||
placeholder="https://"
|
||||
value={link}
|
||||
onChange={(e) => setLink(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<label for="card-content">Content</label>
|
||||
<textarea
|
||||
placeholder="Start typing..."
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
/>
|
||||
<span className="help-text">
|
||||
Supports{" "}
|
||||
<ExternalLink href="https://www.markdownguide.org/cheat-sheet">
|
||||
Markdown
|
||||
</ExternalLink>
|
||||
</span>
|
||||
</div>
|
||||
<div className="new-card-buttons">
|
||||
<button
|
||||
className="btn btn-primary add-button"
|
||||
onClick={() => onSave({ title, image, content, link })}
|
||||
>
|
||||
{cta || "Add Card"}
|
||||
</button>
|
||||
<button className="btn delete-button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface EditCardProps {
|
||||
card: CardType;
|
||||
}
|
||||
|
||||
function EditCard({ card }: EditCardProps) {
|
||||
const login = useLogin();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
async function editCard({ title, image, link, content }) {
|
||||
const pub = login?.publisher();
|
||||
if (pub) {
|
||||
const ev = await pub.generic((eb) => {
|
||||
eb.kind(CARD).content(content).tag(["d", card.identifier]);
|
||||
if (title?.length > 0) {
|
||||
eb.tag(["title", title]);
|
||||
}
|
||||
if (image?.length > 0) {
|
||||
eb.tag(["image", image]);
|
||||
}
|
||||
if (link?.lenght > 0) {
|
||||
eb.tag(["r", link]);
|
||||
}
|
||||
return eb;
|
||||
});
|
||||
console.debug(ev);
|
||||
System.BroadcastEvent(ev);
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||
<Dialog.Trigger asChild>
|
||||
<button className="btn btn-primary">Edit</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="dialog-overlay" />
|
||||
<Dialog.Content className="dialog-content">
|
||||
<CardDialog
|
||||
header="Edit card"
|
||||
cta="Save Card"
|
||||
card={card}
|
||||
onSave={editCard}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
}
|
||||
|
||||
interface DeleteCardProps {
|
||||
card: NostrEvent;
|
||||
cards: NostrEvent[];
|
||||
}
|
||||
|
||||
function DeleteCard({ card, cards }: DeleteCardProps) {
|
||||
const login = useLogin();
|
||||
const tags = cards.map(toTag);
|
||||
|
||||
async function deleteCard() {
|
||||
const pub = login?.publisher();
|
||||
if (pub) {
|
||||
const userCardsEv = await pub.generic((eb) => {
|
||||
eb.kind(USER_CARDS).content("");
|
||||
for (const tag of tags) {
|
||||
if (tag.at(1) !== toTag(card).at(1)) {
|
||||
eb.tag(tag);
|
||||
}
|
||||
}
|
||||
return eb;
|
||||
});
|
||||
|
||||
console.log(userCardsEv);
|
||||
|
||||
System.BroadcastEvent(userCardsEv);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button className="btn delete-button" onClick={deleteCard}>
|
||||
Delete
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
interface AddCardProps {
|
||||
cards: NostrEvent[];
|
||||
}
|
||||
|
||||
function AddCard({ cards }: AddCardProps) {
|
||||
const login = useLogin();
|
||||
const tags = cards.map(toTag);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
async function createCard({ title, image, link, content }) {
|
||||
const pub = login?.publisher();
|
||||
if (pub) {
|
||||
const ev = await pub.generic((eb) => {
|
||||
const d = String(Date.now());
|
||||
eb.kind(CARD).content(content).tag(["d", d]);
|
||||
if (title?.length > 0) {
|
||||
eb.tag(["title", title]);
|
||||
}
|
||||
if (image?.length > 0) {
|
||||
eb.tag(["image", image]);
|
||||
}
|
||||
if (link?.length > 0) {
|
||||
eb.tag(["r", link]);
|
||||
}
|
||||
return eb;
|
||||
});
|
||||
const userCardsEv = await pub.generic((eb) => {
|
||||
eb.kind(USER_CARDS).content("");
|
||||
for (const tag of tags) {
|
||||
eb.tag(tag);
|
||||
}
|
||||
eb.tag(toTag(ev));
|
||||
return eb;
|
||||
});
|
||||
|
||||
console.debug(ev);
|
||||
console.debug(userCardsEv);
|
||||
|
||||
System.BroadcastEvent(ev);
|
||||
System.BroadcastEvent(userCardsEv);
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="stream-card add-card">
|
||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||
<Dialog.Trigger asChild>
|
||||
<Icon name="plus" className="add-icon" />
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="dialog-overlay" />
|
||||
<Dialog.Content className="dialog-content">
|
||||
<CardDialog onSave={createCard} onCancel={onCancel} />
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function StreamCards({ host }) {
|
||||
const login = useLogin();
|
||||
const canEdit = login?.pubkey === host;
|
||||
const cards = useCards(host, canEdit);
|
||||
return (
|
||||
<div className="stream-cards">
|
||||
{cards.map((ev) => (
|
||||
<Card canEdit={canEdit} cards={cards} key={ev.id} ev={ev} />
|
||||
))}
|
||||
{canEdit && <AddCard cards={cards} />}
|
||||
</div>
|
||||
);
|
||||
}
|
86
src/hooks/cards.ts
Normal file
86
src/hooks/cards.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import {
|
||||
ReplaceableNoteStore,
|
||||
NoteCollection,
|
||||
RequestBuilder,
|
||||
} from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { USER_CARDS, CARD } from "const";
|
||||
import { findTag } from "utils";
|
||||
import { System } from "index";
|
||||
|
||||
export function useCards(pubkey: string, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`user-cards:${pubkey.slice(0, 12)}`);
|
||||
b.withOptions({
|
||||
leaveOpen,
|
||||
})
|
||||
.withFilter()
|
||||
.authors([pubkey])
|
||||
.kinds([USER_CARDS]);
|
||||
return b;
|
||||
}, [pubkey, leaveOpen]);
|
||||
|
||||
const { data: userCards } = useRequestBuilder<ReplaceableNoteStore>(
|
||||
System,
|
||||
ReplaceableNoteStore,
|
||||
sub,
|
||||
);
|
||||
|
||||
const related = useMemo(() => {
|
||||
// filtering to only show CARD kinds for now, but in the future we could link and render anything
|
||||
if (userCards) {
|
||||
return userCards.tags.filter(
|
||||
(t) => t.at(0) === "a" && t.at(1)?.startsWith(`${CARD}:`),
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}, [userCards]);
|
||||
|
||||
const subRelated = useMemo(() => {
|
||||
if (!pubkey) return null;
|
||||
const splitted = related.map((t) => t.at(1)!.split(":"));
|
||||
const authors = splitted
|
||||
.map((s) => s.at(1))
|
||||
.filter((s) => s)
|
||||
.map((s) => s as string);
|
||||
const identifiers = splitted
|
||||
.map((s) => s.at(2))
|
||||
.filter((s) => s)
|
||||
.map((s) => s as string);
|
||||
|
||||
const rb = new RequestBuilder(`cards:${pubkey}`);
|
||||
rb.withOptions({ leaveOpen })
|
||||
.withFilter()
|
||||
.kinds([CARD])
|
||||
.authors(authors)
|
||||
.tag("d", identifiers);
|
||||
|
||||
return rb;
|
||||
}, [pubkey, related]);
|
||||
|
||||
const { data } = useRequestBuilder<NoteCollection>(
|
||||
System,
|
||||
NoteCollection,
|
||||
subRelated,
|
||||
);
|
||||
|
||||
const cards = useMemo(() => {
|
||||
return related
|
||||
.map((t) => {
|
||||
const [k, pubkey, identifier] = t.at(1).split(":");
|
||||
const kind = Number(k);
|
||||
return data.find(
|
||||
(e) =>
|
||||
e.kind === kind &&
|
||||
e.pubkey === pubkey &&
|
||||
findTag(e, "d") === identifier,
|
||||
);
|
||||
})
|
||||
.filter((e) => e);
|
||||
}, [related, data]);
|
||||
|
||||
return cards;
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
RequestBuilder,
|
||||
EventKind,
|
||||
ReplaceableNoteStore,
|
||||
NoteCollection,
|
||||
NostrEvent,
|
||||
@ -9,6 +8,7 @@ import { useRequestBuilder } from "@snort/system-react";
|
||||
import { System } from "index";
|
||||
import { useMemo } from "react";
|
||||
import { findTag } from "utils";
|
||||
import { EMOJI_PACK, USER_EMOJIS } from "const";
|
||||
import type { EmojiTag } from "../element/emoji";
|
||||
import uniqBy from "lodash.uniqby";
|
||||
|
||||
@ -49,9 +49,7 @@ export default function useEmoji(pubkey?: string) {
|
||||
if (!pubkey) return null;
|
||||
const rb = new RequestBuilder(`emoji:${pubkey}`);
|
||||
|
||||
rb.withFilter()
|
||||
.authors([pubkey])
|
||||
.kinds([10030 as EventKind]);
|
||||
rb.withFilter().authors([pubkey]).kinds([USER_EMOJIS]);
|
||||
|
||||
return rb;
|
||||
}, [pubkey]);
|
||||
@ -59,13 +57,13 @@ export default function useEmoji(pubkey?: string) {
|
||||
const { data: userEmoji } = useRequestBuilder<ReplaceableNoteStore>(
|
||||
System,
|
||||
ReplaceableNoteStore,
|
||||
sub
|
||||
sub,
|
||||
);
|
||||
|
||||
const related = useMemo(() => {
|
||||
if (userEmoji) {
|
||||
return userEmoji.tags.filter(
|
||||
(t) => t.at(0) === "a" && t.at(1)?.startsWith(`30030:`)
|
||||
(t) => t.at(0) === "a" && t.at(1)?.startsWith(`${EMOJI_PACK}:`),
|
||||
);
|
||||
}
|
||||
return [];
|
||||
@ -85,14 +83,9 @@ export default function useEmoji(pubkey?: string) {
|
||||
|
||||
const rb = new RequestBuilder(`emoji-related:${pubkey}`);
|
||||
|
||||
rb.withFilter()
|
||||
.kinds([30030 as EventKind])
|
||||
.authors(authors)
|
||||
.tag("d", identifiers);
|
||||
rb.withFilter().kinds([EMOJI_PACK]).authors(authors).tag("d", identifiers);
|
||||
|
||||
rb.withFilter()
|
||||
.kinds([30030 as EventKind])
|
||||
.authors([pubkey]);
|
||||
rb.withFilter().kinds([EMOJI_PACK]).authors([pubkey]);
|
||||
|
||||
return rb;
|
||||
}, [pubkey, related]);
|
||||
@ -100,7 +93,7 @@ export default function useEmoji(pubkey?: string) {
|
||||
const { data: relatedData } = useRequestBuilder<NoteCollection>(
|
||||
System,
|
||||
NoteCollection,
|
||||
subRelated
|
||||
subRelated,
|
||||
);
|
||||
|
||||
const emojiPacks = useMemo(() => {
|
||||
|
43
src/hooks/event.ts
Normal file
43
src/hooks/event.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import {
|
||||
NostrPrefix,
|
||||
ReplaceableNoteStore,
|
||||
RequestBuilder,
|
||||
type NostrLink,
|
||||
} from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
|
||||
import { System } from "index";
|
||||
|
||||
export function useEvent(link: NostrLink) {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`event:${link.id.slice(0, 12)}`);
|
||||
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 {
|
||||
const f = b.withFilter().ids([link.id]);
|
||||
if (link.relays) {
|
||||
link.relays.slice(0, 2).forEach((r) => f.relay(r));
|
||||
}
|
||||
if (link.author) {
|
||||
f.authors([link.author]);
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}, [link]);
|
||||
|
||||
const { data } = useRequestBuilder<ReplaceableNoteStore>(
|
||||
System,
|
||||
ReplaceableNoteStore,
|
||||
sub,
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
@ -1,13 +1,37 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
EventKind,
|
||||
NostrEvent,
|
||||
RequestBuilder,
|
||||
NoteCollection,
|
||||
ReplaceableNoteStore,
|
||||
NostrLink,
|
||||
parseZap,
|
||||
} from "@snort/system";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { GOAL } from "const";
|
||||
import { System } from "index";
|
||||
|
||||
export function useZaps(goal: NostrEvent, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`goal-zaps:${goal.id.slice(0, 12)}`);
|
||||
b.withOptions({ leaveOpen });
|
||||
b.withFilter()
|
||||
.kinds([EventKind.ZapReceipt])
|
||||
.tag("e", [goal.id])
|
||||
.since(goal.created_at);
|
||||
return b;
|
||||
}, [goal, leaveOpen]);
|
||||
|
||||
const { data } = useRequestBuilder<NoteCollection>(
|
||||
System,
|
||||
NoteCollection,
|
||||
sub,
|
||||
);
|
||||
|
||||
return data?.map((ev) => parseZap(ev, System.ProfileLoader.Cache)).filter((z) => z && z.valid) ?? [];
|
||||
}
|
||||
|
||||
export function useZapGoal(host: string, link: NostrLink, leaveOpen = false) {
|
||||
const sub = useMemo(() => {
|
||||
const b = new RequestBuilder(`goals:${host.slice(0, 12)}`);
|
||||
@ -22,7 +46,7 @@ export function useZapGoal(host: string, link: NostrLink, leaveOpen = false) {
|
||||
const { data } = useRequestBuilder<ReplaceableNoteStore>(
|
||||
System,
|
||||
ReplaceableNoteStore,
|
||||
sub
|
||||
sub,
|
||||
);
|
||||
|
||||
return data;
|
||||
|
@ -12,6 +12,10 @@ body {
|
||||
--gap-m: 24px;
|
||||
--gap-s: 16px;
|
||||
--header-height: 48px;
|
||||
--text-muted: #797979;
|
||||
--text-link: #F838D9;
|
||||
--text-danger: #FF563F;
|
||||
--border: #333;
|
||||
}
|
||||
|
||||
@media(max-width: 1020px) {
|
||||
@ -267,3 +271,9 @@ div.paper {
|
||||
.szh-menu__item--hover {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ export function ProfilePage() {
|
||||
}, [streams]);
|
||||
const futureStreams = useMemo(() => {
|
||||
return streams.filter(
|
||||
(ev) => findTag(ev, "status") === StreamState.Planned
|
||||
(ev) => findTag(ev, "status") === StreamState.Planned,
|
||||
);
|
||||
}, [streams]);
|
||||
const isLive = Boolean(liveEvent);
|
||||
@ -75,7 +75,7 @@ export function ProfilePage() {
|
||||
d,
|
||||
undefined,
|
||||
liveEvent.kind,
|
||||
liveEvent.pubkey
|
||||
liveEvent.pubkey,
|
||||
);
|
||||
navigate(`/${naddr}`);
|
||||
}
|
||||
@ -114,7 +114,7 @@ export function ProfilePage() {
|
||||
liveEvent
|
||||
? `${liveEvent.kind}:${liveEvent.pubkey}:${findTag(
|
||||
liveEvent,
|
||||
"d"
|
||||
"d",
|
||||
)}`
|
||||
: undefined
|
||||
}
|
||||
@ -171,7 +171,7 @@ export function ProfilePage() {
|
||||
<span className="timestamp">
|
||||
Streamed on{" "}
|
||||
{moment(Number(ev.created_at) * 1000).format(
|
||||
"MMM DD, YYYY"
|
||||
"MMM DD, YYYY",
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@ -186,7 +186,7 @@ export function ProfilePage() {
|
||||
<span className="timestamp">
|
||||
Scheduled for{" "}
|
||||
{moment(Number(ev.created_at) * 1000).format(
|
||||
"MMM DD, YYYY h:mm:ss a"
|
||||
"MMM DD, YYYY h:mm:ss a",
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -18,6 +18,7 @@ import { useUserProfile } from "@snort/system-react";
|
||||
import { NewStreamDialog } from "element/new-stream";
|
||||
import { Tags } from "element/tags";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { StreamCards } from "element/stream-cards";
|
||||
import { formatSats } from "number";
|
||||
import { StreamTimer } from "element/stream-time";
|
||||
import { ShareMenu } from "element/share-menu";
|
||||
@ -135,6 +136,7 @@ export function StreamPage() {
|
||||
<div className="video-content">
|
||||
<LiveVideoPlayer stream={stream} poster={image} status={status} />
|
||||
<ProfileInfo ev={ev} goal={goal} />
|
||||
<StreamCards host={host} />
|
||||
</div>
|
||||
<LiveChat link={link} ev={ev} goal={goal} />
|
||||
</div>
|
||||
|
26
src/utils.ts
26
src/utils.ts
@ -2,6 +2,20 @@ import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
|
||||
import * as utils from "@noble/curves/abstract/utils";
|
||||
import { bech32 } from "@scure/base";
|
||||
|
||||
export function toTag(e: NostrEvent): string[] {
|
||||
if (e.kind && e.kind >= 30000 && e.kind <= 40000) {
|
||||
const dTag = findTag(e, "d");
|
||||
|
||||
return ["a", `${e.kind}:${e.pubkey}:${dTag}`];
|
||||
}
|
||||
|
||||
if (e.kind === 0 || e.kind === 3) {
|
||||
return ["p", e.pubkey];
|
||||
}
|
||||
|
||||
return ["e", e.id];
|
||||
}
|
||||
|
||||
export function findTag(e: NostrEvent | undefined, tag: string) {
|
||||
const maybeTag = e?.tags.find((evTag) => {
|
||||
return evTag[0] === tag;
|
||||
@ -48,17 +62,21 @@ export function eventLink(ev: NostrEvent) {
|
||||
d,
|
||||
undefined,
|
||||
ev.kind,
|
||||
ev.pubkey
|
||||
ev.pubkey,
|
||||
);
|
||||
return `/${naddr}`;
|
||||
}
|
||||
|
||||
export function getHost(ev?: NostrEvent) {
|
||||
return ev?.tags.find(a => a[0] === "p" && a[3] === "host")?.[1] ?? ev?.pubkey ?? "";
|
||||
return (
|
||||
ev?.tags.find((a) => a[0] === "p" && a[3] === "host")?.[1] ??
|
||||
ev?.pubkey ??
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
export async function openFile(): Promise<File | undefined> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
const elm = document.createElement("input");
|
||||
elm.type = "file";
|
||||
elm.onchange = (e: Event) => {
|
||||
@ -71,4 +89,4 @@ export async function openFile(): Promise<File | undefined> {
|
||||
};
|
||||
elm.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
629
yarn.lock
629
yarn.lock
@ -2775,6 +2775,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/debug@npm:^4.0.0":
|
||||
version: 4.1.8
|
||||
resolution: "@types/debug@npm:4.1.8"
|
||||
dependencies:
|
||||
"@types/ms": "*"
|
||||
checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/eslint-scope@npm:^3.7.3":
|
||||
version: 3.7.4
|
||||
resolution: "@types/eslint-scope@npm:3.7.4"
|
||||
@ -2833,6 +2842,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/hast@npm:^2.0.0":
|
||||
version: 2.3.5
|
||||
resolution: "@types/hast@npm:2.3.5"
|
||||
dependencies:
|
||||
"@types/unist": ^2
|
||||
checksum: e435e9fbf6afc616ade377d2246a632fb75f4064be4bfd619b67a1ba0d9935d75968a18fbdb66535dfb5e77ef81f4b9b56fd8f35c1cffa34b48ddb0287fec91e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/html-minifier-terser@npm:^6.0.0":
|
||||
version: 6.1.0
|
||||
resolution: "@types/html-minifier-terser@npm:6.1.0"
|
||||
@ -2914,6 +2932,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/mdast@npm:^3.0.0":
|
||||
version: 3.0.12
|
||||
resolution: "@types/mdast@npm:3.0.12"
|
||||
dependencies:
|
||||
"@types/unist": ^2
|
||||
checksum: 83adb8679b9d139f69f63554d120af921e9f1289e9903a2c99e0554a327c8524a6c0beccdc0721e4fdbccc606e81964fecb0d390d53df0f74360938e22f1a469
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/mime@npm:*":
|
||||
version: 3.0.1
|
||||
resolution: "@types/mime@npm:3.0.1"
|
||||
@ -2928,6 +2955,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/ms@npm:*":
|
||||
version: 0.7.31
|
||||
resolution: "@types/ms@npm:0.7.31"
|
||||
checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*":
|
||||
version: 20.3.1
|
||||
resolution: "@types/node@npm:20.3.1"
|
||||
@ -2949,7 +2983,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/prop-types@npm:*":
|
||||
"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0":
|
||||
version: 15.7.5
|
||||
resolution: "@types/prop-types@npm:15.7.5"
|
||||
checksum: 5b43b8b15415e1f298243165f1d44390403bb2bd42e662bca3b5b5633fdd39c938e91b7fce3a9483699db0f7a715d08cef220c121f723a634972fdf596aec980
|
||||
@ -3110,6 +3144,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/unist@npm:^2, @types/unist@npm:^2.0.0":
|
||||
version: 2.0.7
|
||||
resolution: "@types/unist@npm:2.0.7"
|
||||
checksum: b97a219554e83431f19a93ff113306bf0512909292815e8f32964e47d041c505af1aaa2a381c23e137c4c0b962fad58d4ce9c5c3256642921a466be43c1fc715
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/webscopeio__react-textarea-autocomplete@npm:^4.7.2":
|
||||
version: 4.7.2
|
||||
resolution: "@types/webscopeio__react-textarea-autocomplete@npm:4.7.2"
|
||||
@ -3912,6 +3953,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bail@npm:^2.0.0":
|
||||
version: 2.0.2
|
||||
resolution: "bail@npm:2.0.2"
|
||||
checksum: aab4e8ccdc8d762bf3fdfce8e706601695620c0c2eda256dd85088dc0be3cfd7ff126f6e99c2bee1f24f5d418414aacf09d7f9702f16d6963df2fa488cda8824
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"balanced-match@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "balanced-match@npm:1.0.2"
|
||||
@ -4156,6 +4204,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"character-entities@npm:^2.0.0":
|
||||
version: 2.0.2
|
||||
resolution: "character-entities@npm:2.0.2"
|
||||
checksum: cf1643814023697f725e47328fcec17923b8f1799102a8a79c1514e894815651794a2bffd84bb1b3a4b124b050154e4529ed6e81f7c8068a734aecf07a6d3def
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chokidar@npm:^3.5.3":
|
||||
version: 3.5.3
|
||||
resolution: "chokidar@npm:3.5.3"
|
||||
@ -4278,6 +4333,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"comma-separated-tokens@npm:^2.0.0":
|
||||
version: 2.0.3
|
||||
resolution: "comma-separated-tokens@npm:2.0.3"
|
||||
checksum: e3bf9e0332a5c45f49b90e79bcdb4a7a85f28d6a6f0876a94f1bb9b2bfbdbbb9292aac50e1e742d8c0db1e62a0229a106f57917e2d067fca951d81737651700d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^10.0.1":
|
||||
version: 10.0.1
|
||||
resolution: "commander@npm:10.0.1"
|
||||
@ -4669,7 +4731,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4":
|
||||
"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4":
|
||||
version: 4.3.4
|
||||
resolution: "debug@npm:4.3.4"
|
||||
dependencies:
|
||||
@ -4681,6 +4743,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decode-named-character-reference@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "decode-named-character-reference@npm:1.0.2"
|
||||
dependencies:
|
||||
character-entities: ^2.0.0
|
||||
checksum: f4c71d3b93105f20076052f9cb1523a22a9c796b8296cd35eef1ca54239c78d182c136a848b83ff8da2071e3ae2b1d300bf29d00650a6d6e675438cc31b11d78
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-equal@npm:^2.0.5":
|
||||
version: 2.2.1
|
||||
resolution: "deep-equal@npm:2.2.1"
|
||||
@ -4768,7 +4839,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dequal@npm:^2.0.3":
|
||||
"dequal@npm:^2.0.0, dequal@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "dequal@npm:2.0.3"
|
||||
checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90
|
||||
@ -4810,6 +4881,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"diff@npm:^5.0.0":
|
||||
version: 5.1.0
|
||||
resolution: "diff@npm:5.1.0"
|
||||
checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dir-glob@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "dir-glob@npm:3.0.1"
|
||||
@ -5464,6 +5542,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"extend@npm:^3.0.0":
|
||||
version: 3.0.2
|
||||
resolution: "extend@npm:3.0.2"
|
||||
checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "fast-deep-equal@npm:3.1.3"
|
||||
@ -6025,6 +6110,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hast-util-whitespace@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "hast-util-whitespace@npm:2.0.1"
|
||||
checksum: 431be6b2f35472f951615540d7a53f69f39461e5e080c0190268bdeb2be9ab9b1dddfd1f467dd26c1de7e7952df67beb1307b6ee940baf78b24a71b5e0663868
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"he@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "he@npm:1.2.0"
|
||||
@ -6324,6 +6416,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inline-style-parser@npm:0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "inline-style-parser@npm:0.1.1"
|
||||
checksum: 5d545056a3e1f2bf864c928a886a0e1656a3517127d36917b973de581bd54adc91b4bf1febcb0da054f204b4934763f1a4e09308b4d55002327cf1d48ac5d966
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "internal-slot@npm:1.0.5"
|
||||
@ -6421,6 +6520,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-buffer@npm:^2.0.0":
|
||||
version: 2.0.5
|
||||
resolution: "is-buffer@npm:2.0.5"
|
||||
checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7":
|
||||
version: 1.2.7
|
||||
resolution: "is-callable@npm:1.2.7"
|
||||
@ -6543,6 +6649,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-plain-obj@npm:^4.0.0":
|
||||
version: 4.1.0
|
||||
resolution: "is-plain-obj@npm:4.1.0"
|
||||
checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-plain-object@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "is-plain-object@npm:2.0.4"
|
||||
@ -6932,6 +7045,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"kleur@npm:^4.0.3":
|
||||
version: 4.1.5
|
||||
resolution: "kleur@npm:4.1.5"
|
||||
checksum: 1dc476e32741acf0b1b5b0627ffd0d722e342c1b0da14de3e8ae97821327ca08f9fb944542fb3c126d90ac5f27f9d804edbe7c585bf7d12ef495d115e0f22c12
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"launch-editor@npm:^2.6.0":
|
||||
version: 2.6.0
|
||||
resolution: "launch-editor@npm:2.6.0"
|
||||
@ -7160,6 +7280,62 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-definitions@npm:^5.0.0":
|
||||
version: 5.1.2
|
||||
resolution: "mdast-util-definitions@npm:5.1.2"
|
||||
dependencies:
|
||||
"@types/mdast": ^3.0.0
|
||||
"@types/unist": ^2.0.0
|
||||
unist-util-visit: ^4.0.0
|
||||
checksum: 2544daccab744ea1ede76045c2577ae4f1cc1b9eb1ea51ab273fe1dca8db5a8d6f50f87759c0ce6484975914b144b7f40316f805cb9c86223a78db8de0b77bae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-from-markdown@npm:^1.0.0":
|
||||
version: 1.3.1
|
||||
resolution: "mdast-util-from-markdown@npm:1.3.1"
|
||||
dependencies:
|
||||
"@types/mdast": ^3.0.0
|
||||
"@types/unist": ^2.0.0
|
||||
decode-named-character-reference: ^1.0.0
|
||||
mdast-util-to-string: ^3.1.0
|
||||
micromark: ^3.0.0
|
||||
micromark-util-decode-numeric-character-reference: ^1.0.0
|
||||
micromark-util-decode-string: ^1.0.0
|
||||
micromark-util-normalize-identifier: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
unist-util-stringify-position: ^3.0.0
|
||||
uvu: ^0.5.0
|
||||
checksum: c2fac225167e248d394332a4ea39596e04cbde07d8cdb3889e91e48972c4c3462a02b39fda3855345d90231eb17a90ac6e082fb4f012a77c1d0ddfb9c7446940
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-to-hast@npm:^12.1.0":
|
||||
version: 12.3.0
|
||||
resolution: "mdast-util-to-hast@npm:12.3.0"
|
||||
dependencies:
|
||||
"@types/hast": ^2.0.0
|
||||
"@types/mdast": ^3.0.0
|
||||
mdast-util-definitions: ^5.0.0
|
||||
micromark-util-sanitize-uri: ^1.1.0
|
||||
trim-lines: ^3.0.0
|
||||
unist-util-generated: ^2.0.0
|
||||
unist-util-position: ^4.0.0
|
||||
unist-util-visit: ^4.0.0
|
||||
checksum: ea40c9f07dd0b731754434e81c913590c611b1fd753fa02550a1492aadfc30fb3adecaf62345ebb03cea2ddd250c15ab6e578fffde69c19955c9b87b10f2a9bb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-to-string@npm:^3.1.0":
|
||||
version: 3.2.0
|
||||
resolution: "mdast-util-to-string@npm:3.2.0"
|
||||
dependencies:
|
||||
"@types/mdast": ^3.0.0
|
||||
checksum: dc40b544d54339878ae2c9f2b3198c029e1e07291d2126bd00ca28272ee6616d0d2194eb1c9828a7c34d412a79a7e73b26512a734698d891c710a1e73db1e848
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdn-data@npm:2.0.28":
|
||||
version: 2.0.28
|
||||
resolution: "mdn-data@npm:2.0.28"
|
||||
@ -7218,6 +7394,242 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-core-commonmark@npm:^1.0.1":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-core-commonmark@npm:1.1.0"
|
||||
dependencies:
|
||||
decode-named-character-reference: ^1.0.0
|
||||
micromark-factory-destination: ^1.0.0
|
||||
micromark-factory-label: ^1.0.0
|
||||
micromark-factory-space: ^1.0.0
|
||||
micromark-factory-title: ^1.0.0
|
||||
micromark-factory-whitespace: ^1.0.0
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-chunked: ^1.0.0
|
||||
micromark-util-classify-character: ^1.0.0
|
||||
micromark-util-html-tag-name: ^1.0.0
|
||||
micromark-util-normalize-identifier: ^1.0.0
|
||||
micromark-util-resolve-all: ^1.0.0
|
||||
micromark-util-subtokenize: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.1
|
||||
uvu: ^0.5.0
|
||||
checksum: c6dfedc95889cc73411cb222fc2330b9eda6d849c09c9fd9eb3cd3398af246167e9d3cdb0ae3ce9ae59dd34a14624c8330e380255d41279ad7350cf6c6be6c5b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-factory-destination@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-factory-destination@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: 9e2b5fb5fedbf622b687e20d51eb3d56ae90c0e7ecc19b37bd5285ec392c1e56f6e21aa7cfcb3c01eda88df88fe528f3acb91a5f57d7f4cba310bc3cd7f824fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-factory-label@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-factory-label@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
uvu: ^0.5.0
|
||||
checksum: fcda48f1287d9b148c562c627418a2ab759cdeae9c8e017910a0cba94bb759a96611e1fc6df33182e97d28fbf191475237298983bb89ef07d5b02464b1ad28d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-factory-space@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-factory-space@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: b58435076b998a7e244259a4694eb83c78915581206b6e7fc07b34c6abd36a1726ade63df8972fbf6c8fa38eecb9074f4e17be8d53f942e3b3d23d1a0ecaa941
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-factory-title@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-factory-title@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-factory-space: ^1.0.0
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: 4432d3dbc828c81f483c5901b0c6591a85d65a9e33f7d96ba7c3ae821617a0b3237ff5faf53a9152d00aaf9afb3a9f185b205590f40ed754f1d9232e0e9157b1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-factory-whitespace@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-factory-whitespace@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-factory-space: ^1.0.0
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: ef0fa682c7d593d85a514ee329809dee27d10bc2a2b65217d8ef81173e33b8e83c549049764b1ad851adfe0a204dec5450d9d20a4ca8598f6c94533a73f73fcd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-character@npm:^1.0.0":
|
||||
version: 1.2.0
|
||||
resolution: "micromark-util-character@npm:1.2.0"
|
||||
dependencies:
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: 089e79162a19b4a28731736246579ab7e9482ac93cd681c2bfca9983dcff659212ef158a66a5957e9d4b1dba957d1b87b565d85418a5b009f0294f1f07f2aaac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-chunked@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-chunked@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-symbol: ^1.0.0
|
||||
checksum: c435bde9110cb595e3c61b7f54c2dc28ee03e6a57fa0fc1e67e498ad8bac61ee5a7457a2b6a73022ddc585676ede4b912d28dcf57eb3bd6951e54015e14dc20b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-classify-character@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-classify-character@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: 8499cb0bb1f7fb946f5896285fcca65cd742f66cd3e79ba7744792bd413ec46834f932a286de650349914d02e822946df3b55d03e6a8e1d245d1ddbd5102e5b0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-combine-extensions@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-combine-extensions@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-chunked: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: ee78464f5d4b61ccb437850cd2d7da4d690b260bca4ca7a79c4bb70291b84f83988159e373b167181b6716cb197e309bc6e6c96a68cc3ba9d50c13652774aba9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-decode-numeric-character-reference@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-decode-numeric-character-reference@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-symbol: ^1.0.0
|
||||
checksum: 4733fe75146e37611243f055fc6847137b66f0cde74d080e33bd26d0408c1d6f44cabc984063eee5968b133cb46855e729d555b9ff8d744652262b7b51feec73
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-decode-string@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-decode-string@npm:1.1.0"
|
||||
dependencies:
|
||||
decode-named-character-reference: ^1.0.0
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-decode-numeric-character-reference: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
checksum: f1625155db452f15aa472918499689ba086b9c49d1322a08b22bfbcabe918c61b230a3002c8bc3ea9b1f52ca7a9bb1c3dd43ccb548c7f5f8b16c24a1ae77a813
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-encode@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-encode@npm:1.1.0"
|
||||
checksum: 4ef29d02b12336918cea6782fa87c8c578c67463925221d4e42183a706bde07f4b8b5f9a5e1c7ce8c73bb5a98b261acd3238fecd152e6dd1cdfa2d1ae11b60a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-html-tag-name@npm:^1.0.0":
|
||||
version: 1.2.0
|
||||
resolution: "micromark-util-html-tag-name@npm:1.2.0"
|
||||
checksum: ccf0fa99b5c58676dc5192c74665a3bfd1b536fafaf94723bd7f31f96979d589992df6fcf2862eba290ef18e6a8efb30ec8e1e910d9f3fc74f208871e9f84750
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-normalize-identifier@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-normalize-identifier@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-symbol: ^1.0.0
|
||||
checksum: 8655bea41ffa4333e03fc22462cb42d631bbef9c3c07b625fd852b7eb442a110f9d2e5902a42e65188d85498279569502bf92f3434a1180fc06f7c37edfbaee2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-resolve-all@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-resolve-all@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-types: ^1.0.0
|
||||
checksum: 1ce6c0237cd3ca061e76fae6602cf95014e764a91be1b9f10d36cb0f21ca88f9a07de8d49ab8101efd0b140a4fbfda6a1efb72027ab3f4d5b54c9543271dc52c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-sanitize-uri@npm:^1.0.0, micromark-util-sanitize-uri@npm:^1.1.0":
|
||||
version: 1.2.0
|
||||
resolution: "micromark-util-sanitize-uri@npm:1.2.0"
|
||||
dependencies:
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-encode: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
checksum: 6663f365c4fe3961d622a580f4a61e34867450697f6806f027f21cf63c92989494895fcebe2345d52e249fe58a35be56e223a9776d084c9287818b40c779acc1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-subtokenize@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-subtokenize@npm:1.1.0"
|
||||
dependencies:
|
||||
micromark-util-chunked: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.0
|
||||
uvu: ^0.5.0
|
||||
checksum: 4a9d780c4d62910e196ea4fd886dc4079d8e424e5d625c0820016da0ed399a281daff39c50f9288045cc4bcd90ab47647e5396aba500f0853105d70dc8b1fc45
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-symbol@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-symbol@npm:1.1.0"
|
||||
checksum: 02414a753b79f67ff3276b517eeac87913aea6c028f3e668a19ea0fc09d98aea9f93d6222a76ca783d20299af9e4b8e7c797fe516b766185dcc6e93290f11f88
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1":
|
||||
version: 1.1.0
|
||||
resolution: "micromark-util-types@npm:1.1.0"
|
||||
checksum: b0ef2b4b9589f15aec2666690477a6a185536927ceb7aa55a0f46475852e012d75a1ab945187e5c7841969a842892164b15d58ff8316b8e0d6cc920cabd5ede7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromark@npm:^3.0.0":
|
||||
version: 3.2.0
|
||||
resolution: "micromark@npm:3.2.0"
|
||||
dependencies:
|
||||
"@types/debug": ^4.0.0
|
||||
debug: ^4.0.0
|
||||
decode-named-character-reference: ^1.0.0
|
||||
micromark-core-commonmark: ^1.0.1
|
||||
micromark-factory-space: ^1.0.0
|
||||
micromark-util-character: ^1.0.0
|
||||
micromark-util-chunked: ^1.0.0
|
||||
micromark-util-combine-extensions: ^1.0.0
|
||||
micromark-util-decode-numeric-character-reference: ^1.0.0
|
||||
micromark-util-encode: ^1.0.0
|
||||
micromark-util-normalize-identifier: ^1.0.0
|
||||
micromark-util-resolve-all: ^1.0.0
|
||||
micromark-util-sanitize-uri: ^1.0.0
|
||||
micromark-util-subtokenize: ^1.0.0
|
||||
micromark-util-symbol: ^1.0.0
|
||||
micromark-util-types: ^1.0.1
|
||||
uvu: ^0.5.0
|
||||
checksum: 56c15851ad3eb8301aede65603473443e50c92a54849cac1dadd57e4ec33ab03a0a77f3df03de47133e6e8f695dae83b759b514586193269e98c0bf319ecd5e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5":
|
||||
version: 4.0.5
|
||||
resolution: "micromatch@npm:4.0.5"
|
||||
@ -7412,6 +7824,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mri@npm:^1.1.0":
|
||||
version: 1.2.0
|
||||
resolution: "mri@npm:1.2.0"
|
||||
checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mrmime@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "mrmime@npm:1.0.1"
|
||||
@ -8321,7 +8740,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
|
||||
"prop-types@npm:^15.0.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
|
||||
version: 15.8.1
|
||||
resolution: "prop-types@npm:15.8.1"
|
||||
dependencies:
|
||||
@ -8332,6 +8751,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"property-information@npm:^6.0.0":
|
||||
version: 6.2.0
|
||||
resolution: "property-information@npm:6.2.0"
|
||||
checksum: 23afce07ba821cbe7d926e63cdd680991961c82be4bbb6c0b17c47f48894359c1be6e51cd74485fc10a9d3fd361b475388e1e39311ed2b53127718f72aab1955
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proxy-addr@npm:~2.0.7":
|
||||
version: 2.0.7
|
||||
resolution: "proxy-addr@npm:2.0.7"
|
||||
@ -8483,6 +8909,32 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-markdown@npm:^8.0.7":
|
||||
version: 8.0.7
|
||||
resolution: "react-markdown@npm:8.0.7"
|
||||
dependencies:
|
||||
"@types/hast": ^2.0.0
|
||||
"@types/prop-types": ^15.0.0
|
||||
"@types/unist": ^2.0.0
|
||||
comma-separated-tokens: ^2.0.0
|
||||
hast-util-whitespace: ^2.0.0
|
||||
prop-types: ^15.0.0
|
||||
property-information: ^6.0.0
|
||||
react-is: ^18.0.0
|
||||
remark-parse: ^10.0.0
|
||||
remark-rehype: ^10.0.0
|
||||
space-separated-tokens: ^2.0.0
|
||||
style-to-object: ^0.4.0
|
||||
unified: ^10.0.0
|
||||
unist-util-visit: ^4.0.0
|
||||
vfile: ^5.0.0
|
||||
peerDependencies:
|
||||
"@types/react": ">=16"
|
||||
react: ">=16"
|
||||
checksum: 0f3e570975134a3382c3fe5189e04e742ae154941463bdfaab2293319da1f1585cb9b75b6f07d99f514c4d728d69cc1af3c96ab37df90003b3bcc210dd0001ba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-remove-scroll-bar@npm:^2.3.3":
|
||||
version: 2.3.4
|
||||
resolution: "react-remove-scroll-bar@npm:2.3.4"
|
||||
@ -8726,6 +9178,29 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-parse@npm:^10.0.0":
|
||||
version: 10.0.2
|
||||
resolution: "remark-parse@npm:10.0.2"
|
||||
dependencies:
|
||||
"@types/mdast": ^3.0.0
|
||||
mdast-util-from-markdown: ^1.0.0
|
||||
unified: ^10.0.0
|
||||
checksum: 5041b4b44725f377e69986e02f8f072ae2222db5e7d3b6c80829756b842e811343ffc2069cae1f958a96bfa36104ab91a57d7d7e2f0cef521e210ab8c614d5c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-rehype@npm:^10.0.0":
|
||||
version: 10.1.0
|
||||
resolution: "remark-rehype@npm:10.1.0"
|
||||
dependencies:
|
||||
"@types/hast": ^2.0.0
|
||||
"@types/mdast": ^3.0.0
|
||||
mdast-util-to-hast: ^12.1.0
|
||||
unified: ^10.0.0
|
||||
checksum: b9ac8acff3383b204dfdc2599d0bdf86e6ca7e837033209584af2e6aaa6a9013e519a379afa3201299798cab7298c8f4b388de118c312c67234c133318aec084
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"renderkid@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "renderkid@npm:3.0.0"
|
||||
@ -8871,6 +9346,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sade@npm:^1.7.3":
|
||||
version: 1.8.1
|
||||
resolution: "sade@npm:1.8.1"
|
||||
dependencies:
|
||||
mri: ^1.1.0
|
||||
checksum: 0756e5b04c51ccdc8221ebffd1548d0ce5a783a44a0fa9017a026659b97d632913e78f7dca59f2496aa996a0be0b0c322afd87ca72ccd909406f49dbffa0f45d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1":
|
||||
version: 5.1.2
|
||||
resolution: "safe-buffer@npm:5.1.2"
|
||||
@ -9304,6 +9788,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"space-separated-tokens@npm:^2.0.0":
|
||||
version: 2.0.2
|
||||
resolution: "space-separated-tokens@npm:2.0.2"
|
||||
checksum: 202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"spdy-transport@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "spdy-transport@npm:3.0.0"
|
||||
@ -9435,6 +9926,7 @@ __metadata:
|
||||
react-dom: ^18.2.0
|
||||
react-helmet: ^6.1.0
|
||||
react-intersection-observer: ^9.5.1
|
||||
react-markdown: ^8.0.7
|
||||
react-router-dom: ^6.13.0
|
||||
react-tag-input-component: ^2.0.2
|
||||
semantic-sdp: ^3.26.2
|
||||
@ -9603,6 +10095,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"style-to-object@npm:^0.4.0":
|
||||
version: 0.4.1
|
||||
resolution: "style-to-object@npm:0.4.1"
|
||||
dependencies:
|
||||
inline-style-parser: 0.1.1
|
||||
checksum: 2ea213e98eed21764ae1d1dc9359231a9f2d480d6ba55344c4c15eb275f0809f1845786e66d4caf62414a5cc8f112ce9425a58d251c77224060373e0db48f8c2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stylehacks@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "stylehacks@npm:6.0.0"
|
||||
@ -9801,6 +10302,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"trim-lines@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "trim-lines@npm:3.0.1"
|
||||
checksum: e241da104682a0e0d807222cc1496b92e716af4db7a002f4aeff33ae6a0024fef93165d49eab11aa07c71e1347c42d46563f91dfaa4d3fb945aa535cdead53ed
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"trough@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "trough@npm:2.1.0"
|
||||
checksum: a577bb561c2b401cc0e1d9e188fcfcdf63b09b151ff56a668da12197fe97cac15e3d77d5b51f426ccfd94255744a9118e9e9935afe81a3644fa1be9783c82886
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-api-utils@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "ts-api-utils@npm:1.0.1"
|
||||
@ -9992,6 +10507,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unified@npm:^10.0.0":
|
||||
version: 10.1.2
|
||||
resolution: "unified@npm:10.1.2"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
bail: ^2.0.0
|
||||
extend: ^3.0.0
|
||||
is-buffer: ^2.0.0
|
||||
is-plain-obj: ^4.0.0
|
||||
trough: ^2.0.0
|
||||
vfile: ^5.0.0
|
||||
checksum: 053e7c65ede644607f87bd625a299e4b709869d2f76ec8138569e6e886903b6988b21cd9699e471eda42bee189527be0a9dac05936f1d069a5e65d0125d5d756
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-filename@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "unique-filename@npm:3.0.0"
|
||||
@ -10019,6 +10549,61 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-generated@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "unist-util-generated@npm:2.0.1"
|
||||
checksum: 6221ad0571dcc9c8964d6b054f39ef6571ed59cc0ce3e88ae97ea1c70afe76b46412a5ffaa91f96814644ac8477e23fb1b477d71f8d70e625728c5258f5c0d99
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-is@npm:^5.0.0":
|
||||
version: 5.2.1
|
||||
resolution: "unist-util-is@npm:5.2.1"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
checksum: ae76fdc3d35352cd92f1bedc3a0d407c3b9c42599a52ab9141fe89bdd786b51f0ec5a2ab68b93fb532e239457cae62f7e39eaa80229e1cb94875da2eafcbe5c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-position@npm:^4.0.0":
|
||||
version: 4.0.4
|
||||
resolution: "unist-util-position@npm:4.0.4"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
checksum: e7487b6cec9365299695e3379ded270a1717074fa11fd2407c9b934fb08db6fe1d9077ddeaf877ecf1813665f8ccded5171693d3d9a7a01a125ec5cdd5e88691
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-stringify-position@npm:^3.0.0":
|
||||
version: 3.0.3
|
||||
resolution: "unist-util-stringify-position@npm:3.0.3"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
checksum: dbd66c15183607ca942a2b1b7a9f6a5996f91c0d30cf8966fb88955a02349d9eefd3974e9010ee67e71175d784c5a9fea915b0aa0b0df99dcb921b95c4c9e124
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-visit-parents@npm:^5.1.1":
|
||||
version: 5.1.3
|
||||
resolution: "unist-util-visit-parents@npm:5.1.3"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
unist-util-is: ^5.0.0
|
||||
checksum: 8ecada5978994f846b64658cf13b4092cd78dea39e1ba2f5090a5de842ba4852712c02351a8ae95250c64f864635e7b02aedf3b4a093552bb30cf1bd160efbaa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unist-util-visit@npm:^4.0.0":
|
||||
version: 4.1.2
|
||||
resolution: "unist-util-visit@npm:4.1.2"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
unist-util-is: ^5.0.0
|
||||
unist-util-visit-parents: ^5.1.1
|
||||
checksum: 95a34e3f7b5b2d4b68fd722b6229972099eb97b6df18913eda44a5c11df8b1e27efe7206dd7b88c4ed244a48c474a5b2e2629ab79558ff9eb936840295549cee
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"universalify@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "universalify@npm:2.0.0"
|
||||
@ -10143,6 +10728,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uvu@npm:^0.5.0":
|
||||
version: 0.5.6
|
||||
resolution: "uvu@npm:0.5.6"
|
||||
dependencies:
|
||||
dequal: ^2.0.0
|
||||
diff: ^5.0.0
|
||||
kleur: ^4.0.3
|
||||
sade: ^1.7.3
|
||||
bin:
|
||||
uvu: bin.js
|
||||
checksum: 09460a37975627de9fcad396e5078fb844d01aaf64a6399ebfcfd9e55f1c2037539b47611e8631f89be07656962af0cf48c334993db82b9ae9c3d25ce3862168
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vary@npm:~1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "vary@npm:1.1.2"
|
||||
@ -10150,6 +10749,28 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vfile-message@npm:^3.0.0":
|
||||
version: 3.1.4
|
||||
resolution: "vfile-message@npm:3.1.4"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
unist-util-stringify-position: ^3.0.0
|
||||
checksum: d0ee7da1973ad76513c274e7912adbed4d08d180eaa34e6bd40bc82459f4b7bc50fcaff41556135e3339995575eac5f6f709aba9332b80f775618ea4880a1367
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vfile@npm:^5.0.0":
|
||||
version: 5.3.7
|
||||
resolution: "vfile@npm:5.3.7"
|
||||
dependencies:
|
||||
"@types/unist": ^2.0.0
|
||||
is-buffer: ^2.0.0
|
||||
unist-util-stringify-position: ^3.0.0
|
||||
vfile-message: ^3.0.0
|
||||
checksum: 642cce703afc186dbe7cabf698dc954c70146e853491086f5da39e1ce850676fc96b169fcf7898aa3ff245e9313aeec40da93acd1e1fcc0c146dc4f6308b4ef9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"watchpack@npm:^2.4.0":
|
||||
version: 2.4.0
|
||||
resolution: "watchpack@npm:2.4.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user