a tool for shared writing and social publishing

Merge branch 'main' into feature/page-blocks

+5848 -3867
+4 -1
actions/createIdentity.ts
··· 8 import { v7 } from "uuid"; 9 import { PgTransaction } from "drizzle-orm/pg-core"; 10 import { NodePgDatabase } from "drizzle-orm/node-postgres"; 11 12 export async function createIdentity( 13 db: NodePgDatabase, ··· 43 .insert(identities) 44 .values({ home_page: permissionToken.id, ...data }) 45 .returning(); 46 - return identity; 47 }); 48 }
··· 8 import { v7 } from "uuid"; 9 import { PgTransaction } from "drizzle-orm/pg-core"; 10 import { NodePgDatabase } from "drizzle-orm/node-postgres"; 11 + import { Json } from "supabase/database.types"; 12 13 export async function createIdentity( 14 db: NodePgDatabase, ··· 44 .insert(identities) 45 .values({ home_page: permissionToken.id, ...data }) 46 .returning(); 47 + return identity as Omit<typeof identity, "interface_state"> & { 48 + interface_state: Json; 49 + }; 50 }); 51 }
+1 -1
actions/getIdentityData.ts
··· 25 id, 26 root_entity, 27 permission_token_rights(*), 28 - leaflets_in_publications(*, publications(*)) 29 ) 30 ) 31 )`,
··· 25 id, 26 root_entity, 27 permission_token_rights(*), 28 + leaflets_in_publications(*, publications(*), documents(*)) 29 ) 30 ) 31 )`,
+135
actions/publishToPublication.ts
··· 359 } 360 return; 361 } 362 } 363 364 function YJSFragmentToFacets(
··· 359 } 360 return; 361 } 362 + <<<<<<< HEAD 363 + ======= 364 + 365 + if (b.type === "blockquote") { 366 + let [stringValue, facets] = getBlockContent(b.value); 367 + let block: $Typed<PubLeafletBlocksBlockquote.Main> = { 368 + $type: ids.PubLeafletBlocksBlockquote, 369 + plaintext: stringValue, 370 + facets, 371 + }; 372 + return block; 373 + } 374 + 375 + if (b.type == "text") { 376 + let [stringValue, facets] = getBlockContent(b.value); 377 + let block: $Typed<PubLeafletBlocksText.Main> = { 378 + $type: ids.PubLeafletBlocksText, 379 + plaintext: stringValue, 380 + facets, 381 + }; 382 + return block; 383 + } 384 + if (b.type === "embed") { 385 + let [url] = scan.eav(b.value, "embed/url"); 386 + let [height] = scan.eav(b.value, "embed/height"); 387 + if (!url) return; 388 + let block: $Typed<PubLeafletBlocksIframe.Main> = { 389 + $type: "pub.leaflet.blocks.iframe", 390 + url: url.data.value, 391 + height: Math.floor(height?.data.value || 600), 392 + }; 393 + return block; 394 + } 395 + if (b.type == "image") { 396 + let [image] = scan.eav(b.value, "block/image"); 397 + if (!image) return; 398 + let [altText] = scan.eav(b.value, "image/alt"); 399 + let blobref = imageMap.get(image.data.src); 400 + if (!blobref) return; 401 + let block: $Typed<PubLeafletBlocksImage.Main> = { 402 + $type: "pub.leaflet.blocks.image", 403 + image: blobref, 404 + aspectRatio: { 405 + height: image.data.height, 406 + width: image.data.width, 407 + }, 408 + alt: altText ? altText.data.value : undefined, 409 + }; 410 + return block; 411 + } 412 + if (b.type === "link") { 413 + let [previewImage] = scan.eav(b.value, "link/preview"); 414 + let [description] = scan.eav(b.value, "link/description"); 415 + let [src] = scan.eav(b.value, "link/url"); 416 + if (!src) return; 417 + let blobref = previewImage 418 + ? imageMap.get(previewImage?.data.src) 419 + : undefined; 420 + let [title] = scan.eav(b.value, "link/title"); 421 + let block: $Typed<PubLeafletBlocksWebsite.Main> = { 422 + $type: "pub.leaflet.blocks.website", 423 + previewImage: blobref, 424 + src: src.data.value, 425 + description: description?.data.value, 426 + title: title?.data.value, 427 + }; 428 + return block; 429 + } 430 + if (b.type === "code") { 431 + let [language] = scan.eav(b.value, "block/code-language"); 432 + let [code] = scan.eav(b.value, "block/code"); 433 + let [theme] = scan.eav(root_entity, "theme/code-theme"); 434 + let block: $Typed<PubLeafletBlocksCode.Main> = { 435 + $type: "pub.leaflet.blocks.code", 436 + language: language?.data.value, 437 + plaintext: code?.data.value || "", 438 + syntaxHighlightingTheme: theme?.data.value, 439 + }; 440 + return block; 441 + } 442 + if (b.type === "math") { 443 + let [math] = scan.eav(b.value, "block/math"); 444 + let block: $Typed<PubLeafletBlocksMath.Main> = { 445 + $type: "pub.leaflet.blocks.math", 446 + tex: math?.data.value || "", 447 + }; 448 + return block; 449 + } 450 + return; 451 + } 452 + 453 + async function sendPostToEmailSubscribers( 454 + publication_uri: string, 455 + post: { content: string; title: string }, 456 + ) { 457 + let { data: publication } = await supabaseServerClient 458 + .from("publications") 459 + .select("*, subscribers_to_publications(*)") 460 + .eq("uri", publication_uri) 461 + .single(); 462 + 463 + let res = await fetch("https://api.postmarkapp.com/email/batch", { 464 + method: "POST", 465 + headers: { 466 + "Content-Type": "application/json", 467 + "X-Postmark-Server-Token": process.env.POSTMARK_API_KEY!, 468 + }, 469 + body: JSON.stringify( 470 + publication?.subscribers_to_publications.map((sub) => ({ 471 + Headers: [ 472 + { 473 + Name: "List-Unsubscribe-Post", 474 + Value: "List-Unsubscribe=One-Click", 475 + }, 476 + { 477 + Name: "List-Unsubscribe", 478 + Value: `<${"TODO"}/mail/unsubscribe?sub_id=${sub.identity}>`, 479 + }, 480 + ], 481 + MessageStream: "broadcast", 482 + From: `${publication.name} <mailbox@leaflet.pub>`, 483 + Subject: post.title, 484 + To: sub.identity, 485 + HtmlBody: ` 486 + <h1>${publication.name}</h1> 487 + <hr style="margin-top: 1em; margin-bottom: 1em;"> 488 + ${post.content} 489 + <hr style="margin-top: 1em; margin-bottom: 1em;"> 490 + This is a super alpha release! Ask Jared if you want to unsubscribe (sorry) 491 + `, 492 + TextBody: post.content, 493 + })), 494 + ), 495 + }); 496 + >>>>>>> main 497 } 498 499 function YJSFragmentToFacets(
+16
actions/updateIdentityInterfaceState.ts
···
··· 1 + "use server"; 2 + 3 + import { InterfaceState } from "components/IdentityProvider"; 4 + import { getIdentityData } from "./getIdentityData"; 5 + import { supabaseServerClient } from "supabase/serverClient"; 6 + 7 + export async function updateIdentityInterfaceState( 8 + interfaceState: InterfaceState, 9 + ) { 10 + let identity = await getIdentityData(); 11 + if (!identity) return; 12 + await supabaseServerClient 13 + .from("identities") 14 + .update({ interface_state: interfaceState }) 15 + .eq("id", identity.id); 16 + }
+1 -1
app/[leaflet_id]/Actions.tsx
··· 27 return ( 28 <SpeedyLink 29 href={`${getBasePublicationURL(props.publication)}/dashboard`} 30 - className="hover:!no-underline" 31 > 32 <ActionButton 33 icon={<GoBackSmall className="shrink-0" />}
··· 27 return ( 28 <SpeedyLink 29 href={`${getBasePublicationURL(props.publication)}/dashboard`} 30 + className="hover:no-underline!" 31 > 32 <ActionButton 33 icon={<GoBackSmall className="shrink-0" />}
+16 -17
app/[leaflet_id]/page.tsx
··· 12 import { getPollData } from "actions/pollActions"; 13 import { supabaseServerClient } from "supabase/serverClient"; 14 import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data"; 15 16 export const preferredRegion = ["sfo1"]; 17 export const dynamic = "force-dynamic"; ··· 29 let rootEntity = res.data?.root_entity; 30 if (!rootEntity || !res.data || res.data.blocked_by_admin) 31 return ( 32 - <div className="w-screen h-full flex place-items-center bg-bg-leaflet"> 33 - <div className="bg-bg-page mx-auto p-4 border border-border rounded-md flex flex-col text-center justify-center gap-1 w-fit"> 34 - <div className="font-bold"> 35 - Hmmm…we couldn&apos;t find that Leaflet. 36 - </div> 37 - <div> 38 - You can{" "} 39 - <a href="mailto:contact@leaflet.pub" target="blank"> 40 - email us 41 - </a>{" "} 42 - for help! 43 - </div> 44 - </div> 45 - </div> 46 ); 47 48 let [{ data }, rsvp_data, poll_data] = await Promise.all([ ··· 76 ); 77 let rootEntity = res.data?.root_entity; 78 if (!rootEntity || !res.data) return { title: "Leaflet not found" }; 79 - if (res.data.leaflets_in_publications[0]) { 80 return { 81 - title: res.data.leaflets_in_publications[0].title || "Untitled", 82 - description: res.data.leaflets_in_publications[0].description, 83 }; 84 } 85 let { data } = await supabaseServerClient.rpc("get_facts", {
··· 12 import { getPollData } from "actions/pollActions"; 13 import { supabaseServerClient } from "supabase/serverClient"; 14 import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data"; 15 + import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 16 17 export const preferredRegion = ["sfo1"]; 18 export const dynamic = "force-dynamic"; ··· 30 let rootEntity = res.data?.root_entity; 31 if (!rootEntity || !res.data || res.data.blocked_by_admin) 32 return ( 33 + <NotFoundLayout> 34 + <p className="font-bold">Sorry, we can't find this leaflet!</p> 35 + <p> 36 + This may be a glitch on our end. If the issue persists please{" "} 37 + <a href="mailto:contact@leaflet.pub">send us a note</a>. 38 + </p> 39 + </NotFoundLayout> 40 ); 41 42 let [{ data }, rsvp_data, poll_data] = await Promise.all([ ··· 70 ); 71 let rootEntity = res.data?.root_entity; 72 if (!rootEntity || !res.data) return { title: "Leaflet not found" }; 73 + let publication_data = 74 + res.data?.leaflets_in_publications?.[0] || 75 + res.data?.permission_token_rights[0].entity_sets?.permission_tokens?.find( 76 + (p) => p.leaflets_in_publications.length, 77 + )?.leaflets_in_publications?.[0]; 78 + if (publication_data) { 79 return { 80 + title: publication_data.title || "Untitled", 81 + description: publication_data.description, 82 }; 83 } 84 let { data } = await supabaseServerClient.rpc("get_facts", {
-8
app/[leaflet_id]/publish/PublishIllustration/PublishIllustration.tsx
··· 78 Math.pow((isPlanet ? y + 14 : y) - (moonPosY + 41), 2), 79 ); 80 if (moonDistance < 60) { 81 - console.log("moon collision!"); 82 - 83 return true; 84 } 85 ··· 90 Math.pow(y - (isPlanet ? star.y + 14 : star.y), 2), 91 ); 92 if (starDistance < 40) { 93 - console.log("star collision!"); 94 - 95 return true; 96 } 97 } ··· 147 y: y, 148 rot: Math.random() * 360, 149 }); 150 - console.log("planet : " + type); 151 } 152 // the first, fouth, and seventh stars are large stars 153 else if ( ··· 156 i === numberOfPlanets + 6 157 ) { 158 let type = Math.floor(Math.random() * 4); 159 - console.log("big star : " + type); 160 stars.push({ 161 type: 162 type === 0 ··· 172 }); 173 } else { 174 let type = Math.floor(Math.random() * 3); 175 - console.log("small star : " + type); 176 177 stars.push({ 178 type: type === 0 ? "S1" : type === 1 ? "S2" : "S3", ··· 181 rot: Math.random() * 360, 182 }); 183 } 184 - console.log("planetcount : " + JSON.stringify(planetCounter)); 185 } 186 } 187 // animate the last child
··· 78 Math.pow((isPlanet ? y + 14 : y) - (moonPosY + 41), 2), 79 ); 80 if (moonDistance < 60) { 81 return true; 82 } 83 ··· 88 Math.pow(y - (isPlanet ? star.y + 14 : star.y), 2), 89 ); 90 if (starDistance < 40) { 91 return true; 92 } 93 } ··· 143 y: y, 144 rot: Math.random() * 360, 145 }); 146 } 147 // the first, fouth, and seventh stars are large stars 148 else if ( ··· 151 i === numberOfPlanets + 6 152 ) { 153 let type = Math.floor(Math.random() * 4); 154 stars.push({ 155 type: 156 type === 0 ··· 166 }); 167 } else { 168 let type = Math.floor(Math.random() * 3); 169 170 stars.push({ 171 type: type === 0 ? "S1" : type === 1 ? "S2" : "S3", ··· 174 rot: Math.random() * 360, 175 }); 176 } 177 } 178 } 179 // animate the last child
+3 -3
app/[leaflet_id]/publish/PublishPost.tsx
··· 133 <div 134 className={`w-full pl-5 pb-4 ${shareOption !== "bluesky" ? "opacity-50" : ""}`} 135 > 136 - <div className="opaque-container p-3 !rounded-lg"> 137 <div className="flex gap-2"> 138 <img 139 className="bg-test rounded-full w-[42px] h-[42px] shrink-0" ··· 173 </div> 174 <div className="flex justify-between"> 175 <Link 176 - className="hover:!no-underline font-bold" 177 href={`/${params.leaflet_id}`} 178 > 179 Back ··· 200 <PublishIllustration posts_in_pub={props.posts_in_pub} /> 201 <h2 className="pt-2">Published!</h2> 202 <Link 203 - className="hover:!no-underline font-bold place-self-center pt-2" 204 href={`/lish/${uri.host}/${encodeURIComponent(props.record?.name || "")}/dashboard`} 205 > 206 <ButtonPrimary>Back to Dashboard</ButtonPrimary>
··· 133 <div 134 className={`w-full pl-5 pb-4 ${shareOption !== "bluesky" ? "opacity-50" : ""}`} 135 > 136 + <div className="opaque-container p-3 rounded-lg!"> 137 <div className="flex gap-2"> 138 <img 139 className="bg-test rounded-full w-[42px] h-[42px] shrink-0" ··· 173 </div> 174 <div className="flex justify-between"> 175 <Link 176 + className="hover:no-underline! font-bold" 177 href={`/${params.leaflet_id}`} 178 > 179 Back ··· 200 <PublishIllustration posts_in_pub={props.posts_in_pub} /> 201 <h2 className="pt-2">Published!</h2> 202 <Link 203 + className="hover:no-underline! font-bold place-self-center pt-2" 204 href={`/lish/${uri.host}/${encodeURIComponent(props.record?.name || "")}/dashboard`} 205 > 206 <ButtonPrimary>Back to Dashboard</ButtonPrimary>
+21 -16
app/[leaflet_id]/publish/page.tsx
··· 1 import { supabaseServerClient } from "supabase/serverClient"; 2 - import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data"; 3 import { PublishPost } from "./PublishPost"; 4 import { PubLeafletPublication } from "lexicons/api"; 5 import { getIdentityData } from "actions/getIdentityData"; ··· 15 // this is now a token id not leaflet! Should probs rename 16 params: Promise<{ leaflet_id: string }>; 17 }; 18 - export default async function LeafletPage(props: Props) { 19 let leaflet_id = (await props.params).leaflet_id; 20 - let { result: res } = await get_leaflet_data.handler( 21 - { token_id: leaflet_id }, 22 - { supabase: supabaseServerClient }, 23 - ); 24 - let rootEntity = res.data?.root_entity; 25 - if ( 26 - !rootEntity || 27 - !res.data || 28 - res.data.blocked_by_admin || 29 - !res.data.leaflets_in_publications[0] 30 - ) 31 return ( 32 <div> 33 missin something 34 - <pre>{JSON.stringify(res.data, undefined, 2)}</pre> 35 </div> 36 ); 37 38 let identity = await getIdentityData(); 39 if (!identity || !identity.atp_did) return null; 40 - let pub = res.data.leaflets_in_publications[0]; 41 let agent = new AtpAgent({ service: "https://public.api.bsky.app" }); 42 43 let profile = await agent.getProfile({ actor: identity.atp_did }); 44 return ( 45 <ReplicacheProvider 46 rootEntity={rootEntity} 47 - token={res.data} 48 name={rootEntity} 49 initialFacts={[]} 50 >
··· 1 import { supabaseServerClient } from "supabase/serverClient"; 2 import { PublishPost } from "./PublishPost"; 3 import { PubLeafletPublication } from "lexicons/api"; 4 import { getIdentityData } from "actions/getIdentityData"; ··· 14 // this is now a token id not leaflet! Should probs rename 15 params: Promise<{ leaflet_id: string }>; 16 }; 17 + export default async function PublishLeafletPage(props: Props) { 18 let leaflet_id = (await props.params).leaflet_id; 19 + let { data } = await supabaseServerClient 20 + .from("permission_tokens") 21 + .select( 22 + `*, 23 + permission_token_rights(*), 24 + leaflets_in_publications( 25 + *, 26 + publications( 27 + *, 28 + documents_in_publications(count) 29 + ), 30 + documents(*))`, 31 + ) 32 + .eq("id", leaflet_id) 33 + .single(); 34 + let rootEntity = data?.root_entity; 35 + if (!data || !rootEntity || !data.leaflets_in_publications[0]) 36 return ( 37 <div> 38 missin something 39 + <pre>{JSON.stringify(data, undefined, 2)}</pre> 40 </div> 41 ); 42 43 let identity = await getIdentityData(); 44 if (!identity || !identity.atp_did) return null; 45 + let pub = data.leaflets_in_publications[0]; 46 let agent = new AtpAgent({ service: "https://public.api.bsky.app" }); 47 48 let profile = await agent.getProfile({ actor: identity.atp_did }); 49 return ( 50 <ReplicacheProvider 51 rootEntity={rootEntity} 52 + token={data} 53 name={rootEntity} 54 initialFacts={[]} 55 >
+6 -3
app/api/oauth/[route]/oauth-metadata.ts
··· 1 - import { OAuthClientMetadata } from "@atproto/oauth-client-node"; 2 const hostname = 3 process.env.NODE_ENV === "development" 4 ? "http://localhost:3000" 5 : "https://leaflet.pub"; 6 7 const scope = "atproto transition:generic transition:email"; 8 - const localconfig: OAuthClientMetadata = { 9 client_id: `http://localhost/?redirect_uri=${encodeURI(`http://127.0.0.1:3000/api/oauth/callback`)}&scope=${encodeURIComponent(scope)}`, 10 client_name: `Leaflet`, 11 client_uri: hostname, ··· 18 dpop_bound_access_tokens: true, 19 }; 20 21 - const prodconfig: OAuthClientMetadata = { 22 client_id: `${hostname}/api/oauth/metadata`, 23 client_name: `Leaflet`, 24 client_uri: hostname,
··· 1 + import { 2 + OAuthClientMetadata, 3 + OAuthClientMetadataInput, 4 + } from "@atproto/oauth-client-node"; 5 const hostname = 6 process.env.NODE_ENV === "development" 7 ? "http://localhost:3000" 8 : "https://leaflet.pub"; 9 10 const scope = "atproto transition:generic transition:email"; 11 + const localconfig: OAuthClientMetadataInput = { 12 client_id: `http://localhost/?redirect_uri=${encodeURI(`http://127.0.0.1:3000/api/oauth/callback`)}&scope=${encodeURIComponent(scope)}`, 13 client_name: `Leaflet`, 14 client_uri: hostname, ··· 21 dpop_bound_access_tokens: true, 22 }; 23 24 + const prodconfig: OAuthClientMetadataInput = { 25 client_id: `${hostname}/api/oauth/metadata`, 26 client_name: `Leaflet`, 27 client_uri: hostname,
+37 -8
app/api/rpc/[command]/getFactsFromHomeLeaflets.ts
··· 3 import type { Attribute } from "src/replicache/attributes"; 4 import { makeRoute } from "../lib"; 5 import type { Env } from "./route"; 6 7 export const getFactsFromHomeLeaflets = makeRoute({ 8 route: "getFactsFromHomeLeaflets", ··· 16 }); 17 18 if (all_facts.data) { 19 return { 20 - result: all_facts.data.reduce( 21 - (acc, fact) => { 22 - if (!acc[fact.root_id]) acc[fact.root_id] = []; 23 - acc[fact.root_id].push(fact as unknown as Fact<Attribute>); 24 - return acc; 25 - }, 26 - {} as { [key: string]: Fact<Attribute>[] }, 27 - ), 28 }; 29 } 30
··· 3 import type { Attribute } from "src/replicache/attributes"; 4 import { makeRoute } from "../lib"; 5 import type { Env } from "./route"; 6 + import { scanIndexLocal } from "src/replicache/utils"; 7 + import { getBlocksWithTypeLocal } from "src/hooks/queries/useBlocks"; 8 + import * as base64 from "base64-js"; 9 + import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment"; 10 + import { applyUpdate, Doc } from "yjs"; 11 12 export const getFactsFromHomeLeaflets = makeRoute({ 13 route: "getFactsFromHomeLeaflets", ··· 21 }); 22 23 if (all_facts.data) { 24 + let titles = {} as { [key: string]: string }; 25 + 26 + let facts = all_facts.data.reduce( 27 + (acc, fact) => { 28 + if (!acc[fact.root_id]) acc[fact.root_id] = []; 29 + acc[fact.root_id].push(fact as unknown as Fact<Attribute>); 30 + return acc; 31 + }, 32 + {} as { [key: string]: Fact<Attribute>[] }, 33 + ); 34 + for (let token of tokens) { 35 + let scan = scanIndexLocal(facts[token]); 36 + let [root] = scan.eav(token, "root/page"); 37 + let rootEntity = root?.data.value || token; 38 + let [title] = getBlocksWithTypeLocal(facts[token], rootEntity).filter( 39 + (b) => b.type === "text" || b.type === "heading", 40 + ); 41 + if (!title) titles[token] = "Untitled"; 42 + else { 43 + let [content] = scan.eav(title.value, "block/text"); 44 + if (!content) titles[token] = "Untitled"; 45 + else { 46 + let doc = new Doc(); 47 + const update = base64.toByteArray(content.data.value); 48 + applyUpdate(doc, update); 49 + let nodes = doc.getXmlElement("prosemirror").toArray(); 50 + let stringValue = YJSFragmentToString(nodes[0]); 51 + titles[token] = stringValue; 52 + } 53 + } 54 + } 55 return { 56 + result: { facts, titles }, 57 }; 58 } 59
+1 -1
app/api/rpc/[command]/get_leaflet_data.ts
··· 6 ReturnType<(typeof get_leaflet_data)["handler"]> 7 >; 8 9 - const leaflets_in_publications_query = `leaflets_in_publications(*, publications(*, documents_in_publications(count)), documents(*))`; 10 export const get_leaflet_data = makeRoute({ 11 route: "get_leaflet_data", 12 input: z.object({
··· 6 ReturnType<(typeof get_leaflet_data)["handler"]> 7 >; 8 9 + const leaflets_in_publications_query = `leaflets_in_publications(*, publications(*), documents(*))`; 10 export const get_leaflet_data = makeRoute({ 11 route: "get_leaflet_data", 12 input: z.object({
+13 -1
app/api/rpc/[command]/get_publication_data.ts
··· 2 import { makeRoute } from "../lib"; 3 import type { Env } from "./route"; 4 import { AtUri } from "@atproto/syntax"; 5 6 export type GetPublicationDataReturnType = Awaited< 7 ReturnType<(typeof get_publication_data)["handler"]> ··· 36 publication_subscriptions(*, identities(bsky_profiles(*))), 37 publication_domains(*), 38 leaflets_in_publications(*, 39 permission_tokens(*, 40 permission_token_rights(*), 41 custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) ··· 46 .eq("identity_did", did) 47 .single(); 48 49 - return { result: publication }; 50 }, 51 });
··· 2 import { makeRoute } from "../lib"; 3 import type { Env } from "./route"; 4 import { AtUri } from "@atproto/syntax"; 5 + import { getFactsFromHomeLeaflets } from "./getFactsFromHomeLeaflets"; 6 7 export type GetPublicationDataReturnType = Awaited< 8 ReturnType<(typeof get_publication_data)["handler"]> ··· 37 publication_subscriptions(*, identities(bsky_profiles(*))), 38 publication_domains(*), 39 leaflets_in_publications(*, 40 + documents(*), 41 permission_tokens(*, 42 permission_token_rights(*), 43 custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*) ··· 48 .eq("identity_did", did) 49 .single(); 50 51 + let leaflet_data = await getFactsFromHomeLeaflets.handler( 52 + { 53 + tokens: 54 + publication?.leaflets_in_publications.map( 55 + (l) => l.permission_tokens?.root_entity!, 56 + ) || [], 57 + }, 58 + { supabase }, 59 + ); 60 + 61 + return { result: { publication, leaflet_data: leaflet_data.result } }; 62 }, 63 });
+2 -24
app/discover/PubListing.tsx
··· 4 import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider"; 5 import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api"; 6 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 7 import { Json } from "supabase/database.types"; 8 9 export const PubListing = (props: { ··· 35 backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 36 backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 37 }} 38 - className={`!no-underline flex flex-row gap-2 39 bg-bg-leaflet 40 border border-border-light rounded-lg 41 px-3 py-3 selected-outline ··· 69 </BaseThemeProvider> 70 ); 71 }; 72 - 73 - export function timeAgo(timestamp: string): string { 74 - const now = new Date(); 75 - const date = new Date(timestamp); 76 - const diffMs = now.getTime() - date.getTime(); 77 - const diffSeconds = Math.floor(diffMs / 1000); 78 - const diffMinutes = Math.floor(diffSeconds / 60); 79 - const diffHours = Math.floor(diffMinutes / 60); 80 - const diffDays = Math.floor(diffHours / 24); 81 - const diffYears = Math.floor(diffDays / 365); 82 - 83 - if (diffYears > 0) { 84 - return `${diffYears} year${diffYears === 1 ? "" : "s"} ago`; 85 - } else if (diffDays > 0) { 86 - return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`; 87 - } else if (diffHours > 0) { 88 - return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`; 89 - } else if (diffMinutes > 0) { 90 - return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`; 91 - } else { 92 - return "just now"; 93 - } 94 - }
··· 4 import { BaseThemeProvider } from "components/ThemeManager/ThemeProvider"; 5 import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api"; 6 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 7 + import { timeAgo } from "src/utils/timeAgo"; 8 import { Json } from "supabase/database.types"; 9 10 export const PubListing = (props: { ··· 36 backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 37 backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 38 }} 39 + className={`no-underline! flex flex-row gap-2 40 bg-bg-leaflet 41 border border-border-light rounded-lg 42 px-3 py-3 selected-outline ··· 70 </BaseThemeProvider> 71 ); 72 };
+2 -3
app/discover/SortedPublicationList.tsx
··· 11 }) { 12 let [order, setOrder] = useState(props.order); 13 return ( 14 - <div className="discoverHeader flex flex-col items-center px-4"> 15 <SortButtons 16 order={order} 17 setOrder={(o) => { ··· 21 setOrder(o); 22 }} 23 /> 24 - <div className="discoverPubList flex flex-col gap-3 pt-6"> 25 {props.publications 26 ?.filter((pub) => pub.documents_in_publications.length > 0) 27 ?.sort((a, b) => { 28 if (order === "popular") { 29 - console.log("sorting by popularity"); 30 return ( 31 b.publication_subscriptions[0].count - 32 a.publication_subscriptions[0].count
··· 11 }) { 12 let [order, setOrder] = useState(props.order); 13 return ( 14 + <div className="discoverHeader flex flex-col items-center "> 15 <SortButtons 16 order={order} 17 setOrder={(o) => { ··· 21 setOrder(o); 22 }} 23 /> 24 + <div className="discoverPubList flex flex-col gap-3 pt-6 w-full"> 25 {props.publications 26 ?.filter((pub) => pub.documents_in_publications.length > 0) 27 ?.sort((a, b) => { 28 if (order === "popular") { 29 return ( 30 b.publication_subscriptions[0].count - 31 a.publication_subscriptions[0].count
+32 -11
app/discover/page.tsx
··· 2 import Link from "next/link"; 3 import { SortedPublicationList } from "./SortedPublicationList"; 4 import { Metadata } from "next"; 5 6 export const dynamic = "force-static"; 7 export const revalidate = 60; ··· 36 let publications = await getPublications(); 37 38 return ( 39 - <div className="bg-[#FDFCFA] w-full h-full overflow-scroll"> 40 - <div className="max-w-prose mx-auto sm:py-6 py-4 px-4"> 41 - <div className="discoverHeader flex flex-col items-center px-4"> 42 - <h1>Discover</h1> 43 - <p className="text-lg text-secondary italic mb-2"> 44 - Explore publications on Leaflet ✨ Or{" "} 45 - <Link href="/lish/createPub">make your own</Link>! 46 - </p> 47 - </div> 48 - <SortedPublicationList publications={publications} order={order} /> 49 </div> 50 </div> 51 ); 52 - }
··· 2 import Link from "next/link"; 3 import { SortedPublicationList } from "./SortedPublicationList"; 4 import { Metadata } from "next"; 5 + import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 6 7 export const dynamic = "force-static"; 8 export const revalidate = 60; ··· 37 let publications = await getPublications(); 38 39 return ( 40 + <div className="w-full h-full mx-auto bg-[#FDFCFA]"> 41 + <DashboardLayout 42 + id="discover" 43 + hasBackgroundImage={false} 44 + currentPage="discover" 45 + defaultTab="default" 46 + actions={null} 47 + tabs={{ 48 + default: { 49 + controls: null, 50 + content: <DiscoverContent order={order} />, 51 + }, 52 + }} 53 + /> 54 + </div> 55 + ); 56 + } 57 + 58 + const DiscoverContent = async (props: { order: string }) => { 59 + let publications = await getPublications(); 60 + 61 + return ( 62 + <div className="max-w-prose mx-auto w-full"> 63 + <div className="discoverHeader flex flex-col items-center text-center pt-2 px-4"> 64 + <h1>Discover</h1> 65 + <p className="text-lg text-secondary italic mb-2"> 66 + Explore publications on Leaflet ✨ Or{" "} 67 + <Link href="/lish/createPub">make your own</Link>! 68 + </p> 69 </div> 70 + <SortedPublicationList publications={publications} order={props.order} /> 71 </div> 72 ); 73 + };
+106 -24
app/globals.css
··· 1 - @tailwind base; 2 - @tailwind components; 3 - @tailwind utilities; 4 5 @layer base { 6 :root { ··· 103 @apply text-accent-contrast; 104 @apply hover:cursor-pointer; 105 @apply no-underline; 106 - } 107 - 108 - a:hover { 109 - @apply underline; 110 } 111 112 pre { ··· 126 /* START GLOBAL STYLING */ 127 128 /* END GLOBAL STYLING */ 129 } 130 131 blockquote { ··· 165 font-size: 1em; 166 @apply bg-border-light; 167 @apply font-mono; 168 - @apply px-[1px]; 169 - @apply py-[1px]; 170 - @apply -mx-[1px]; 171 - @apply -my-[1px]; 172 @apply rounded-[4px]; 173 @apply box-decoration-clone; 174 } ··· 191 } 192 193 .highlight { 194 - @apply px-[1px]; 195 - @apply py-[1px]; 196 - @apply -mx-[1px]; 197 - @apply -my-[1px]; 198 @apply rounded-[4px]; 199 @apply box-decoration-clone; 200 } ··· 213 } 214 215 .transparent-outline { 216 - @apply outline; 217 @apply outline-transparent; 218 } 219 220 .selected-outline { 221 @apply border; 222 - @apply focus:outline; 223 @apply focus:outline-2; 224 @apply focus:outline-offset-1; 225 - @apply focus-within:outline; 226 @apply focus-within:outline-2; 227 @apply focus-within:outline-offset-1; 228 - @apply hover:outline; 229 @apply hover:outline-2; 230 @apply hover:outline-offset-1; 231 } ··· 240 @apply hover:border-tertiary; 241 242 @apply focus:border-tertiary; 243 - @apply focus:outline; 244 @apply focus:outline-tertiary; 245 @apply focus:outline-2; 246 @apply focus:outline-offset-1; 247 248 @apply focus-within:border-tertiary; 249 - @apply focus-within:outline; 250 @apply focus-within:outline-tertiary; 251 @apply focus-within:outline-2; 252 @apply focus-within:outline-offset-1; ··· 260 @apply border; 261 @apply border-border-light; 262 @apply rounded-lg; 263 - @apply outline; 264 @apply outline-offset-1; 265 @apply outline-2; 266 @apply outline-transparent; ··· 271 @apply border; 272 @apply border-border; 273 @apply rounded-lg; 274 - @apply outline; 275 @apply outline-offset-1; 276 @apply outline-2; 277 @apply outline-border; ··· 290 } 291 292 .container { 293 - background: rgba(var(--bg-page), 0.5); 294 @apply border; 295 @apply border-bg-page; 296 @apply rounded-md;
··· 1 + @import "tailwindcss"; 2 + 3 + @theme inline { 4 + --breakpoint-*: initial; 5 + --breakpoint-sm: 640px; 6 + --breakpoint-md: 960px; 7 + --breakpoint-lg: 1280px; 8 + 9 + --radius-*: initial; 10 + --radius-none: 0; 11 + --radius-md: 0.25rem; 12 + --radius-lg: 0.5rem; 13 + --radius-full: 9999px; 14 + 15 + --color-*: initial; 16 + --color-inherit: inherit; 17 + --color-transparent: transparent; 18 + --color-current: currentColor; 19 + --color-primary: rgb(var(--primary)); 20 + --color-secondary: color-mix( 21 + in oklab, 22 + rgb(var(--primary)), 23 + rgb(var(--bg-page)) 25% 24 + ); 25 + --color-tertiary: color-mix( 26 + in oklab, 27 + rgb(var(--primary)), 28 + rgb(var(--bg-page)) 55% 29 + ); 30 + --color-border: color-mix( 31 + in oklab, 32 + rgb(var(--primary)), 33 + rgb(var(--bg-page)) 75% 34 + ); 35 + --color-border-light: color-mix( 36 + in oklab, 37 + rgb(var(--primary)), 38 + rgb(var(--bg-page)) 85% 39 + ); 40 + --color-white: #ffffff; 41 + --color-accent-1: rgb(var(--accent-1)); 42 + --color-accent-2: rgb(var(--accent-2)); 43 + --color-accent-contrast: rgb(var(--accent-contrast)); 44 + --color-bg-leaflet: rgb(var(--bg-leaflet)); 45 + --color-bg-page: rgb(var(--bg-page)); 46 + --color-highlight-1: var(--highlight-1); 47 + --color-highlight-2: rgb(var(--highlight-2)); 48 + --color-highlight-3: rgb(var(--highlight-3)); 49 + --color-test: #e18181; 50 + --color-test-blue: #48d1ef; 51 + 52 + --text-*: initial; 53 + --text-xs: 0.75rem; 54 + --text-sm: 0.875rem; 55 + --text-base: 1rem; 56 + --text-lg: 1.125rem; 57 + --text-xl: 1.625rem; 58 + --text-2xl: 2rem; 59 + 60 + --shadow-sm: 0.9px 1.5px 1.7px -1.8px rgba(var(--primary), 0.2), 61 + 4.2px 6.9px 7.8px -3.5px rgba(var(--primary), 0.15); 62 + --shadow-md: 1.2px 2.5px 2.7px -1.8px rgba(var(--primary), 0.1), 63 + 5.6px 11.6px 12.5px -3.5px rgba(var(--primary), 0.15); 64 + 65 + --font-sans: var(--font-quattro); 66 + --font-serif: Garamond; 67 + } 68 + 69 + /* 70 + The default border color has changed to `currentcolor` in Tailwind CSS v4, 71 + so we've added these compatibility styles to make sure everything still 72 + looks the same as it did with Tailwind CSS v3. 73 + 74 + If we ever want to remove these styles, we need to add an explicit border 75 + color utility to any element that depends on these defaults. 76 + */ 77 + @layer base { 78 + *, 79 + ::after, 80 + ::before, 81 + ::backdrop, 82 + ::file-selector-button { 83 + border-color: var(--color-gray-200, currentcolor); 84 + } 85 + } 86 87 @layer base { 88 :root { ··· 185 @apply text-accent-contrast; 186 @apply hover:cursor-pointer; 187 @apply no-underline; 188 + @apply hover:underline; 189 } 190 191 pre { ··· 205 /* START GLOBAL STYLING */ 206 207 /* END GLOBAL STYLING */ 208 + } 209 + button:hover { 210 + cursor: pointer; 211 } 212 213 blockquote { ··· 247 font-size: 1em; 248 @apply bg-border-light; 249 @apply font-mono; 250 + @apply px-px; 251 + @apply py-px; 252 + @apply -mx-px; 253 + @apply -my-px; 254 @apply rounded-[4px]; 255 @apply box-decoration-clone; 256 } ··· 273 } 274 275 .highlight { 276 + @apply px-px; 277 + @apply py-px; 278 + @apply -mx-px; 279 + @apply -my-px; 280 @apply rounded-[4px]; 281 @apply box-decoration-clone; 282 } ··· 295 } 296 297 .transparent-outline { 298 + @apply outline-solid; 299 @apply outline-transparent; 300 } 301 302 .selected-outline { 303 @apply border; 304 + @apply focus:outline-solid; 305 @apply focus:outline-2; 306 @apply focus:outline-offset-1; 307 + @apply focus-within:outline-solid; 308 @apply focus-within:outline-2; 309 @apply focus-within:outline-offset-1; 310 + @apply hover:outline-solid; 311 @apply hover:outline-2; 312 @apply hover:outline-offset-1; 313 } ··· 322 @apply hover:border-tertiary; 323 324 @apply focus:border-tertiary; 325 + @apply focus:outline-solid; 326 @apply focus:outline-tertiary; 327 @apply focus:outline-2; 328 @apply focus:outline-offset-1; 329 330 @apply focus-within:border-tertiary; 331 + @apply focus-within:outline-solid; 332 @apply focus-within:outline-tertiary; 333 @apply focus-within:outline-2; 334 @apply focus-within:outline-offset-1; ··· 342 @apply border; 343 @apply border-border-light; 344 @apply rounded-lg; 345 + @apply outline-solid; 346 @apply outline-offset-1; 347 @apply outline-2; 348 @apply outline-transparent; ··· 353 @apply border; 354 @apply border-border; 355 @apply rounded-lg; 356 + @apply outline-solid; 357 @apply outline-offset-1; 358 @apply outline-2; 359 @apply outline-border; ··· 372 } 373 374 .container { 375 + background: rgba(var(--bg-page), 0.75); 376 @apply border; 377 @apply border-bg-page; 378 @apply rounded-md;
app/home/AccountSettings.tsx app/home/Actions/AccountSettings.tsx
+1 -1
app/home/CreateNewButton.tsx app/home/Actions/CreateNewButton.tsx
··· 56 id="new-leaflet-button" 57 primary 58 icon=<AddTiny className="m-1 shrink-0" /> 59 - label="New Doc" 60 /> 61 } 62 >
··· 56 id="new-leaflet-button" 57 primary 58 icon=<AddTiny className="m-1 shrink-0" /> 59 + label="New" 60 /> 61 } 62 >
+26
app/home/HomeEmpty/DiscoverIllo.tsx
···
··· 1 + import { theme } from "tailwind.config"; 2 + export const DiscoverIllo = () => { 3 + return ( 4 + <svg 5 + className="mx-auto" 6 + width="40" 7 + height="48" 8 + viewBox="0 0 40 48" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M27.995 7.21895C28.3796 6.88667 30.0087 5.46507 30.4 2.87748C30.7913 0.289891 32.8883 0.607118 32.497 3.19459C32.1056 5.78206 32.9051 7.53471 33.1993 8.00597C33.4935 8.47722 33.9112 9.87785 36.1378 10.2148C38.3645 10.5518 38.0167 12.8516 35.79 12.5149C33.5633 12.1782 33.0608 13.1244 32.3297 13.7561C31.5986 14.3878 30.6667 15.3456 30.2851 17.8686C29.9036 20.3915 27.7969 20.1388 28.1882 17.5515C28.5795 14.9641 27.5024 13.2044 27.3795 13.0075C27.2565 12.8106 25.9473 11.0264 24.2028 10.7626C22.4582 10.4988 22.8231 8.2013 24.5506 8.46255C26.2782 8.72381 27.6104 7.55122 27.995 7.21895Z" 14 + fill={theme.colors["accent-2"]} 15 + /> 16 + <path 17 + d="M14.0436 10.775C14.8742 10.425 15.789 10.2979 16.6757 10.4806C21.184 11.9238 25.7155 13.2951 30.2265 14.7299C33.7072 15.8375 35.1769 19.9862 33.9774 23.7289C32.7772 27.472 29.1634 30.0064 25.682 28.8996C25.0272 28.6913 24.4433 28.376 23.9352 27.9753C24.0578 29.8258 23.5966 31.7973 22.5415 33.5507C20.1175 37.5776 15.2021 39.3975 11.4706 37.17C10.3639 36.5092 9.64595 35.5804 8.92715 34.5472L2.07416 24.6709C1.57874 23.7422 1.40352 22.6478 1.46822 21.605C0.876338 21.6639 0.331032 21.2463 0.234179 20.6453C0.0915272 19.7582 0.253475 18.7713 0.683459 17.8694C1.10716 16.981 1.75459 16.2504 2.51161 15.8132C3.26248 15.3796 4.24046 15.1818 5.15852 15.6351C5.4442 15.7762 5.64396 16.0231 5.73785 16.3048C6.85704 16.0396 7.98533 16.1226 8.87394 16.3813L9.25524 16.5079L9.2715 16.5153L9.8597 16.7611C10.0213 16.6144 10.2168 16.4391 10.4346 16.2548C10.6319 16.0878 10.8582 15.906 11.0967 15.7234C10.6482 15.6994 10.2675 15.3614 10.21 14.9021C10.0757 13.8176 10.3941 12.7515 10.9253 11.951C11.4368 11.1802 12.2818 10.4694 13.3165 10.4497C13.6068 10.4444 13.8663 10.5731 14.0436 10.775ZM19.2798 23.5847C16.693 22.2682 13.0521 23.4364 11.0819 26.7095C9.04105 30.1008 9.84611 34.0029 12.4431 35.5539C15.0407 37.1046 18.8789 35.9739 20.9203 32.5822C22.8879 29.3123 22.2072 25.5671 19.8281 23.9129L19.2798 23.5847ZM12.5066 27.9296C13.8894 25.2252 16.6958 23.8876 18.7744 24.9417C20.8524 25.9961 21.1485 29.4309 20.0335 31.7462C18.9185 34.0614 15.8458 35.7888 13.7672 34.7356C11.6887 33.6815 11.1239 30.6341 12.5066 27.9296ZM18.863 29.3824C18.4624 29.2737 18.0443 29.5112 17.9305 29.9137C17.5451 31.2786 16.6274 32.2476 15.4816 32.5145C15.0747 32.6093 14.8171 33.0162 14.9067 33.4227C14.9968 33.8287 15.4001 34.0811 15.8067 33.9864C17.6111 33.5659 18.8818 32.0851 19.3832 30.3097C19.4968 29.907 19.2635 29.4916 18.863 29.3824ZM8.3153 18.1814C6.98035 17.7934 5.02731 18.0142 3.96143 19.774C3.02036 21.3284 3.35247 23.0216 3.70427 23.7054L8.0744 30.005C8.19007 28.557 8.65007 27.0839 9.45919 25.7395C10.78 23.5456 12.8405 22.0084 15.0456 21.4225L13.8072 20.8264L13.7998 20.8234L12.0263 19.9506C11.9598 19.9179 11.9009 19.8749 11.846 19.8299L9.65871 18.7201L8.57393 18.2668L8.3153 18.1814ZM19.0995 26.5799C18.8494 26.2497 18.3773 26.1876 18.0443 26.4416C17.7113 26.6963 17.6434 27.1707 17.8935 27.5013C17.9343 27.5552 17.977 27.6287 18.0132 27.7089C18.0501 27.7907 18.07 27.8577 18.0768 27.8914C18.1585 28.2993 18.5552 28.5591 18.9635 28.4728C19.3719 28.3861 19.6373 27.986 19.5562 27.5779C19.4936 27.265 19.3269 26.8805 19.0995 26.5799ZM29.6516 16.5241C27.4753 15.8319 24.7243 17.3802 23.7327 20.4717C22.7415 23.5634 24.082 26.4132 26.2584 27.1054C28.4347 27.7968 31.1846 26.2478 32.1759 23.1564C33.1666 20.0654 31.8272 17.217 29.6516 16.5241ZM25.0864 20.9088C25.8644 18.6948 27.8745 17.3801 29.5763 17.9724C31.2782 18.5655 31.8625 21.1674 31.2492 23.0563C30.6358 24.9452 28.4614 26.5857 26.7594 25.9927C25.0576 25.3994 24.3085 23.1233 25.0864 20.9088ZM30.1039 21.3842C29.7952 21.3437 29.5093 21.5623 29.4654 21.8729C29.3114 22.9627 28.7055 23.8052 27.8486 24.1307C27.5561 24.2419 27.4059 24.5693 27.5131 24.8623C27.6207 25.1552 27.9446 25.3027 28.2373 25.192C29.5689 24.6865 30.387 23.4274 30.5857 22.0201C30.6291 21.7096 30.4126 21.4251 30.1039 21.3842ZM16.2087 12.3087C15.6227 12.192 14.6928 12.3931 13.9963 12.9887C13.5732 13.3506 13.2385 13.8583 13.1584 14.5459C13.1767 14.5393 13.1948 14.5317 13.213 14.5253C13.7448 14.3389 14.2572 14.2221 14.6318 14.1515C14.8204 14.1159 14.9777 14.0907 15.09 14.0749C15.3279 14.0415 15.553 14.0285 15.7831 14.1367L20.1739 16.2018C20.4022 16.3094 20.5779 16.5046 20.6616 16.7419C20.7451 16.9794 20.7295 17.2415 20.6188 17.4676C20.0298 18.6703 19.6797 19.9843 19.6168 21.5873L20.1887 21.9303C20.3029 21.99 20.4164 22.0532 20.5286 22.1202C20.8892 22.3354 21.2207 22.5792 21.5247 22.8458C21.4865 21.8762 21.6184 20.8729 21.9311 19.8976C22.5837 17.8636 23.9491 16.1876 25.6185 15.2583L16.2087 12.3087ZM29.9842 19.2073C29.7647 18.9869 29.4064 18.9891 29.1846 19.2117C28.9637 19.4344 28.9614 19.7935 29.1802 20.0139C29.2181 20.052 29.2604 20.1041 29.2969 20.1626C29.334 20.222 29.3556 20.2728 29.3649 20.2995C29.4667 20.5942 29.7886 20.7483 30.0832 20.6439C30.378 20.5386 30.5364 20.2134 30.4349 19.9182C30.3553 19.6875 30.1879 19.4123 29.9842 19.2073ZM18.1891 18.5259C17.7543 18.6688 17.3359 18.813 17.0378 18.938C16.7617 19.0538 16.3329 19.3654 15.8895 19.7357L17.7842 20.6483C17.8606 19.8991 17.9962 19.1952 18.1891 18.5259ZM14.9836 16.0031C14.6688 16.0624 14.2552 16.1582 13.8411 16.3033C13.6711 16.3629 13.5046 16.4296 13.3475 16.5035C12.8795 16.7239 12.263 17.1843 11.71 17.6486L14.054 18.8379C14.1789 18.7233 14.3197 18.5926 14.4737 18.4596C14.9623 18.0377 15.6797 17.4632 16.3048 17.2012C16.554 17.0967 16.8576 16.9855 17.1753 16.8759L15.2333 15.9604C15.1623 15.9716 15.0778 15.9853 14.9836 16.0031Z" 18 + fill={theme.colors["accent-1"]} 19 + /> 20 + <path 21 + d="M10.8586 38.4374C11.0345 38.0999 11.7756 36.6608 11.3198 34.7706C10.8641 32.8804 12.4238 32.5044 12.8795 34.3945C13.3352 36.2847 14.3902 37.2634 14.7294 37.5041C15.0687 37.7448 15.7567 38.5896 17.4129 38.1905C19.0691 37.7913 19.4742 39.4713 17.818 39.8706C16.1619 40.27 16.0766 41.063 15.7422 41.7045C15.4079 42.346 15.0247 43.2688 15.4691 45.1118C15.9135 46.9548 14.3652 47.3779 13.9094 45.4878C13.4537 43.5978 12.2021 42.6929 12.0603 42.5923C11.9186 42.4917 10.4973 41.6358 9.19972 41.9486C7.90219 42.2615 7.50971 40.5782 8.79465 40.2684C10.0796 39.9586 10.6827 38.7749 10.8586 38.4374Z" 22 + fill={theme.colors["accent-2"]} 23 + /> 24 + </svg> 25 + ); 26 + };
+108
app/home/HomeEmpty/HomeEmpty.tsx
···
··· 1 + "use client"; 2 + 3 + import { PubListEmptyIllo } from "components/ActionBar/Publications"; 4 + import { ButtonPrimary } from "components/Buttons"; 5 + import { AddSmall } from "components/Icons/AddSmall"; 6 + import { Link } from "react-aria-components"; 7 + import { DiscoverIllo } from "./DiscoverIllo"; 8 + import { WelcomeToLeafletIllo } from "./WelcomeToLeafletIllo"; 9 + import { DiscoverSmall } from "components/Icons/DiscoverSmall"; 10 + import { PublishSmall } from "components/Icons/PublishSmall"; 11 + import { createNewLeaflet } from "actions/createNewLeaflet"; 12 + import { useIsMobile } from "src/hooks/isMobile"; 13 + 14 + export function HomeEmptyState() { 15 + let isMobile = useIsMobile(); 16 + return ( 17 + <div className="flex flex-col gap-4 font-bold"> 18 + <div className="container p-2 flex gap-4"> 19 + <div className="w-[72px]"> 20 + <WelcomeToLeafletIllo /> 21 + </div> 22 + <div className="flex flex-col grow"> 23 + <h3 className="text-xl font-semibold pt-2">Leaflet</h3> 24 + {/*<h3>A platform for social publishing.</h3>*/} 25 + <div className="font-normal text-tertiary italic"> 26 + Write and share delightful documents! 27 + </div> 28 + <ButtonPrimary 29 + className="!text-lg my-3" 30 + onClick={async () => { 31 + let openNewLeaflet = (id: string) => { 32 + if (isMobile) { 33 + window.location.href = `/${id}?focusFirstBlock`; 34 + } else { 35 + window.open(`/${id}?focusFirstBlock`, "_blank"); 36 + } 37 + }; 38 + 39 + let id = await createNewLeaflet({ 40 + pageType: "doc", 41 + redirectUser: false, 42 + }); 43 + openNewLeaflet(id); 44 + }} 45 + > 46 + <AddSmall /> Write a Doc! 47 + </ButtonPrimary> 48 + </div> 49 + </div> 50 + <div className="flex gap-2 w-full items-center text-tertiary font-normal italic"> 51 + <hr className="border-border w-full" /> 52 + <div>or</div> 53 + <hr className="border-border w-full" /> 54 + </div> 55 + 56 + <PublicationBanner /> 57 + <DiscoverBanner /> 58 + <div className="text-tertiary italic text-sm font-normal -mt-2"> 59 + Right now docs and publications are separate. Soon you'll be able to add 60 + docs to pubs! 61 + </div> 62 + </div> 63 + ); 64 + } 65 + 66 + export const PublicationBanner = (props: { small?: boolean }) => { 67 + return ( 68 + <div 69 + className={`accent-container flex sm:py-2 gap-4 items-center ${props.small ? "items-start p-2 text-sm font-normal" : "items-center p-4"}`} 70 + > 71 + {props.small ? ( 72 + <PublishSmall className="shrink-0" /> 73 + ) : ( 74 + <div className="w-[64px] mx-auto"> 75 + <PubListEmptyIllo /> 76 + </div> 77 + )} 78 + <div className="grow"> 79 + <Link href={"/lish/createPub"} className="font-bold"> 80 + Start a Publication 81 + </Link>{" "} 82 + and blog in the Atmosphere 83 + </div> 84 + </div> 85 + ); 86 + }; 87 + 88 + export const DiscoverBanner = (props: { small?: boolean }) => { 89 + return ( 90 + <div 91 + className={`accent-container flex sm:py-2 gap-4 items-center ${props.small ? "items-start p-2 text-sm font-normal" : "items-center p-4"}`} 92 + > 93 + {props.small ? ( 94 + <DiscoverSmall className="shrink-0" /> 95 + ) : ( 96 + <div className="w-[64px] mx-auto"> 97 + <DiscoverIllo /> 98 + </div> 99 + )} 100 + <div className="grow"> 101 + <Link href={"/discover"} className="font-bold"> 102 + Explore Publications 103 + </Link>{" "} 104 + on art, tech, games, music & more! 105 + </div> 106 + </div> 107 + ); 108 + };
+24
app/home/HomeEmpty/WelcomeToLeafletIllo.tsx
···
··· 1 + import { theme } from "tailwind.config"; 2 + 3 + export const WelcomeToLeafletIllo = () => { 4 + return ( 5 + <svg 6 + width="73" 7 + height="68" 8 + viewBox="0 0 73 68" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M21.7639 12.9818C39.5065 8.22165 72.8274 1.9906 72.8274 11.684C72.8274 13.2356 72.5131 14.6375 71.9885 15.9193C71.422 15.1994 70.6084 14.987 69.927 14.6595C69.0312 14.3006 68.1186 14.0196 67.219 13.7347C65.9446 13.3234 64.6378 12.9769 63.4944 12.3705C63.0971 12.1482 62.6977 11.8678 62.5598 11.4935C62.4128 11.1213 62.6531 10.6704 62.9592 10.3334C63.598 9.64116 64.4397 9.13591 65.3 8.65466C64.4055 9.0767 63.5312 9.49833 62.7922 10.1713C62.4481 10.4956 62.061 11.0041 62.219 11.5961C62.3834 12.1584 62.8383 12.4935 63.2483 12.7738C64.436 13.5238 65.7311 13.9496 66.9934 14.4086C67.8817 14.7243 68.7738 15.0378 69.6096 15.4095C70.4149 15.7681 71.3704 16.291 71.4895 16.8314C71.4933 16.884 71.4915 16.9 71.3928 17.0043C71.2883 17.1047 71.1421 17.196 70.9524 17.2894C70.6123 17.455 70.1758 17.5978 69.7727 17.7084C68.9351 17.937 68.049 18.1027 67.1721 18.2513C65.4162 18.5448 63.6217 18.7708 61.8323 19.0199C60.0416 19.2716 58.2462 19.5223 56.429 19.933C55.5221 20.1485 54.6197 20.3715 53.6711 20.8383C53.2374 21.0796 52.6446 21.3532 52.2737 22.1595C52.0934 22.554 52.1241 23.1248 52.2629 23.4594C52.4237 23.8666 52.582 24.0666 52.8069 24.3275C55.2093 26.8202 58.4399 26.9744 61.1399 27.4711C61.3262 27.5015 61.5132 27.5331 61.6995 27.5638C60.4533 29.2073 60.129 30.8258 61.717 32.7035C53.3556 36.0694 40.5573 34.9986 30.1731 37.5492C27.0455 38.2875 23.9357 39.315 21.0657 41.3636C19.6745 42.3955 18.2597 43.7123 17.4534 45.7152C16.5587 47.6874 17.1166 50.6073 18.6731 52.1156C19.6636 53.1771 20.8229 53.8313 21.9641 54.3002C22.9895 54.7165 24.0905 54.774 25.1536 54.5228C25.4784 54.4319 25.7983 54.2981 26.054 54.0844C26.3095 53.8704 26.4797 53.5942 26.5325 53.2894C26.5851 52.9847 26.5177 52.6675 26.3489 52.3802C26.1797 52.0928 25.9225 51.8583 25.6467 51.6635C24.8912 51.0971 24.2127 50.692 23.5432 50.4203C22.7927 50.117 22.1473 49.7103 21.7405 49.2709C21.1437 48.5942 21.0342 48.064 21.3293 47.2084C21.6382 46.365 22.4832 45.4184 23.5022 44.6605C25.5969 43.1038 28.3033 42.084 31.094 41.3705C37.0436 39.9193 43.4459 39.2868 49.7786 38.347C52.9572 37.8792 56.1424 37.383 59.342 36.6058C60.9443 36.2072 62.5256 35.7578 64.136 35.0453C64.1883 35.0196 64.2409 34.9932 64.2942 34.9672C72.897 41.9183 73.7798 46.9821 71.0959 52.3617C68.9368 56.6893 57.6782 70.1038 45.5647 66.642C17.1652 58.5254 -0.313688 61.8818 0.983643 36.35C1.62827 23.6653 13.1137 15.3026 21.7639 12.9818ZM47.5911 44.0482C47.6939 42.4007 46.335 42.3157 46.2317 43.9633C46.1286 45.6104 45.1675 46.5931 44.9397 46.8236C44.7125 47.0535 43.9263 47.8634 42.8059 47.7933C41.6859 47.7235 41.5833 49.1871 42.7141 49.2582C43.8449 49.329 44.7847 50.3786 44.8752 50.4965C44.9656 50.6142 45.7535 51.6618 45.6506 53.309C45.5476 54.9569 46.9105 55.0009 47.011 53.3939C47.1116 51.7875 47.6534 51.1346 48.0852 50.6976C48.5171 50.2607 48.7857 49.6386 50.2297 49.7289C51.6728 49.8185 51.7638 48.3548 50.3206 48.264C48.8768 48.1734 48.5288 47.3157 48.3137 47.0355C48.0986 46.7552 47.488 45.6961 47.5911 44.0482ZM18.887 18.7377C18.8903 16.7042 17.2118 16.7019 17.2083 18.7357C17.2044 20.7693 16.0954 22.0494 15.8333 22.349C15.5704 22.6494 14.6652 23.703 13.2834 23.7006C11.901 23.6983 11.8845 25.5056 13.2805 25.5082C14.675 25.5108 15.9068 26.7289 16.0286 26.8685C16.1478 27.0058 17.1971 28.2352 17.1936 30.2689C17.1902 32.3024 18.8677 32.2546 18.8713 30.2719C18.8748 28.2891 19.4922 27.4449 19.9905 26.8754C20.4888 26.3059 20.7721 25.5208 22.554 25.5238C24.336 25.5269 24.3396 23.7198 22.5579 23.7162C20.7757 23.7129 20.2846 22.6833 19.9993 22.3549C19.7136 22.0258 18.8835 20.7706 18.887 18.7377ZM70.5559 18.5209C68.5834 21.3602 65.7167 23.5582 63.5715 25.5668C62.8433 25.4447 62.1381 25.3759 61.4397 25.2875C58.698 24.9636 55.7947 24.7314 54.1506 23.0892C53.1448 22.9109 55.2165 21.8481 56.8167 21.475C58.5129 21.0124 60.2807 20.6842 62.0471 20.3597C63.8164 20.0365 65.6066 19.7363 67.384 19.3646C68.2733 19.1761 69.1747 18.9708 70.0657 18.6898C70.2249 18.6387 70.3894 18.5819 70.5559 18.5209Z" 14 + fill="color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 85%)" 15 + /> 16 + <path 17 + fill-rule="evenodd" 18 + clip-rule="evenodd" 19 + d="M33.1936 19.9644C33.8224 20.2936 34.5595 20.6793 35.7586 19.9049C36.5426 19.3985 37.1772 17.1878 37.8852 14.7211C38.9202 11.1155 40.1122 6.96302 42.1573 6.78723C44.6243 6.57519 44.7981 7.92712 44.9319 8.96695C45.0257 9.69631 45.0997 10.2721 45.9315 10.047C46.7788 9.8176 47.3647 8.61374 48.0447 7.2167C48.984 5.28694 50.1027 2.98859 52.3373 2.38086C53.9128 1.95238 54.054 2.64187 54.1973 3.34108C54.2888 3.788 54.3812 4.2389 54.8494 4.40436C55.3107 4.56736 56.2896 4.23303 57.4149 3.84869C59.219 3.23253 61.3994 2.48783 62.4269 3.45769C63.3302 4.31032 62.738 5.52027 62.1704 6.67999C61.6896 7.66228 61.2264 8.60853 61.7046 9.27112C62.0948 9.81171 62.6783 10.0278 63.2502 10.2396C64.1857 10.5861 65.0904 10.9211 65.0681 12.6458C65.0445 14.4695 62.5034 15.5801 60.1214 16.6211C58.126 17.4931 56.2423 18.3164 56.0441 19.4692C55.9197 20.1921 56.464 20.3823 57.0738 20.5954C57.8026 20.85 58.6249 21.1373 58.5116 22.4058C58.2468 25.3711 55.1373 26.1276 52.219 26.8377C49.7141 27.4471 47.3501 28.0223 47.0466 29.9306C46.8714 31.0323 47.5477 31.0548 48.2898 31.0794C49.1981 31.1095 50.205 31.1429 49.869 33.1634C49.2261 37.03 43.6557 38.4183 38.5564 39.6893C36.6866 40.1553 34.8802 40.6056 33.4032 41.1564C33.2347 41.2193 33.1049 41.3564 33.0503 41.5278C32.2094 44.1647 31.8313 46.932 31.428 49.8838C31.3394 50.5323 31.2496 51.1896 31.1534 51.8565C31.0262 52.738 29.2486 55.1385 28.3628 55.2311C27.9656 55.2726 27.8339 52.2775 27.9611 51.3959C28.4347 48.1126 29.1515 45.0249 30.0528 42.1314C31.418 36.9751 34.9339 30.201 39.0683 24.7487C42.7265 19.8134 46.4117 15.8889 50.9826 12.4792C51.5547 12.0524 51.1837 11.3521 50.5642 11.7068C46.3602 14.1137 42.9934 17.6783 39.4855 21.2985C35.6701 25.2361 32.8282 29.9969 31.1838 33.3557C30.8939 33.9479 29.8993 33.7609 29.8756 33.102C29.6905 27.9469 29.636 23.1879 30.7621 21.1685C31.8313 19.2514 32.4334 19.5665 33.1936 19.9644Z" 20 + fill={theme.colors["accent-1"]} 21 + /> 22 + </svg> 23 + ); 24 + };
-25
app/home/HomeFooter.tsx
··· 1 - "use client"; 2 - import { Footer } from "components/ActionBar/Footer"; 3 - import { Media } from "components/Media"; 4 - import { ThemePopover } from "components/ThemeManager/ThemeSetter"; 5 - import { CreateNewLeafletButton } from "./CreateNewButton"; 6 - import { HelpPopover } from "components/HelpPopover"; 7 - import { AccountSettings } from "./AccountSettings"; 8 - import { useIdentityData } from "components/IdentityProvider"; 9 - import { useReplicache } from "src/replicache"; 10 - import { LoginActionButton } from "components/LoginButton"; 11 - 12 - export const HomeFooter = () => { 13 - let { identity } = useIdentityData(); 14 - let { rootEntity } = useReplicache(); 15 - return ( 16 - <Media mobile> 17 - <Footer> 18 - <CreateNewLeafletButton /> 19 - {identity ? <AccountSettings /> : <LoginActionButton />} 20 - <HelpPopover noShortcuts /> 21 - <ThemePopover entityID={rootEntity} home /> 22 - </Footer> 23 - </Media> 24 - ); 25 - };
···
app/home/HomeHelp.tsx app/home/Actions/HomeHelp.tsx
+334
app/home/HomeLayout.tsx
···
··· 1 + "use client"; 2 + 3 + import { getHomeDocs, HomeDoc } from "./storage"; 4 + import useSWR from "swr"; 5 + import { 6 + Fact, 7 + PermissionToken, 8 + ReplicacheProvider, 9 + useEntity, 10 + } from "src/replicache"; 11 + import { LeafletListItem } from "./LeafletList/LeafletListItem"; 12 + import { useIdentityData } from "components/IdentityProvider"; 13 + import type { Attribute } from "src/replicache/attributes"; 14 + import { callRPC } from "app/api/rpc/client"; 15 + import { StaticLeafletDataContext } from "components/PageSWRDataProvider"; 16 + import { HomeSmall } from "components/Icons/HomeSmall"; 17 + import { 18 + HomeDashboardControls, 19 + DashboardLayout, 20 + DashboardState, 21 + useDashboardState, 22 + } from "components/PageLayouts/DashboardLayout"; 23 + import { Actions } from "./Actions/Actions"; 24 + import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 25 + import { Json } from "supabase/database.types"; 26 + import { useTemplateState } from "./Actions/CreateNewButton"; 27 + import { CreateNewLeafletButton } from "./Actions/CreateNewButton"; 28 + import { ActionButton } from "components/ActionBar/ActionButton"; 29 + import { AddTiny } from "components/Icons/AddTiny"; 30 + import { 31 + get_leaflet_data, 32 + GetLeafletDataReturnType, 33 + } from "app/api/rpc/[command]/get_leaflet_data"; 34 + import { useEffect, useRef, useState } from "react"; 35 + import { Input } from "components/Input"; 36 + import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 37 + import { 38 + ButtonPrimary, 39 + ButtonSecondary, 40 + ButtonTertiary, 41 + } from "components/Buttons"; 42 + import { AddSmall } from "components/Icons/AddSmall"; 43 + import { PublishIllustration } from "app/[leaflet_id]/publish/PublishIllustration/PublishIllustration"; 44 + import { PubListEmptyIllo } from "components/ActionBar/Publications"; 45 + import { theme } from "tailwind.config"; 46 + import Link from "next/link"; 47 + import { DiscoverIllo } from "./HomeEmpty/DiscoverIllo"; 48 + import { WelcomeToLeafletIllo } from "./HomeEmpty/WelcomeToLeafletIllo"; 49 + import { 50 + DiscoverBanner, 51 + HomeEmptyState, 52 + PublicationBanner, 53 + } from "./HomeEmpty/HomeEmpty"; 54 + 55 + type Leaflet = { 56 + added_at: string; 57 + token: PermissionToken & { 58 + leaflets_in_publications?: Exclude< 59 + GetLeafletDataReturnType["result"]["data"], 60 + null 61 + >["leaflets_in_publications"]; 62 + }; 63 + }; 64 + 65 + export const HomeLayout = (props: { 66 + entityID: string; 67 + titles: { [root_entity: string]: string }; 68 + initialFacts: { 69 + [root_entity: string]: Fact<Attribute>[]; 70 + }; 71 + }) => { 72 + let hasBackgroundImage = !!useEntity( 73 + props.entityID, 74 + "theme/background-image", 75 + ); 76 + let cardBorderHidden = !!useCardBorderHidden(props.entityID); 77 + 78 + let [searchValue, setSearchValue] = useState(""); 79 + let [debouncedSearchValue, setDebouncedSearchValue] = useState(""); 80 + 81 + useDebouncedEffect( 82 + () => { 83 + setDebouncedSearchValue(searchValue); 84 + }, 85 + 200, 86 + [searchValue], 87 + ); 88 + 89 + let { identity } = useIdentityData(); 90 + 91 + let hasPubs = !identity || identity.publications.length === 0 ? false : true; 92 + let hasTemplates = 93 + useTemplateState((s) => s.templates).length === 0 ? false : true; 94 + 95 + return ( 96 + <DashboardLayout 97 + id="home" 98 + hasBackgroundImage={hasBackgroundImage} 99 + currentPage="home" 100 + defaultTab="home" 101 + actions={<Actions />} 102 + tabs={{ 103 + home: { 104 + controls: ( 105 + <HomeDashboardControls 106 + defaultDisplay={"grid"} 107 + searchValue={searchValue} 108 + setSearchValueAction={setSearchValue} 109 + hasBackgroundImage={hasBackgroundImage} 110 + hasPubs={hasPubs} 111 + hasTemplates={hasTemplates} 112 + /> 113 + ), 114 + content: ( 115 + <HomeLeafletList 116 + titles={props.titles} 117 + initialFacts={props.initialFacts} 118 + cardBorderHidden={cardBorderHidden} 119 + searchValue={debouncedSearchValue} 120 + /> 121 + ), 122 + }, 123 + }} 124 + /> 125 + ); 126 + }; 127 + 128 + export function HomeLeafletList(props: { 129 + titles: { [root_entity: string]: string }; 130 + initialFacts: { 131 + [root_entity: string]: Fact<Attribute>[]; 132 + }; 133 + searchValue: string; 134 + cardBorderHidden: boolean; 135 + }) { 136 + let { identity } = useIdentityData(); 137 + let { data: initialFacts } = useSWR( 138 + "home-leaflet-data", 139 + async () => { 140 + if (identity) { 141 + let { result } = await callRPC("getFactsFromHomeLeaflets", { 142 + tokens: identity.permission_token_on_homepage.map( 143 + (ptrh) => ptrh.permission_tokens.root_entity, 144 + ), 145 + }); 146 + let titles = { 147 + ...result.titles, 148 + ...identity.permission_token_on_homepage.reduce( 149 + (acc, tok) => { 150 + let title = 151 + tok.permission_tokens.leaflets_in_publications[0]?.title; 152 + if (title) acc[tok.permission_tokens.root_entity] = title; 153 + return acc; 154 + }, 155 + {} as { [k: string]: string }, 156 + ), 157 + }; 158 + return { ...result, titles }; 159 + } 160 + }, 161 + { fallbackData: { facts: props.initialFacts, titles: props.titles } }, 162 + ); 163 + 164 + let { data: localLeaflets } = useSWR("leaflets", () => getHomeDocs(), { 165 + fallbackData: [], 166 + }); 167 + let leaflets: Leaflet[] = identity 168 + ? identity.permission_token_on_homepage.map((ptoh) => ({ 169 + added_at: ptoh.created_at, 170 + token: ptoh.permission_tokens as PermissionToken, 171 + })) 172 + : localLeaflets 173 + .sort((a, b) => (a.added_at > b.added_at ? -1 : 1)) 174 + .filter((d) => !d.hidden) 175 + .map((ll) => ll); 176 + 177 + return leaflets.length === 0 ? ( 178 + <HomeEmptyState /> 179 + ) : ( 180 + <> 181 + <LeafletList 182 + defaultDisplay="grid" 183 + searchValue={props.searchValue} 184 + leaflets={leaflets} 185 + titles={initialFacts?.titles || {}} 186 + cardBorderHidden={props.cardBorderHidden} 187 + initialFacts={initialFacts?.facts || {}} 188 + showPreview 189 + /> 190 + <div className="spacer h-4 w-full bg-transparent shrink-0 " /> 191 + 192 + {leaflets.filter((l) => !!l.token.leaflets_in_publications).length === 193 + 0 && <PublicationBanner small />} 194 + <DiscoverBanner small /> 195 + <div className="spacer h-8 w-full bg-transparent shrink-0 " /> 196 + </> 197 + ); 198 + } 199 + 200 + export function LeafletList(props: { 201 + leaflets: Leaflet[]; 202 + titles: { [root_entity: string]: string }; 203 + defaultDisplay: Exclude<DashboardState["display"], undefined>; 204 + initialFacts: { 205 + [root_entity: string]: Fact<Attribute>[]; 206 + }; 207 + searchValue: string; 208 + cardBorderHidden: boolean; 209 + showPreview?: boolean; 210 + }) { 211 + let { identity } = useIdentityData(); 212 + let { display } = useDashboardState(); 213 + 214 + display = display || props.defaultDisplay; 215 + 216 + let searchedLeaflets = useSearchedLeaflets( 217 + props.leaflets, 218 + props.titles, 219 + props.searchValue, 220 + ); 221 + 222 + return ( 223 + <div 224 + className={` 225 + leafletList 226 + w-full 227 + ${display === "grid" ? "grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-4 gap-x-4 sm:gap-x-6 sm:gap-y-5 grow" : "flex flex-col gap-2 pt-2"} `} 228 + > 229 + {props.leaflets.map(({ token: leaflet, added_at }, index) => ( 230 + <ReplicacheProvider 231 + disablePull 232 + initialFactsOnly={!!identity} 233 + key={leaflet.id} 234 + rootEntity={leaflet.root_entity} 235 + token={leaflet} 236 + name={leaflet.root_entity} 237 + initialFacts={props.initialFacts?.[leaflet.root_entity] || []} 238 + > 239 + <StaticLeafletDataContext 240 + value={{ 241 + ...leaflet, 242 + leaflets_in_publications: leaflet.leaflets_in_publications || [], 243 + blocked_by_admin: null, 244 + custom_domain_routes: [], 245 + }} 246 + > 247 + <LeafletListItem 248 + title={props?.titles?.[leaflet.root_entity] || "Untitled"} 249 + token={leaflet} 250 + draft={!!leaflet.leaflets_in_publications?.length} 251 + published={!!leaflet.leaflets_in_publications?.find((l) => l.doc)} 252 + publishedAt={ 253 + leaflet.leaflets_in_publications?.find((l) => l.doc)?.documents 254 + ?.indexed_at 255 + } 256 + leaflet_id={leaflet.root_entity} 257 + loggedIn={!!identity} 258 + display={display} 259 + added_at={added_at} 260 + cardBorderHidden={props.cardBorderHidden} 261 + index={index} 262 + showPreview={props.showPreview} 263 + isHidden={ 264 + !searchedLeaflets.some( 265 + (sl) => sl.token.root_entity === leaflet.root_entity, 266 + ) 267 + } 268 + /> 269 + </StaticLeafletDataContext> 270 + </ReplicacheProvider> 271 + ))} 272 + </div> 273 + ); 274 + } 275 + 276 + function useSearchedLeaflets( 277 + leaflets: Leaflet[], 278 + titles: { [root_entity: string]: string }, 279 + searchValue: string, 280 + ) { 281 + let { sort, filter } = useDashboardState(); 282 + 283 + let sortedLeaflets = leaflets.sort((a, b) => { 284 + if (sort === "alphabetical") { 285 + if (titles[a.token.root_entity] === titles[b.token.root_entity]) { 286 + return a.added_at > b.added_at ? -1 : 1; 287 + } else { 288 + return titles[a.token.root_entity].toLocaleLowerCase() > 289 + titles[b.token.root_entity].toLocaleLowerCase() 290 + ? 1 291 + : -1; 292 + } 293 + } else { 294 + return a.added_at === b.added_at 295 + ? a.token.root_entity > b.token.root_entity 296 + ? -1 297 + : 1 298 + : a.added_at > b.added_at 299 + ? -1 300 + : 1; 301 + } 302 + }); 303 + 304 + let allTemplates = useTemplateState((s) => s.templates); 305 + let filteredLeaflets = sortedLeaflets.filter(({ token: leaflet }) => { 306 + let published = !!leaflet.leaflets_in_publications?.find((l) => l.doc); 307 + let drafts = !!leaflet.leaflets_in_publications?.length && !published; 308 + let docs = !leaflet.leaflets_in_publications?.length; 309 + let templates = !!allTemplates.find((t) => t.id === leaflet.id); 310 + // If no filters are active, show all 311 + if ( 312 + !filter.drafts && 313 + !filter.published && 314 + !filter.docs && 315 + !filter.templates 316 + ) 317 + return true; 318 + 319 + return ( 320 + (filter.drafts && drafts) || 321 + (filter.published && published) || 322 + (filter.docs && docs) || 323 + (filter.templates && templates) 324 + ); 325 + }); 326 + if (searchValue === "") return filteredLeaflets; 327 + let searchedLeaflets = filteredLeaflets.filter(({ token: leaflet }) => { 328 + return titles[leaflet.root_entity] 329 + ?.toLowerCase() 330 + .includes(searchValue.toLowerCase()); 331 + }); 332 + 333 + return searchedLeaflets; 334 + }
+4 -9
app/home/HomeSidebar.tsx app/home/Actions/Actions.tsx
··· 3 import { CreateNewLeafletButton } from "./CreateNewButton"; 4 import { HelpPopover } from "components/HelpPopover"; 5 import { AccountSettings } from "./AccountSettings"; 6 - import { Sidebar } from "components/ActionBar/Sidebar"; 7 import { useIdentityData } from "components/IdentityProvider"; 8 import { useReplicache } from "src/replicache"; 9 import { LoginActionButton } from "components/LoginButton"; 10 - import { MyPublicationList } from "./Publications"; 11 12 - export const HomeSidebar = () => { 13 let { identity } = useIdentityData(); 14 let { rootEntity } = useReplicache(); 15 - 16 return ( 17 - <Sidebar alwaysOpen className="my-6"> 18 <CreateNewLeafletButton /> 19 {identity ? <AccountSettings /> : <LoginActionButton />} 20 - <HelpPopover noShortcuts /> 21 <ThemePopover entityID={rootEntity} home /> 22 - <hr className="border-bg-page" /> 23 - <MyPublicationList /> 24 - </Sidebar> 25 ); 26 };
··· 3 import { CreateNewLeafletButton } from "./CreateNewButton"; 4 import { HelpPopover } from "components/HelpPopover"; 5 import { AccountSettings } from "./AccountSettings"; 6 import { useIdentityData } from "components/IdentityProvider"; 7 import { useReplicache } from "src/replicache"; 8 import { LoginActionButton } from "components/LoginButton"; 9 10 + export const Actions = () => { 11 let { identity } = useIdentityData(); 12 let { rootEntity } = useReplicache(); 13 return ( 14 + <> 15 <CreateNewLeafletButton /> 16 {identity ? <AccountSettings /> : <LoginActionButton />} 17 + {/*<HelpPopover noShortcuts />*/} 18 <ThemePopover entityID={rootEntity} home /> 19 + </> 20 ); 21 };
-103
app/home/LeafletList.tsx
··· 1 - "use client"; 2 - 3 - import { useEffect, useState } from "react"; 4 - import { getHomeDocs, HomeDoc } from "./storage"; 5 - import useSWR from "swr"; 6 - import { Fact, PermissionToken, ReplicacheProvider } from "src/replicache"; 7 - import { LeafletPreview } from "./LeafletPreview"; 8 - import { useIdentityData } from "components/IdentityProvider"; 9 - import type { Attribute } from "src/replicache/attributes"; 10 - import { callRPC } from "app/api/rpc/client"; 11 - import { StaticLeafletDataContext } from "components/PageSWRDataProvider"; 12 - 13 - export function LeafletList(props: { 14 - initialFacts: { 15 - [root_entity: string]: Fact<Attribute>[]; 16 - }; 17 - }) { 18 - let { data: localLeaflets } = useSWR("leaflets", () => getHomeDocs(), { 19 - fallbackData: [], 20 - }); 21 - let { identity } = useIdentityData(); 22 - let { data: initialFacts } = useSWR( 23 - "home-leaflet-data", 24 - async () => { 25 - if (identity) { 26 - let { result } = await callRPC("getFactsFromHomeLeaflets", { 27 - tokens: identity.permission_token_on_homepage.map( 28 - (ptrh) => ptrh.permission_tokens.root_entity, 29 - ), 30 - }); 31 - return result; 32 - } 33 - }, 34 - { fallbackData: props.initialFacts }, 35 - ); 36 - let leaflets: Array< 37 - PermissionToken & { 38 - leaflets_in_publications?: Array<{ 39 - doc: string; 40 - description: string; 41 - publication: string; 42 - leaflet: string; 43 - title: string; 44 - publications: null; 45 - documents: null; 46 - }>; 47 - } 48 - > = identity 49 - ? identity.permission_token_on_homepage 50 - .sort((a, b) => 51 - a.created_at === b.created_at 52 - ? a.permission_tokens.root_entity > b.permission_tokens.root_entity 53 - ? -1 54 - : 1 55 - : a.created_at > b.created_at 56 - ? -1 57 - : 1, 58 - ) 59 - .map((ptoh) => ptoh.permission_tokens) 60 - : localLeaflets 61 - .sort((a, b) => (a.added_at > b.added_at ? -1 : 1)) 62 - .filter((d) => !d.hidden) 63 - .map((ll) => ll.token); 64 - 65 - return ( 66 - <div className="homeLeafletGrid grow w-full h-full"> 67 - <div className="grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-8 gap-x-4 sm:gap-x-6 sm:gap-y-8 grow pt-3 pb-28 px-2 sm:pt-6 sm:pb-12 sm:pl-6 sm:pr-1"> 68 - {leaflets.map((leaflet, index) => ( 69 - <ReplicacheProvider 70 - disablePull 71 - initialFactsOnly={!!identity} 72 - key={leaflet.id} 73 - rootEntity={leaflet.root_entity} 74 - token={leaflet} 75 - name={leaflet.root_entity} 76 - initialFacts={initialFacts?.[leaflet.root_entity] || []} 77 - > 78 - <StaticLeafletDataContext 79 - value={{ 80 - ...leaflet, 81 - leaflets_in_publications: 82 - leaflet.leaflets_in_publications || [], 83 - blocked_by_admin: null, 84 - custom_domain_routes: [], 85 - }} 86 - > 87 - <LeafletPreview 88 - index={index} 89 - token={leaflet} 90 - draft={!!leaflet.leaflets_in_publications?.length} 91 - published={ 92 - !!leaflet.leaflets_in_publications?.find((l) => l.doc) 93 - } 94 - leaflet_id={leaflet.root_entity} 95 - loggedIn={!!identity} 96 - /> 97 - </StaticLeafletDataContext> 98 - </ReplicacheProvider> 99 - ))} 100 - </div> 101 - </div> 102 - ); 103 - }
···
+68
app/home/LeafletList/LeafletContent.tsx
···
··· 1 + "use client"; 2 + import { BlockPreview } from "components/Blocks/PageLinkBlock"; 3 + import { useEffect, useRef, useState } from "react"; 4 + import { useBlocks } from "src/hooks/queries/useBlocks"; 5 + import { useEntity } from "src/replicache"; 6 + import { CanvasContent } from "components/Canvas"; 7 + import styles from "./LeafletPreview.module.css"; 8 + import { PublicationMetadataPreview } from "components/Pages/PublicationMetadata"; 9 + 10 + export const LeafletContent = (props: { 11 + entityID: string; 12 + isOnScreen: boolean; 13 + }) => { 14 + let type = useEntity(props.entityID, "page/type")?.data.value || "doc"; 15 + let blocks = useBlocks(props.entityID); 16 + let previewRef = useRef<HTMLDivElement | null>(null); 17 + 18 + if (type === "canvas") 19 + return ( 20 + <div 21 + className={`pageLinkBlockPreview shrink-0 h-full overflow-clip relative bg-bg-page shadow-sm rounded-md`} 22 + > 23 + <div 24 + className={`absolute top-0 left-0 origin-top-left pointer-events-none ${styles.scaleLeafletCanvasPreview}`} 25 + style={{ 26 + width: `1272px`, 27 + height: "calc(1272px * 2)", 28 + }} 29 + > 30 + {props.isOnScreen && ( 31 + <CanvasContent entityID={props.entityID} preview /> 32 + )} 33 + </div> 34 + </div> 35 + ); 36 + 37 + return ( 38 + <div 39 + ref={previewRef} 40 + className={`pageLinkBlockPreview h-full overflow-clip flex flex-col gap-0.5 no-underline relative`} 41 + > 42 + <div 43 + className={`absolute top-0 left-0 w-full h-full origin-top-left pointer-events-none ${styles.scaleLeafletDocPreview}`} 44 + style={{ 45 + width: `var(--page-width-units)`, 46 + }} 47 + > 48 + <PublicationMetadataPreview /> 49 + 50 + {props.isOnScreen && 51 + blocks.slice(0, 10).map((b, index, arr) => { 52 + return ( 53 + <BlockPreview 54 + pageType="doc" 55 + entityID={b.value} 56 + previousBlock={arr[index - 1] || null} 57 + nextBlock={arr[index + 1] || null} 58 + nextPosition={""} 59 + previewRef={previewRef} 60 + {...b} 61 + key={b.factID} 62 + /> 63 + ); 64 + })} 65 + </div> 66 + </div> 67 + ); 68 + };
+88
app/home/LeafletList/LeafletInfo.tsx
···
··· 1 + "use client"; 2 + import { PermissionToken } from "src/replicache"; 3 + import { LeafletOptions } from "./LeafletOptions"; 4 + import Link from "next/link"; 5 + import { useState } from "react"; 6 + import { theme } from "tailwind.config"; 7 + import { TemplateSmall } from "components/Icons/TemplateSmall"; 8 + import { timeAgo } from "src/utils/timeAgo"; 9 + 10 + export const LeafletInfo = (props: { 11 + title?: string; 12 + draft?: boolean; 13 + published?: boolean; 14 + token: PermissionToken; 15 + leaflet_id: string; 16 + loggedIn: boolean; 17 + isTemplate: boolean; 18 + className?: string; 19 + display: "grid" | "list"; 20 + added_at: string; 21 + publishedAt?: string; 22 + }) => { 23 + let [prefetch, setPrefetch] = useState(false); 24 + let prettyCreatedAt = props.added_at ? timeAgo(props.added_at) : ""; 25 + 26 + let prettyPublishedAt = props.publishedAt ? timeAgo(props.publishedAt) : ""; 27 + 28 + return ( 29 + <div 30 + className={`leafletInfo w-full min-w-0 flex flex-col ${props.className}`} 31 + > 32 + <div className="flex justify-between items-center shrink-0 max-w-full gap-2 leading-tight overflow-hidden"> 33 + <Link 34 + onMouseEnter={() => setPrefetch(true)} 35 + onPointerDown={() => setPrefetch(true)} 36 + prefetch={prefetch} 37 + href={`/${props.token.id}`} 38 + className="no-underline sm:hover:no-underline text-primary grow min-w-0" 39 + > 40 + <h3 className="sm:text-lg text-base truncate w-full min-w-0"> 41 + {props.title} 42 + </h3> 43 + </Link> 44 + <div className="flex gap-1 shrink-0"> 45 + {props.isTemplate && props.display === "list" ? ( 46 + <TemplateSmall 47 + fill={theme.colors["bg-page"]} 48 + className="text-tertiary" 49 + /> 50 + ) : null} 51 + <LeafletOptions 52 + leaflet={props.token} 53 + isTemplate={props.isTemplate} 54 + loggedIn={props.loggedIn} 55 + added_at={props.added_at} 56 + /> 57 + </div> 58 + </div> 59 + <Link 60 + onMouseEnter={() => setPrefetch(true)} 61 + onPointerDown={() => setPrefetch(true)} 62 + prefetch={prefetch} 63 + href={`/${props.token.id}`} 64 + className="no-underline sm:hover:no-underline text-primary w-full" 65 + > 66 + {props.draft || props.published ? ( 67 + <div 68 + className={`text-xs ${props.published ? "font-bold text-tertiary" : "text-tertiary"}`} 69 + > 70 + {props.published 71 + ? `Published ${prettyPublishedAt}` 72 + : `Draft ${prettyCreatedAt}`} 73 + </div> 74 + ) : ( 75 + <div className="text-xs text-tertiary">{prettyCreatedAt}</div> 76 + )} 77 + </Link> 78 + {props.isTemplate && props.display === "grid" ? ( 79 + <div className="absolute -top-2 right-1"> 80 + <TemplateSmall 81 + className="text-tertiary" 82 + fill={theme.colors["bg-page"]} 83 + /> 84 + </div> 85 + ) : null} 86 + </div> 87 + ); 88 + };
+102
app/home/LeafletList/LeafletListItem.tsx
···
··· 1 + "use client"; 2 + import { PermissionToken } from "src/replicache"; 3 + import { useTemplateState } from "../Actions/CreateNewButton"; 4 + import { LeafletListPreview, LeafletGridPreview } from "./LeafletPreview"; 5 + import { LeafletInfo } from "./LeafletInfo"; 6 + import { useState, useRef, useEffect } from "react"; 7 + 8 + export const LeafletListItem = (props: { 9 + token: PermissionToken; 10 + leaflet_id: string; 11 + loggedIn: boolean; 12 + display: "list" | "grid"; 13 + cardBorderHidden: boolean; 14 + added_at: string; 15 + title: string; 16 + draft?: boolean; 17 + published?: boolean; 18 + publishedAt?: string; 19 + index: number; 20 + isHidden: boolean; 21 + showPreview?: boolean; 22 + }) => { 23 + let isTemplate = useTemplateState( 24 + (s) => !!s.templates.find((t) => t.id === props.token.id), 25 + ); 26 + 27 + let [isOnScreen, setIsOnScreen] = useState(props.index < 16 ? true : false); 28 + let previewRef = useRef<HTMLDivElement | null>(null); 29 + 30 + useEffect(() => { 31 + if (!previewRef.current) return; 32 + let observer = new IntersectionObserver( 33 + (entries) => { 34 + entries.forEach((entry) => { 35 + if (entry.isIntersecting) { 36 + setIsOnScreen(true); 37 + } else { 38 + setIsOnScreen(false); 39 + } 40 + }); 41 + }, 42 + { threshold: 0.1, root: null }, 43 + ); 44 + observer.observe(previewRef.current); 45 + return () => observer.disconnect(); 46 + }, [previewRef]); 47 + 48 + if (props.display === "list") 49 + return ( 50 + <> 51 + <div 52 + ref={previewRef} 53 + className={`gap-3 w-full ${props.cardBorderHidden ? "" : "px-2 py-1 block-border hover:outline-border"}`} 54 + style={{ 55 + backgroundColor: props.cardBorderHidden 56 + ? "transparent" 57 + : "rgba(var(--bg-page), var(--bg-page-alpha))", 58 + 59 + display: props.isHidden ? "none" : "flex", 60 + }} 61 + > 62 + {props.showPreview && ( 63 + <LeafletListPreview isVisible={isOnScreen} {...props} /> 64 + )} 65 + <LeafletInfo isTemplate={isTemplate} {...props} /> 66 + </div> 67 + {props.cardBorderHidden && ( 68 + <hr 69 + className="last:hidden border-border-light" 70 + style={{ 71 + display: props.isHidden ? "none" : "block", 72 + }} 73 + /> 74 + )} 75 + </> 76 + ); 77 + return ( 78 + <div 79 + ref={previewRef} 80 + className={`leafletGridListItem relative 81 + flex flex-col gap-1 p-1 h-52 82 + block-border border-border! hover:outline-border 83 + `} 84 + style={{ 85 + backgroundColor: props.cardBorderHidden 86 + ? "transparent" 87 + : "rgba(var(--bg-page), var(--bg-page-alpha))", 88 + 89 + display: props.isHidden ? "none" : "flex", 90 + }} 91 + > 92 + <div className="grow"> 93 + <LeafletGridPreview {...props} isVisible={isOnScreen} /> 94 + </div> 95 + <LeafletInfo 96 + isTemplate={isTemplate} 97 + className="px-1 pb-0.5 shrink-0" 98 + {...props} 99 + /> 100 + </div> 101 + ); 102 + };
+187
app/home/LeafletList/LeafletPreview.tsx
···
··· 1 + "use client"; 2 + import { 3 + ThemeBackgroundProvider, 4 + ThemeProvider, 5 + } from "components/ThemeManager/ThemeProvider"; 6 + import { 7 + PermissionToken, 8 + useEntity, 9 + useReferenceToEntity, 10 + } from "src/replicache"; 11 + import { useTemplateState } from "../Actions/CreateNewButton"; 12 + import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 13 + import { LeafletContent } from "./LeafletContent"; 14 + import { Tooltip } from "components/Tooltip"; 15 + import { useState } from "react"; 16 + import Link from "next/link"; 17 + import { SpeedyLink } from "components/SpeedyLink"; 18 + 19 + export const LeafletListPreview = (props: { 20 + draft?: boolean; 21 + published?: boolean; 22 + isVisible: boolean; 23 + token: PermissionToken; 24 + leaflet_id: string; 25 + loggedIn: boolean; 26 + }) => { 27 + let root = 28 + useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity || 29 + props.leaflet_id; 30 + let firstPage = useEntity(root, "root/page")[0]; 31 + let page = firstPage?.data.value || root; 32 + 33 + let cardBorderHidden = useCardBorderHidden(root); 34 + let rootBackgroundImage = useEntity(root, "theme/card-background-image"); 35 + let rootBackgroundRepeat = useEntity( 36 + root, 37 + "theme/card-background-image-repeat", 38 + ); 39 + let rootBackgroundOpacity = useEntity( 40 + root, 41 + "theme/card-background-image-opacity", 42 + ); 43 + 44 + return ( 45 + <Tooltip 46 + open={true} 47 + delayDuration={0} 48 + side="right" 49 + trigger={ 50 + <div className="w-12 h-full py-1"> 51 + <div className="rounded-md h-full overflow-hidden"> 52 + <ThemeProvider local entityID={root} className=""> 53 + <ThemeBackgroundProvider entityID={root}> 54 + <div className="w-full h-full rounded-md p-1 border border-border"> 55 + <div 56 + className={`w-full h-full rounded-[2px]`} 57 + style={ 58 + cardBorderHidden 59 + ? { 60 + borderWidth: "2px", 61 + borderColor: "rgb(var(--primary))", 62 + } 63 + : { 64 + backgroundColor: 65 + "rgba(var(--bg-page), var(--bg-page-alpha))", 66 + } 67 + } 68 + /> 69 + </div> 70 + </ThemeBackgroundProvider> 71 + </ThemeProvider> 72 + </div> 73 + </div> 74 + } 75 + className="p-1!" 76 + > 77 + <ThemeProvider local entityID={root} className="rounded-sm"> 78 + <ThemeBackgroundProvider entityID={root}> 79 + <div className="leafletPreview grow shrink-0 h-44 w-64 px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none rounded-[2px] "> 80 + <div 81 + className={`leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`} 82 + style={ 83 + cardBorderHidden 84 + ? {} 85 + : { 86 + backgroundImage: rootBackgroundImage 87 + ? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})` 88 + : undefined, 89 + backgroundRepeat: rootBackgroundRepeat 90 + ? "repeat" 91 + : "no-repeat", 92 + backgroundPosition: "center", 93 + backgroundSize: !rootBackgroundRepeat 94 + ? "cover" 95 + : rootBackgroundRepeat?.data.value / 3, 96 + opacity: 97 + rootBackgroundImage?.data.src && rootBackgroundOpacity 98 + ? rootBackgroundOpacity.data.value 99 + : 1, 100 + backgroundColor: 101 + "rgba(var(--bg-page), var(--bg-page-alpha))", 102 + } 103 + } 104 + > 105 + <LeafletContent entityID={page} isOnScreen={props.isVisible} /> 106 + </div> 107 + </div> 108 + </ThemeBackgroundProvider> 109 + </ThemeProvider> 110 + </Tooltip> 111 + ); 112 + }; 113 + 114 + export const LeafletGridPreview = (props: { 115 + draft?: boolean; 116 + published?: boolean; 117 + token: PermissionToken; 118 + leaflet_id: string; 119 + loggedIn: boolean; 120 + isVisible: boolean; 121 + }) => { 122 + let root = 123 + useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity || 124 + props.leaflet_id; 125 + let firstPage = useEntity(root, "root/page")[0]; 126 + let page = firstPage?.data.value || root; 127 + 128 + let cardBorderHidden = useCardBorderHidden(root); 129 + let rootBackgroundImage = useEntity(root, "theme/card-background-image"); 130 + let rootBackgroundRepeat = useEntity( 131 + root, 132 + "theme/card-background-image-repeat", 133 + ); 134 + let rootBackgroundOpacity = useEntity( 135 + root, 136 + "theme/card-background-image-opacity", 137 + ); 138 + return ( 139 + <ThemeProvider local entityID={root} className="w-full!"> 140 + <div className="border border-border-light rounded-md w-full h-full overflow-hidden relative"> 141 + <div className="relative w-full h-full"> 142 + <ThemeBackgroundProvider entityID={root}> 143 + <div className="leafletPreview relative grow shrink-0 h-full w-full px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none"> 144 + <div 145 + className={`leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`} 146 + style={ 147 + cardBorderHidden 148 + ? {} 149 + : { 150 + backgroundImage: rootBackgroundImage 151 + ? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})` 152 + : undefined, 153 + backgroundRepeat: rootBackgroundRepeat 154 + ? "repeat" 155 + : "no-repeat", 156 + backgroundPosition: "center", 157 + backgroundSize: !rootBackgroundRepeat 158 + ? "cover" 159 + : rootBackgroundRepeat?.data.value / 3, 160 + opacity: 161 + rootBackgroundImage?.data.src && rootBackgroundOpacity 162 + ? rootBackgroundOpacity.data.value 163 + : 1, 164 + backgroundColor: 165 + "rgba(var(--bg-page), var(--bg-page-alpha))", 166 + } 167 + } 168 + > 169 + <LeafletContent entityID={page} isOnScreen={props.isVisible} /> 170 + </div> 171 + </div> 172 + </ThemeBackgroundProvider> 173 + </div> 174 + <LeafletPreviewLink id={props.token.id} /> 175 + </div> 176 + </ThemeProvider> 177 + ); 178 + }; 179 + 180 + const LeafletPreviewLink = (props: { id: string }) => { 181 + return ( 182 + <SpeedyLink 183 + href={`/${props.id}`} 184 + className={`hello no-underline sm:hover:no-underline text-primary absolute inset-0 w-full h-full bg-bg-test`} 185 + /> 186 + ); 187 + };
+72 -8
app/home/LeafletOptions.tsx app/home/LeafletList/LeafletOptions.tsx
··· 1 "use client"; 2 3 import { Menu, MenuItem } from "components/Layout"; 4 - import type { PermissionToken } from "src/replicache"; 5 - import { hideDoc } from "./storage"; 6 import { useState } from "react"; 7 import { ButtonPrimary } from "components/Buttons"; 8 - import { useTemplateState } from "./CreateNewButton"; 9 - import { Item } from "@radix-ui/react-dropdown-menu"; 10 - import { useSmoker } from "components/Toast"; 11 import { removeLeafletFromHome } from "actions/removeLeafletFromHome"; 12 import { useIdentityData } from "components/IdentityProvider"; 13 import { HideSmall } from "components/Icons/HideSmall"; 14 import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; 15 import { TemplateRemoveSmall } from "components/Icons/TemplateRemoveSmall"; 16 import { TemplateSmall } from "components/Icons/TemplateSmall"; 17 18 export const LeafletOptions = (props: { 19 leaflet: PermissionToken; 20 isTemplate: boolean; 21 loggedIn: boolean; 22 }) => { 23 let { mutate: mutateIdentity } = useIdentityData(); 24 let [state, setState] = useState<"normal" | "template">("normal"); 25 let [open, setOpen] = useState(false); 26 let smoker = useSmoker(); 27 return ( 28 <> 29 <Menu ··· 34 setState("normal"); 35 }} 36 trigger={ 37 - <div className="bg-accent-1 text-accent-2 px-2 py-1 border border-accent-2 rounded-md"> 38 - <MoreOptionsTiny /> 39 </div> 40 } 41 > ··· 93 } else { 94 hideDoc(props.leaflet); 95 } 96 }} 97 > 98 <HideSmall /> ··· 110 ); 111 }; 112 113 const AddTemplateForm = (props: { 114 leaflet: PermissionToken; 115 close: () => void; ··· 124 value={name} 125 onChange={(e) => setName(e.target.value)} 126 type="text" 127 - className=" text-primary font-normal border border-border rounded-md outline-none px-2 py-1 w-64" 128 /> 129 </label> 130
··· 1 "use client"; 2 3 import { Menu, MenuItem } from "components/Layout"; 4 + import { useReplicache, type PermissionToken } from "src/replicache"; 5 + import { hideDoc } from "../storage"; 6 import { useState } from "react"; 7 import { ButtonPrimary } from "components/Buttons"; 8 + import { useTemplateState } from "../Actions/CreateNewButton"; 9 + import { useSmoker, useToaster } from "components/Toast"; 10 import { removeLeafletFromHome } from "actions/removeLeafletFromHome"; 11 import { useIdentityData } from "components/IdentityProvider"; 12 import { HideSmall } from "components/Icons/HideSmall"; 13 import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; 14 import { TemplateRemoveSmall } from "components/Icons/TemplateRemoveSmall"; 15 import { TemplateSmall } from "components/Icons/TemplateSmall"; 16 + import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny"; 17 + import { addLeafletToHome } from "actions/addLeafletToHome"; 18 19 export const LeafletOptions = (props: { 20 leaflet: PermissionToken; 21 isTemplate: boolean; 22 loggedIn: boolean; 23 + added_at: string; 24 }) => { 25 let { mutate: mutateIdentity } = useIdentityData(); 26 let [state, setState] = useState<"normal" | "template">("normal"); 27 let [open, setOpen] = useState(false); 28 let smoker = useSmoker(); 29 + let toaster = useToaster(); 30 return ( 31 <> 32 <Menu ··· 37 setState("normal"); 38 }} 39 trigger={ 40 + <div 41 + className="text-secondary shrink-0" 42 + onClick={(e) => { 43 + e.preventDefault; 44 + e.stopPropagation; 45 + }} 46 + > 47 + <MoreOptionsVerticalTiny /> 48 </div> 49 } 50 > ··· 102 } else { 103 hideDoc(props.leaflet); 104 } 105 + toaster({ 106 + content: ( 107 + <div className="font-bold"> 108 + Doc removed!{" "} 109 + <UndoRemoveFromHomeButton 110 + leaflet={props.leaflet} 111 + added_at={props.added_at} 112 + /> 113 + </div> 114 + ), 115 + type: "success", 116 + }); 117 }} 118 > 119 <HideSmall /> ··· 131 ); 132 }; 133 134 + const UndoRemoveFromHomeButton = (props: { 135 + leaflet: PermissionToken; 136 + added_at: string | undefined; 137 + }) => { 138 + let toaster = useToaster(); 139 + let { mutate } = useIdentityData(); 140 + return ( 141 + <button 142 + onClick={async (e) => { 143 + await mutate( 144 + (identity) => { 145 + if (!identity) return; 146 + return { 147 + ...identity, 148 + permission_token_on_homepage: [ 149 + ...identity.permission_token_on_homepage, 150 + { 151 + created_at: props.added_at || new Date().toISOString(), 152 + permission_tokens: { 153 + ...props.leaflet, 154 + leaflets_in_publications: [], 155 + }, 156 + }, 157 + ], 158 + }; 159 + }, 160 + { revalidate: false }, 161 + ); 162 + await addLeafletToHome(props.leaflet.id); 163 + await mutate(); 164 + 165 + toaster({ 166 + content: <div className="font-bold">Recovered Doc!</div>, 167 + type: "success", 168 + }); 169 + }} 170 + className="underline" 171 + > 172 + Undo? 173 + </button> 174 + ); 175 + }; 176 + 177 const AddTemplateForm = (props: { 178 leaflet: PermissionToken; 179 close: () => void; ··· 188 value={name} 189 onChange={(e) => setName(e.target.value)} 190 type="text" 191 + className=" text-primary font-normal border border-border rounded-md outline-hidden px-2 py-1 w-64" 192 /> 193 </label> 194
app/home/LeafletPreview.module.css app/home/LeafletList/LeafletPreview.module.css
-255
app/home/LeafletPreview.tsx
··· 1 - "use client"; 2 - import { BlockPreview, PagePreview } from "components/Blocks/PageLinkBlock"; 3 - import { 4 - ThemeBackgroundProvider, 5 - ThemeProvider, 6 - } from "components/ThemeManager/ThemeProvider"; 7 - import { useEffect, useRef, useState } from "react"; 8 - import { useBlocks } from "src/hooks/queries/useBlocks"; 9 - import { 10 - PermissionToken, 11 - useEntity, 12 - useReferenceToEntity, 13 - useReplicache, 14 - } from "src/replicache"; 15 - import { deleteLeaflet } from "actions/deleteLeaflet"; 16 - import { removeDocFromHome } from "./storage"; 17 - import { mutate } from "swr"; 18 - import { ButtonPrimary } from "components/Buttons"; 19 - import { LeafletOptions } from "./LeafletOptions"; 20 - import { CanvasContent } from "components/Canvas"; 21 - import { theme } from "tailwind.config"; 22 - import { useTemplateState } from "./CreateNewButton"; 23 - import styles from "./LeafletPreview.module.css"; 24 - import { useRouter } from "next/navigation"; 25 - import Link from "next/link"; 26 - import { TemplateSmall } from "components/Icons/TemplateSmall"; 27 - import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 28 - import { 29 - PublicationMetadata, 30 - PublicationMetadataPreview, 31 - } from "components/Pages/PublicationMetadata"; 32 - import { SpeedyLink } from "components/SpeedyLink"; 33 - 34 - export const LeafletPreview = (props: { 35 - draft?: boolean; 36 - published?: boolean; 37 - index: number; 38 - token: PermissionToken; 39 - leaflet_id: string; 40 - loggedIn: boolean; 41 - }) => { 42 - let [state, setState] = useState<"normal" | "deleting">("normal"); 43 - let isTemplate = useTemplateState( 44 - (s) => !!s.templates.find((t) => t.id === props.token.id), 45 - ); 46 - let root = 47 - useReferenceToEntity("root/page", props.leaflet_id)[0]?.entity || 48 - props.leaflet_id; 49 - let firstPage = useEntity(root, "root/page")[0]; 50 - let page = firstPage?.data.value || root; 51 - 52 - let cardBorderHidden = useCardBorderHidden(root); 53 - let rootBackgroundImage = useEntity(root, "theme/card-background-image"); 54 - let rootBackgroundRepeat = useEntity( 55 - root, 56 - "theme/card-background-image-repeat", 57 - ); 58 - let rootBackgroundOpacity = useEntity( 59 - root, 60 - "theme/card-background-image-opacity", 61 - ); 62 - 63 - return ( 64 - <div className="relative max-h-40 h-40"> 65 - <ThemeProvider local entityID={root} className="!w-full"> 66 - <div className="rounded-lg sm:hover:shadow-sm overflow-clip border border-border outline outline-2 outline-transparent outline-offset-1 sm:hover:outline-border bg-bg-leaflet grow w-full h-full"> 67 - {state === "normal" ? ( 68 - <div className="relative w-full h-full"> 69 - <ThemeBackgroundProvider entityID={root}> 70 - <div className="leafletPreview grow shrink-0 h-full w-full px-2 pt-2 sm:px-3 sm:pt-3 flex items-end pointer-events-none"> 71 - <div 72 - className={`leafletContentWrapper h-full sm:w-48 w-40 mx-auto overflow-clip ${!cardBorderHidden && "border border-border-light border-b-0 rounded-t-md"}`} 73 - style={ 74 - cardBorderHidden 75 - ? {} 76 - : { 77 - backgroundImage: rootBackgroundImage 78 - ? `url(${rootBackgroundImage.data.src}), url(${rootBackgroundImage.data.fallback})` 79 - : undefined, 80 - backgroundRepeat: rootBackgroundRepeat 81 - ? "repeat" 82 - : "no-repeat", 83 - backgroundPosition: "center", 84 - backgroundSize: !rootBackgroundRepeat 85 - ? "cover" 86 - : rootBackgroundRepeat?.data.value / 3, 87 - opacity: 88 - rootBackgroundImage?.data.src && 89 - rootBackgroundOpacity 90 - ? rootBackgroundOpacity.data.value 91 - : 1, 92 - backgroundColor: 93 - "rgba(var(--bg-page), var(--bg-page-alpha))", 94 - } 95 - } 96 - > 97 - <LeafletContent entityID={page} index={props.index} /> 98 - </div> 99 - </div> 100 - </ThemeBackgroundProvider> 101 - <SpeedyLink 102 - href={`/${props.token.id}`} 103 - className={`no-underline sm:hover:no-underline text-primary absolute inset-0 w-full h-full`} 104 - /> 105 - </div> 106 - ) : ( 107 - <LeafletAreYouSure token={props.token} setState={setState} /> 108 - )} 109 - </div> 110 - <div className="flex justify-between pt-1 shrink-0 w-full gap-2"> 111 - {props.draft || props.published ? ( 112 - <div 113 - className={`text-xs container !border-none !w-fit px-0.5 italic ${props.published ? "font-bold text-tertiary" : "text-tertiary"}`} 114 - > 115 - {props.published ? "Published!" : "Draft"} 116 - </div> 117 - ) : ( 118 - <div /> 119 - )} 120 - <LeafletOptions 121 - leaflet={props.token} 122 - isTemplate={isTemplate} 123 - loggedIn={props.loggedIn} 124 - /> 125 - </div> 126 - <LeafletTemplateIndicator isTemplate={isTemplate} /> 127 - </ThemeProvider> 128 - </div> 129 - ); 130 - }; 131 - 132 - const LeafletContent = (props: { entityID: string; index: number }) => { 133 - let type = useEntity(props.entityID, "page/type")?.data.value || "doc"; 134 - let blocks = useBlocks(props.entityID); 135 - let previewRef = useRef<HTMLDivElement | null>(null); 136 - let [isVisible, setIsVisible] = useState(props.index > 16 ? false : true); 137 - useEffect(() => { 138 - if (!previewRef.current) return; 139 - let observer = new IntersectionObserver( 140 - (entries) => { 141 - entries.forEach((entry) => { 142 - if (entry.isIntersecting) { 143 - setIsVisible(true); 144 - } else { 145 - setIsVisible(false); 146 - } 147 - }); 148 - }, 149 - { threshold: 0.1, root: null }, 150 - ); 151 - observer.observe(previewRef.current); 152 - return () => observer.disconnect(); 153 - }, [previewRef]); 154 - 155 - if (type === "canvas") 156 - return ( 157 - <div 158 - className={`pageLinkBlockPreview shrink-0 h-full overflow-clip relative bg-bg-page shadow-sm rounded-md`} 159 - > 160 - <div 161 - className={`absolute top-0 left-0 origin-top-left pointer-events-none ${styles.scaleLeafletCanvasPreview}`} 162 - style={{ 163 - width: `1272px`, 164 - height: "calc(1272px * 2)", 165 - }} 166 - > 167 - {isVisible && <CanvasContent entityID={props.entityID} preview />} 168 - </div> 169 - </div> 170 - ); 171 - 172 - return ( 173 - <div 174 - ref={previewRef} 175 - className={`pageLinkBlockPreview h-full overflow-clip flex flex-col gap-0.5 no-underline relative`} 176 - > 177 - <div 178 - className={`absolute top-0 left-0 w-full h-full origin-top-left pointer-events-none ${styles.scaleLeafletDocPreview}`} 179 - style={{ 180 - width: `var(--page-width-units)`, 181 - }} 182 - > 183 - <PublicationMetadataPreview /> 184 - 185 - {isVisible && 186 - blocks.slice(0, 10).map((b, index, arr) => { 187 - return ( 188 - <BlockPreview 189 - pageType="doc" 190 - entityID={b.value} 191 - previousBlock={arr[index - 1] || null} 192 - nextBlock={arr[index + 1] || null} 193 - nextPosition={""} 194 - previewRef={previewRef} 195 - {...b} 196 - key={b.factID} 197 - /> 198 - ); 199 - })} 200 - </div> 201 - </div> 202 - ); 203 - }; 204 - 205 - const LeafletAreYouSure = (props: { 206 - token: PermissionToken; 207 - setState: (s: "normal" | "deleting") => void; 208 - }) => { 209 - return ( 210 - <div 211 - className="leafletContentWrapper w-full h-full px-1 pt-1 sm:px-[6px] sm:pt-2 flex flex-col gap-2 justify-center items-center " 212 - style={{ 213 - backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha))", 214 - }} 215 - > 216 - <div className="font-bold text-center"> 217 - Permanently delete this Leaflet? 218 - </div> 219 - <div className="flex gap-2 font-bold "> 220 - <ButtonPrimary 221 - compact 222 - onMouseDown={(e) => { 223 - e.stopPropagation(); 224 - e.preventDefault(); 225 - deleteLeaflet(props.token); 226 - removeDocFromHome(props.token); 227 - mutate("leaflets"); 228 - }} 229 - > 230 - Delete 231 - </ButtonPrimary> 232 - <button 233 - className="text-accent-1" 234 - onMouseDown={(e) => { 235 - e.stopPropagation(); 236 - e.preventDefault(); 237 - props.setState("normal"); 238 - }} 239 - > 240 - Nevermind 241 - </button> 242 - </div> 243 - </div> 244 - ); 245 - }; 246 - 247 - const LeafletTemplateIndicator = (props: { isTemplate: boolean }) => { 248 - if (!props.isTemplate) return; 249 - 250 - return ( 251 - <div className="absolute -top-3 right-1"> 252 - <TemplateSmall fill={theme.colors["bg-page"]} /> 253 - </div> 254 - ); 255 - };
···
+89 -108
app/home/Publications.tsx components/ActionBar/Publications.tsx
··· 1 "use client"; 2 - import { ButtonSecondary } from "components/Buttons"; 3 import Link from "next/link"; 4 5 import { useIdentityData } from "components/IdentityProvider"; 6 import { theme } from "tailwind.config"; 7 - import { BlueskyTiny } from "components/Icons/BlueskyTiny"; 8 - import { AddTiny } from "components/Icons/AddTiny"; 9 - import { 10 - getBasePublicationURL, 11 - getPublicationURL, 12 - } from "app/lish/createPub/getPublicationURL"; 13 import { Json } from "supabase/database.types"; 14 import { PubLeafletPublication } from "lexicons/api"; 15 import { AtUri } from "@atproto/syntax"; 16 - import { blobRefToSrc } from "src/utils/blobRefToSrc"; 17 - import { PublicationThemeProvider } from "components/ThemeManager/PublicationThemeProvider"; 18 import { SpeedyLink } from "components/SpeedyLink"; 19 20 - export const MyPublicationList = () => { 21 let { identity } = useIdentityData(); 22 if (!identity || !identity.atp_did) return <PubListEmpty />; 23 return ( 24 - <div className="pubListWrapper w-full sm:w-[200px] flex flex-col sm:gap-1 container py-2 sm:p-0 sm:bg-transparent sm:border-0"> 25 - <div className="flex justify-between items-center font-bold text-tertiary text-sm px-2 "> 26 - Publications 27 - <SpeedyLink 28 - href={"/lish/createPub"} 29 - className="pubListCreateNew text-accent-contrast font-bold hover:text-accent-contrast" 30 - > 31 - <AddTiny /> 32 - </SpeedyLink> 33 - </div> 34 - <PublicationList publications={identity.publications} /> 35 </div> 36 ); 37 }; 38 39 - const PublicationList = (props: { 40 - publications: { 41 - identity_did: string; 42 - indexed_at: string; 43 - name: string; 44 - record: Json; 45 - uri: string; 46 - }[]; 47 }) => { 48 return ( 49 - <div className="pubList w-full flex flex-row sm:flex-col gap-0 overflow-auto items-stretch"> 50 - {props.publications?.map((d) => ( 51 - <Publication {...d} key={d.uri} record={d.record} /> 52 - ))} 53 - </div> 54 ); 55 }; 56 57 - function Publication(props: { uri: string; name: string; record: Json }) { 58 - let record = props.record as PubLeafletPublication.Record | null; 59 - let pub_creator = new AtUri(props.uri).host; 60 - let backgroundImage = record?.theme?.backgroundImage?.image?.ref 61 - ? blobRefToSrc(record?.theme?.backgroundImage?.image?.ref, pub_creator) 62 - : null; 63 - 64 - let backgroundImageRepeat = record?.theme?.backgroundImage?.repeat; 65 - let backgroundImageSize = record?.theme?.backgroundImage?.width || 500; 66 - let showPageBackground = !!record?.theme?.showPageBackground; 67 return ( 68 - <PublicationThemeProvider record={record} local pub_creator={pub_creator}> 69 - <SpeedyLink 70 - className="pubListItem sm:w-full sm:min-w-0 min-w-40 w-36 px-1 sm:px-2 py-1 sm:h-max hover:no-underline " 71 - href={`${getBasePublicationURL(props)}/dashboard`} 72 - > 73 - <div 74 - style={{ 75 - backgroundImage: `url(${backgroundImage})`, 76 - backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 77 - backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 78 - }} 79 - className="py-3 px-2 h-full w-full flex flex-col gap-1 place-items-center bg-bg-leaflet border border-border-light rounded-lg text-secondary text-center transparent-outline outline-2 outline-offset-1 hover:outline-border grow " 80 - > 81 - {record?.icon && ( 82 - <div 83 - style={{ 84 - backgroundRepeat: "no-repeat", 85 - backgroundPosition: "center", 86 - backgroundSize: "cover", 87 - backgroundImage: `url(/api/atproto_images?did=${new AtUri(props.uri).host}&cid=${(record.icon?.ref as unknown as { $link: string })["$link"]})`, 88 - }} 89 - className="w-6 h-6 rounded-full" 90 - /> 91 - )} 92 - <h4 93 - className={`font-bold w-full px-1 rounded-md truncate my-auto ${showPageBackground ? "bg-[rgba(var(--bg-page),0.8)]" : ""}`} 94 - > 95 - {props.name} 96 - </h4> 97 - </div> 98 - </SpeedyLink> 99 - </PublicationThemeProvider> 100 ); 101 - } 102 103 - const PubListEmpty = () => { 104 - let { identity } = useIdentityData(); 105 - return ( 106 - <div className="pubListEmpty accent-container text-sm sm:text-center py-4 px-3 w-full sm:max-w-[200px] flex flex-row sm:flex-col items-start sm:place-items-center justify-center gap-3 sm:gap-1"> 107 - <div className="shrink-0"> 108 - <PubListEmptyIllo /> 109 - </div> 110 - <div className="flex flex-col sm:place-items-center"> 111 - <strong>Publish with Leaflet!</strong> 112 - <div className="text-tertiary"> 113 - {!identity 114 - ? "Link a Bluesky account to publish your writing to a blog or newletter" 115 - : identity && !!identity.atp_did 116 - ? "Publish your writing to a blog or newletter on the ATmosphere" 117 - : ""} 118 - </div> 119 120 - {identity && !!identity.atp_did ? ( 121 - <Link href="/lish/createPub"> 122 - <ButtonSecondary compact className="text-sm mt-3"> 123 - Start a Publication! 124 - </ButtonSecondary> 125 - </Link> 126 - ) : ( 127 - <form action="/api/oauth/login?redirect_url=/" method="GET"> 128 - <ButtonSecondary compact className="text-sm mt-3"> 129 - <BlueskyTiny /> Link Bluesky 130 - </ButtonSecondary> 131 - </form> 132 - )} 133 </div> 134 </div> 135 ); 136 }; 137 138 - const PubListEmptyIllo = () => { 139 return ( 140 <svg 141 width="59" ··· 151 /> 152 <path 153 d="M44.0793 31.0064C44.7178 30.7014 45.4832 30.9723 45.7882 31.6108L48.1886 36.6362C48.4936 37.2747 48.2236 38.0402 47.5851 38.3452C46.9466 38.6502 46.1811 38.3792 45.8761 37.7407L43.4757 32.7153C43.1708 32.0769 43.4409 31.3114 44.0793 31.0064ZM47.6769 29.396C48.1531 28.8729 48.9632 28.8351 49.4865 29.311L56.049 35.2827C56.5724 35.7589 56.6111 36.5689 56.1349 37.0923C55.6587 37.6156 54.8487 37.6534 54.3254 37.1772L47.7629 31.2065C47.2395 30.7303 47.2007 29.9194 47.6769 29.396ZM39.717 5.68116C42.0981 4.90017 44.5808 4.94473 46.7375 6.12647C49.3605 7.5638 50.8245 10.3133 51.1896 13.4243C51.5549 16.538 50.8421 20.109 49.0216 23.4312C47.2012 26.7534 44.5751 29.2764 41.7541 30.644C38.9355 32.0105 35.8302 32.2552 33.2072 30.8179C30.9031 29.5552 29.4864 27.1999 28.9464 24.6831C28.8301 24.2474 28.6926 23.8222 28.5343 23.3853C27.9734 24.0541 27.3971 24.5221 26.7863 24.7798C25.6169 25.2729 24.5945 24.8983 23.8361 24.3579C22.7923 23.614 21.7765 22.8095 20.9679 21.9312C20.1616 21.0553 19.5008 20.0411 19.2707 18.8745L19.2629 18.8345C19.1678 18.3542 19.054 17.7817 19.2599 16.8921C19.4384 16.1213 19.8478 15.1379 20.6183 13.6987C19.832 13.0689 19.5569 12.0994 19.592 11.1099C19.6232 10.2278 19.8849 9.24332 20.3722 8.35401C20.8366 7.50662 21.4472 6.74544 22.1593 6.22315C22.8689 5.70275 23.7789 5.35579 24.7511 5.57959C25.3946 5.72772 26.0287 5.9727 26.6593 6.16846C27.3433 5.97648 28.0776 5.95162 28.8185 6.16455C29.2306 6.28299 29.6269 6.46238 30.0373 6.58936C30.7081 6.79695 31.4724 6.98507 32.0539 7.01319C35.195 7.16504 36.6776 6.71358 39.6769 5.69385C39.6901 5.68936 39.7037 5.6851 39.717 5.68116ZM45.7111 7.99951C43.9938 7.05853 41.9923 7.16313 40.1877 7.77588C39.8344 7.90062 39.4783 8.04953 39.1222 8.22217C36.7445 9.37492 34.4283 11.5618 32.7961 14.5405C31.1639 17.5192 30.5672 20.6477 30.8752 23.272C30.9368 23.7972 31.0577 24.2812 31.1691 24.7886C31.6931 26.6779 32.7517 28.1338 34.2336 28.9458C36.091 29.9635 38.4422 29.877 40.8224 28.7231C43.2002 27.5704 45.5163 25.3836 47.1486 22.4048C48.7809 19.4259 49.3775 16.2968 49.0695 13.6724C48.7611 11.0452 47.5685 9.0173 45.7111 7.99951ZM49.9005 26.5269C50.1565 25.8672 50.8991 25.5395 51.5587 25.7954L54.547 26.9556C55.2066 27.2116 55.5333 27.9532 55.2775 28.6128C55.0216 29.2724 54.2799 29.6 53.6203 29.3442L50.632 28.1851C49.9724 27.9291 49.6448 27.1865 49.9005 26.5269ZM39.1652 9.26807C41.2542 8.13629 43.5696 7.82566 45.505 8.88623C47.0749 9.74653 48.0237 11.3117 48.4123 13.1314C48.5106 13.5926 48.2163 14.0465 47.755 14.145C47.2939 14.2433 46.8399 13.949 46.7414 13.4878C46.4268 12.0152 45.7071 10.9451 44.6837 10.3843C43.4283 9.69643 41.7566 9.8068 39.9787 10.77C39.5506 11.0019 39.123 11.2806 38.7023 11.603C40.0255 12.7843 41.0004 14.0292 41.5402 15.2642C42.1439 16.6456 42.2208 18.0723 41.5373 19.3198C40.8549 20.5651 39.5891 21.2565 38.1007 21.4917C36.7655 21.7027 35.1903 21.5621 33.4992 21.1011C33.4542 21.6278 33.4497 22.1372 33.4845 22.6216C33.6295 24.6386 34.4361 26.1074 35.6916 26.7954C36.9471 27.4832 38.6187 27.3719 40.3966 26.4087C42.1654 25.4504 43.9291 23.6955 45.217 21.3452C45.7016 20.4607 46.0801 19.562 46.3556 18.6772C46.496 18.227 46.9746 17.9755 47.425 18.1157C47.8752 18.256 48.1266 18.7347 47.9865 19.1851C47.6767 20.1801 47.2535 21.1839 46.715 22.1665C45.2929 24.7616 43.3083 26.7749 41.2101 27.9116C39.1212 29.0432 36.8056 29.354 34.8703 28.2935C32.9348 27.2329 31.9507 25.1135 31.7804 22.7437C31.6095 20.3635 32.2393 17.6083 33.6613 15.0132C35.0834 12.418 37.067 10.4048 39.1652 9.26807ZM22.297 15.0933C21.7014 16.2428 21.4438 16.9252 21.34 17.3735C21.2375 17.8161 21.2789 18.0251 21.3586 18.4282L21.3654 18.4614C21.4906 19.0963 21.8766 19.7651 22.5392 20.4849C23.1994 21.202 24.0755 21.9061 25.0754 22.6187C25.4732 22.9022 25.706 22.918 25.9572 22.812C26.2951 22.6693 26.8456 22.2264 27.5851 21.0552C27.0094 19.7746 26.4315 18.8074 26.0109 18.3853C25.5581 17.9307 24.9198 17.457 24.3625 17.0815C24.0174 16.8492 23.6334 16.6577 23.3009 16.4087C22.8302 16.056 22.5072 15.5915 22.297 15.0933ZM37.4191 12.7485C36.5848 13.607 35.8111 14.6442 35.1593 15.8335C34.5066 17.0247 34.0481 18.237 33.7736 19.4038C35.3748 19.8612 36.7608 19.9738 37.8341 19.8042C38.9745 19.624 39.6854 19.1451 40.0392 18.4995C40.4026 17.8364 40.4284 16.9858 39.9748 15.9478C39.5441 14.9626 38.6986 13.8621 37.4191 12.7485ZM34.2082 9.14893C33.8924 9.16621 33.5635 9.17537 33.2189 9.17627C33.1388 9.23151 33.0511 9.29794 32.9562 9.37647C32.6169 9.65722 32.2522 10.0395 31.8879 10.479C31.1588 11.3585 30.4955 12.3901 30.1144 13.0855C29.6886 13.8625 29.21 14.8403 28.8732 15.8257C28.5838 16.6724 28.4237 17.4545 28.4445 18.0923C28.6141 18.3652 28.7815 18.6595 28.9445 18.9683C29.2882 17.1514 29.9462 15.2969 30.923 13.5142C31.8396 11.8415 32.9612 10.371 34.2082 9.14893ZM28.2306 8.22315C27.9953 8.13158 27.6238 8.07071 27.3918 8.1919C26.8031 8.49937 25.6057 9.41 25.0587 10.4204C24.7889 10.919 24.5664 11.6366 24.3986 12.355C24.273 12.8924 24.0375 13.5361 24.1984 14.0757C24.2869 14.3723 24.4121 14.5578 24.5441 14.6704C24.8879 14.8728 25.225 15.0872 25.5558 15.3101C25.9699 15.589 26.4729 15.9497 26.9435 16.3472C27.0299 15.9811 27.1376 15.6205 27.256 15.2739C27.6363 14.1611 28.164 13.0878 28.6154 12.2642C29.0414 11.4867 29.7649 10.3634 30.5724 9.38916C30.6783 9.26141 30.7871 9.1351 30.8976 9.01123C30.3592 8.90738 29.8338 8.76163 29.4064 8.6294C28.8588 8.45993 28.422 8.2968 28.2306 8.22315ZM24.2443 7.65479C24.0745 7.62582 23.8022 7.66603 23.422 7.94483C23.0226 8.23774 22.6009 8.7314 22.2453 9.38037C21.9128 9.98712 21.7449 10.6469 21.7257 11.1851C21.7078 11.6907 21.8192 11.8972 21.842 11.939C21.8441 11.9429 21.8456 11.9461 21.8459 11.9468L22.6076 12.5562C22.6451 12.3681 22.687 12.1695 22.7345 11.9663C22.9122 11.206 23.1764 10.3097 23.5568 9.60694C23.909 8.95632 24.4229 8.35989 24.9357 7.86866L24.2443 7.65479Z" 154 - fill={theme.colors["accent-contrast"]} 155 /> 156 </g> 157 <defs>
··· 1 "use client"; 2 import Link from "next/link"; 3 4 import { useIdentityData } from "components/IdentityProvider"; 5 import { theme } from "tailwind.config"; 6 + import { getBasePublicationURL } from "app/lish/createPub/getPublicationURL"; 7 import { Json } from "supabase/database.types"; 8 import { PubLeafletPublication } from "lexicons/api"; 9 import { AtUri } from "@atproto/syntax"; 10 + import { ActionButton } from "./ActionButton"; 11 import { SpeedyLink } from "components/SpeedyLink"; 12 + import { PublishSmall } from "components/Icons/PublishSmall"; 13 14 + export const PublicationButtons = (props: { 15 + currentPubUri: string | undefined; 16 + }) => { 17 let { identity } = useIdentityData(); 18 + 19 + // don't show pub list button if not logged in or no pub list 20 + // we show a "start a pub" banner instead 21 if (!identity || !identity.atp_did) return <PubListEmpty />; 22 return ( 23 + <div className="pubListWrapper w-full flex flex-col gap-1 sm:bg-transparent sm:border-0"> 24 + {identity.publications?.map((d) => { 25 + // console.log("thisURI : " + d.uri); 26 + // console.log("currentURI : " + props.currentPubUri); 27 + console.log(d.uri === props.currentPubUri); 28 + 29 + return ( 30 + <PublicationOption 31 + {...d} 32 + key={d.uri} 33 + record={d.record} 34 + asActionButton 35 + current={d.uri === props.currentPubUri} 36 + /> 37 + ); 38 + })} 39 + <Link 40 + href={"./lish/createPub"} 41 + className="pubListCreateNew text-accent-contrast text-sm place-self-end hover:text-accent-contrast" 42 + > 43 + New 44 + </Link> 45 </div> 46 ); 47 }; 48 49 + export const PublicationOption = (props: { 50 + uri: string; 51 + name: string; 52 + record: Json; 53 + asActionButton?: boolean; 54 + current?: boolean; 55 }) => { 56 + let record = props.record as PubLeafletPublication.Record | null; 57 + if (!record) return; 58 + 59 return ( 60 + <SpeedyLink 61 + href={`${getBasePublicationURL(props)}/dashboard`} 62 + className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full" 63 + > 64 + {props.asActionButton ? ( 65 + <ActionButton 66 + label={record.name} 67 + icon={<PubIcon record={record} uri={props.uri} />} 68 + nav 69 + className={props.current ? "bg-bg-page! border-border!" : ""} 70 + /> 71 + ) : ( 72 + <> 73 + <PubIcon record={record} uri={props.uri} /> 74 + <div className="truncate">{record.name}</div> 75 + </> 76 + )} 77 + </SpeedyLink> 78 ); 79 }; 80 81 + const PubListEmpty = () => { 82 return ( 83 + <SpeedyLink href={`lish/createPub`} className=" hover:no-underline!"> 84 + <ActionButton 85 + label="Publish on ATP" 86 + icon={<PublishSmall />} 87 + nav 88 + subtext="Start a blog in the Atmosphere" 89 + /> 90 + </SpeedyLink> 91 ); 92 + }; 93 94 + export const PubIcon = (props: { 95 + record: PubLeafletPublication.Record; 96 + uri: string; 97 + }) => { 98 + if (!props.record) return; 99 100 + return props.record.icon ? ( 101 + <div 102 + style={{ 103 + backgroundRepeat: "no-repeat", 104 + backgroundPosition: "center", 105 + backgroundSize: "cover", 106 + backgroundImage: `url(/api/atproto_images?did=${new AtUri(props.uri).host}&cid=${(props.record.icon?.ref as unknown as { $link: string })["$link"]})`, 107 + }} 108 + className="w-6 h-6 rounded-full" 109 + /> 110 + ) : ( 111 + <div className="w-6 h-6 rounded-full bg-accent-1 relative"> 112 + <div className="font-bold text-sm absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-accent-2"> 113 + {props.record?.name.slice(0, 1)} 114 </div> 115 </div> 116 ); 117 }; 118 119 + export const PubListEmptyIllo = () => { 120 return ( 121 <svg 122 width="59" ··· 132 /> 133 <path 134 d="M44.0793 31.0064C44.7178 30.7014 45.4832 30.9723 45.7882 31.6108L48.1886 36.6362C48.4936 37.2747 48.2236 38.0402 47.5851 38.3452C46.9466 38.6502 46.1811 38.3792 45.8761 37.7407L43.4757 32.7153C43.1708 32.0769 43.4409 31.3114 44.0793 31.0064ZM47.6769 29.396C48.1531 28.8729 48.9632 28.8351 49.4865 29.311L56.049 35.2827C56.5724 35.7589 56.6111 36.5689 56.1349 37.0923C55.6587 37.6156 54.8487 37.6534 54.3254 37.1772L47.7629 31.2065C47.2395 30.7303 47.2007 29.9194 47.6769 29.396ZM39.717 5.68116C42.0981 4.90017 44.5808 4.94473 46.7375 6.12647C49.3605 7.5638 50.8245 10.3133 51.1896 13.4243C51.5549 16.538 50.8421 20.109 49.0216 23.4312C47.2012 26.7534 44.5751 29.2764 41.7541 30.644C38.9355 32.0105 35.8302 32.2552 33.2072 30.8179C30.9031 29.5552 29.4864 27.1999 28.9464 24.6831C28.8301 24.2474 28.6926 23.8222 28.5343 23.3853C27.9734 24.0541 27.3971 24.5221 26.7863 24.7798C25.6169 25.2729 24.5945 24.8983 23.8361 24.3579C22.7923 23.614 21.7765 22.8095 20.9679 21.9312C20.1616 21.0553 19.5008 20.0411 19.2707 18.8745L19.2629 18.8345C19.1678 18.3542 19.054 17.7817 19.2599 16.8921C19.4384 16.1213 19.8478 15.1379 20.6183 13.6987C19.832 13.0689 19.5569 12.0994 19.592 11.1099C19.6232 10.2278 19.8849 9.24332 20.3722 8.35401C20.8366 7.50662 21.4472 6.74544 22.1593 6.22315C22.8689 5.70275 23.7789 5.35579 24.7511 5.57959C25.3946 5.72772 26.0287 5.9727 26.6593 6.16846C27.3433 5.97648 28.0776 5.95162 28.8185 6.16455C29.2306 6.28299 29.6269 6.46238 30.0373 6.58936C30.7081 6.79695 31.4724 6.98507 32.0539 7.01319C35.195 7.16504 36.6776 6.71358 39.6769 5.69385C39.6901 5.68936 39.7037 5.6851 39.717 5.68116ZM45.7111 7.99951C43.9938 7.05853 41.9923 7.16313 40.1877 7.77588C39.8344 7.90062 39.4783 8.04953 39.1222 8.22217C36.7445 9.37492 34.4283 11.5618 32.7961 14.5405C31.1639 17.5192 30.5672 20.6477 30.8752 23.272C30.9368 23.7972 31.0577 24.2812 31.1691 24.7886C31.6931 26.6779 32.7517 28.1338 34.2336 28.9458C36.091 29.9635 38.4422 29.877 40.8224 28.7231C43.2002 27.5704 45.5163 25.3836 47.1486 22.4048C48.7809 19.4259 49.3775 16.2968 49.0695 13.6724C48.7611 11.0452 47.5685 9.0173 45.7111 7.99951ZM49.9005 26.5269C50.1565 25.8672 50.8991 25.5395 51.5587 25.7954L54.547 26.9556C55.2066 27.2116 55.5333 27.9532 55.2775 28.6128C55.0216 29.2724 54.2799 29.6 53.6203 29.3442L50.632 28.1851C49.9724 27.9291 49.6448 27.1865 49.9005 26.5269ZM39.1652 9.26807C41.2542 8.13629 43.5696 7.82566 45.505 8.88623C47.0749 9.74653 48.0237 11.3117 48.4123 13.1314C48.5106 13.5926 48.2163 14.0465 47.755 14.145C47.2939 14.2433 46.8399 13.949 46.7414 13.4878C46.4268 12.0152 45.7071 10.9451 44.6837 10.3843C43.4283 9.69643 41.7566 9.8068 39.9787 10.77C39.5506 11.0019 39.123 11.2806 38.7023 11.603C40.0255 12.7843 41.0004 14.0292 41.5402 15.2642C42.1439 16.6456 42.2208 18.0723 41.5373 19.3198C40.8549 20.5651 39.5891 21.2565 38.1007 21.4917C36.7655 21.7027 35.1903 21.5621 33.4992 21.1011C33.4542 21.6278 33.4497 22.1372 33.4845 22.6216C33.6295 24.6386 34.4361 26.1074 35.6916 26.7954C36.9471 27.4832 38.6187 27.3719 40.3966 26.4087C42.1654 25.4504 43.9291 23.6955 45.217 21.3452C45.7016 20.4607 46.0801 19.562 46.3556 18.6772C46.496 18.227 46.9746 17.9755 47.425 18.1157C47.8752 18.256 48.1266 18.7347 47.9865 19.1851C47.6767 20.1801 47.2535 21.1839 46.715 22.1665C45.2929 24.7616 43.3083 26.7749 41.2101 27.9116C39.1212 29.0432 36.8056 29.354 34.8703 28.2935C32.9348 27.2329 31.9507 25.1135 31.7804 22.7437C31.6095 20.3635 32.2393 17.6083 33.6613 15.0132C35.0834 12.418 37.067 10.4048 39.1652 9.26807ZM22.297 15.0933C21.7014 16.2428 21.4438 16.9252 21.34 17.3735C21.2375 17.8161 21.2789 18.0251 21.3586 18.4282L21.3654 18.4614C21.4906 19.0963 21.8766 19.7651 22.5392 20.4849C23.1994 21.202 24.0755 21.9061 25.0754 22.6187C25.4732 22.9022 25.706 22.918 25.9572 22.812C26.2951 22.6693 26.8456 22.2264 27.5851 21.0552C27.0094 19.7746 26.4315 18.8074 26.0109 18.3853C25.5581 17.9307 24.9198 17.457 24.3625 17.0815C24.0174 16.8492 23.6334 16.6577 23.3009 16.4087C22.8302 16.056 22.5072 15.5915 22.297 15.0933ZM37.4191 12.7485C36.5848 13.607 35.8111 14.6442 35.1593 15.8335C34.5066 17.0247 34.0481 18.237 33.7736 19.4038C35.3748 19.8612 36.7608 19.9738 37.8341 19.8042C38.9745 19.624 39.6854 19.1451 40.0392 18.4995C40.4026 17.8364 40.4284 16.9858 39.9748 15.9478C39.5441 14.9626 38.6986 13.8621 37.4191 12.7485ZM34.2082 9.14893C33.8924 9.16621 33.5635 9.17537 33.2189 9.17627C33.1388 9.23151 33.0511 9.29794 32.9562 9.37647C32.6169 9.65722 32.2522 10.0395 31.8879 10.479C31.1588 11.3585 30.4955 12.3901 30.1144 13.0855C29.6886 13.8625 29.21 14.8403 28.8732 15.8257C28.5838 16.6724 28.4237 17.4545 28.4445 18.0923C28.6141 18.3652 28.7815 18.6595 28.9445 18.9683C29.2882 17.1514 29.9462 15.2969 30.923 13.5142C31.8396 11.8415 32.9612 10.371 34.2082 9.14893ZM28.2306 8.22315C27.9953 8.13158 27.6238 8.07071 27.3918 8.1919C26.8031 8.49937 25.6057 9.41 25.0587 10.4204C24.7889 10.919 24.5664 11.6366 24.3986 12.355C24.273 12.8924 24.0375 13.5361 24.1984 14.0757C24.2869 14.3723 24.4121 14.5578 24.5441 14.6704C24.8879 14.8728 25.225 15.0872 25.5558 15.3101C25.9699 15.589 26.4729 15.9497 26.9435 16.3472C27.0299 15.9811 27.1376 15.6205 27.256 15.2739C27.6363 14.1611 28.164 13.0878 28.6154 12.2642C29.0414 11.4867 29.7649 10.3634 30.5724 9.38916C30.6783 9.26141 30.7871 9.1351 30.8976 9.01123C30.3592 8.90738 29.8338 8.76163 29.4064 8.6294C28.8588 8.45993 28.422 8.2968 28.2306 8.22315ZM24.2443 7.65479C24.0745 7.62582 23.8022 7.66603 23.422 7.94483C23.0226 8.23774 22.6009 8.7314 22.2453 9.38037C21.9128 9.98712 21.7449 10.6469 21.7257 11.1851C21.7078 11.6907 21.8192 11.8972 21.842 11.939C21.8441 11.9429 21.8456 11.9461 21.8459 11.9468L22.6076 12.5562C22.6451 12.3681 22.687 12.1695 22.7345 11.9663C22.9122 11.206 23.1764 10.3097 23.5568 9.60694C23.909 8.95632 24.4229 8.35989 24.9357 7.86866L24.2443 7.65479Z" 135 + fill={theme.colors["accent-1"]} 136 /> 137 </g> 138 <defs>
+27 -25
app/home/page.tsx
··· 1 import { cookies } from "next/headers"; 2 - import { Fact, ReplicacheProvider } from "src/replicache"; 3 import type { Attribute } from "src/replicache/attributes"; 4 import { 5 ThemeBackgroundProvider, ··· 9 import { createIdentity } from "actions/createIdentity"; 10 import { drizzle } from "drizzle-orm/node-postgres"; 11 import { IdentitySetter } from "./IdentitySetter"; 12 - import { LeafletList } from "./LeafletList"; 13 import { getIdentityData } from "actions/getIdentityData"; 14 import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets"; 15 - import { HomeSidebar } from "./HomeSidebar"; 16 - import { HomeFooter } from "./HomeFooter"; 17 - import { Media } from "components/Media"; 18 - import { MyPublicationList } from "./Publications"; 19 import { supabaseServerClient } from "supabase/serverClient"; 20 import { pool } from "supabase/pool"; 21 22 export default async function Home() { 23 let cookieStore = await cookies(); ··· 59 60 if (!permission_token) 61 return ( 62 - <div className="p-4 text-lg text-center flex flex-col gap-4"> 63 - <p>Sorry, home page not found!</p> 64 <p> 65 This may be a glitch on our end. If the issue persists please{" "} 66 <a href="mailto:contact@leaflet.pub">send us a note</a>. 67 </p> 68 - </div> 69 ); 70 let [homeLeafletFacts, allLeafletFacts] = await Promise.all([ 71 supabaseServerClient.rpc("get_facts", { ··· 87 88 let root_entity = permission_token.root_entity; 89 let home_docs_initialFacts = allLeafletFacts?.result || {}; 90 return ( 91 <ReplicacheProvider 92 rootEntity={root_entity} ··· 99 set={permission_token.permission_token_rights[0].entity_set} 100 > 101 <ThemeProvider entityID={root_entity}> 102 - <div className="homeWrapper flex h-full bg-bg-leaflet pwa-padding"> 103 - <ThemeBackgroundProvider entityID={root_entity}> 104 - <div className="home relative max-w-screen-lg w-full h-full mx-auto flex sm:flex-row flex-col sm:items-stretch sm:px-6 "> 105 - <HomeSidebar /> 106 - <div className={`h-full overflow-y-scroll`}> 107 - <Media mobile> 108 - <div className="pubListWrapper p-2 "> 109 - <MyPublicationList /> 110 - </div> 111 - </Media> 112 - <LeafletList initialFacts={home_docs_initialFacts} /> 113 - </div> 114 - <HomeFooter /> 115 - </div> 116 - </ThemeBackgroundProvider> 117 - </div> 118 </ThemeProvider> 119 </EntitySetProvider> 120 </ReplicacheProvider>
··· 1 import { cookies } from "next/headers"; 2 + import { Fact, ReplicacheProvider, useEntity } from "src/replicache"; 3 import type { Attribute } from "src/replicache/attributes"; 4 import { 5 ThemeBackgroundProvider, ··· 9 import { createIdentity } from "actions/createIdentity"; 10 import { drizzle } from "drizzle-orm/node-postgres"; 11 import { IdentitySetter } from "./IdentitySetter"; 12 + 13 import { getIdentityData } from "actions/getIdentityData"; 14 import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets"; 15 import { supabaseServerClient } from "supabase/serverClient"; 16 import { pool } from "supabase/pool"; 17 + 18 + import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 19 + import { HomeLayout } from "./HomeLayout"; 20 21 export default async function Home() { 22 let cookieStore = await cookies(); ··· 58 59 if (!permission_token) 60 return ( 61 + <NotFoundLayout> 62 + <p className="font-bold">Sorry, we can't find this home!</p> 63 <p> 64 This may be a glitch on our end. If the issue persists please{" "} 65 <a href="mailto:contact@leaflet.pub">send us a note</a>. 66 </p> 67 + </NotFoundLayout> 68 ); 69 let [homeLeafletFacts, allLeafletFacts] = await Promise.all([ 70 supabaseServerClient.rpc("get_facts", { ··· 86 87 let root_entity = permission_token.root_entity; 88 let home_docs_initialFacts = allLeafletFacts?.result || {}; 89 + 90 return ( 91 <ReplicacheProvider 92 rootEntity={root_entity} ··· 99 set={permission_token.permission_token_rights[0].entity_set} 100 > 101 <ThemeProvider entityID={root_entity}> 102 + <ThemeBackgroundProvider entityID={root_entity}> 103 + <HomeLayout 104 + titles={{ 105 + ...home_docs_initialFacts.titles, 106 + ...auth_res?.permission_token_on_homepage.reduce( 107 + (acc, tok) => { 108 + let title = 109 + tok.permission_tokens.leaflets_in_publications[0]?.title; 110 + if (title) acc[tok.permission_tokens.root_entity] = title; 111 + return acc; 112 + }, 113 + {} as { [k: string]: string }, 114 + ), 115 + }} 116 + entityID={root_entity} 117 + initialFacts={home_docs_initialFacts.facts || {}} 118 + /> 119 + </ThemeBackgroundProvider> 120 </ThemeProvider> 121 </EntitySetProvider> 122 </ReplicacheProvider>
+1 -1
app/legal/content.tsx
··· 36 </button> 37 </div> 38 <div 39 - className={`no-scrollbar border border-border rounded-md bg-bg-page sm:px-4 px-3 pt-2 pb-6 -mt-[1px] h-full overflow-y-scroll rounded-tl-none `} 40 > 41 {state === "terms" ? <Terms /> : <Privacy />} 42 </div>
··· 36 </button> 37 </div> 38 <div 39 + className={`no-scrollbar border border-border rounded-md bg-bg-page sm:px-4 px-3 pt-2 pb-6 -mt-px h-full overflow-y-scroll rounded-tl-none `} 40 > 41 {state === "terms" ? <Terms /> : <Privacy />} 42 </div>
+9 -8
app/lish/Subscribe.tsx
··· 43 <div className="flex relative w-full max-w-sm"> 44 <Input 45 type="email" 46 - className="input-with-border !pr-[104px] !py-1 grow w-full" 47 placeholder={ 48 props.compact ? "subscribe with email..." : "email here..." 49 } ··· 55 /> 56 <ButtonPrimary 57 compact 58 - className="absolute right-1 top-1 !outline-0" 59 onClick={async () => { 60 if (identity?.email) { 61 await subscribeToPublicationWithEmail(props.publication); ··· 151 <Input 152 type="text" 153 pattern="[0-9]" 154 - className="input-with-border !pr-[88px] !py-1 max-w-[156px]" 155 placeholder="000000" 156 value={props.codeInputValue} 157 onChange={(e) => { ··· 160 /> 161 <ButtonPrimary 162 compact 163 - className="absolute right-1 top-1 !outline-0" 164 onClick={async () => { 165 console.log( 166 await confirmEmailAuthToken(props.token, props.codeInputValue), ··· 218 setSuccessModalOpen={setSuccessModalOpen} 219 /> 220 <a href={`${props.base_url}/rss`} className="flex" target="_blank"> 221 - <RSSSmall className="self-center" /> 222 </a> 223 </div> 224 </div> ··· 355 <Dialog.Root open={open} onOpenChange={setOpen}> 356 <Dialog.Trigger asChild></Dialog.Trigger> 357 <Dialog.Portal> 358 - <Dialog.Overlay className="fixed inset-0 bg-primary data-[state=open]:animate-overlayShow opacity-10 blur-sm" /> 359 <Dialog.Content 360 className={` 361 z-20 opaque-container 362 fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 363 w-96 px-3 py-4 364 - max-w-[var(--radix-popover-content-available-width)] 365 - max-h-[var(--radix-popover-content-available-height)] 366 overflow-y-scroll no-scrollbar 367 flex flex-col gap-1 text-center justify-center 368 `}
··· 43 <div className="flex relative w-full max-w-sm"> 44 <Input 45 type="email" 46 + className="input-with-border pr-[104px]! py-1! grow w-full" 47 placeholder={ 48 props.compact ? "subscribe with email..." : "email here..." 49 } ··· 55 /> 56 <ButtonPrimary 57 compact 58 + className="absolute right-1 top-1 outline-0!" 59 onClick={async () => { 60 if (identity?.email) { 61 await subscribeToPublicationWithEmail(props.publication); ··· 151 <Input 152 type="text" 153 pattern="[0-9]" 154 + className="input-with-border pr-[88px]! py-1! max-w-[156px]" 155 placeholder="000000" 156 value={props.codeInputValue} 157 onChange={(e) => { ··· 160 /> 161 <ButtonPrimary 162 compact 163 + className="absolute right-1 top-1 outline-0!" 164 onClick={async () => { 165 console.log( 166 await confirmEmailAuthToken(props.token, props.codeInputValue), ··· 218 setSuccessModalOpen={setSuccessModalOpen} 219 /> 220 <a href={`${props.base_url}/rss`} className="flex" target="_blank"> 221 + <span className="sr-only">Subscribe to RSS</span> 222 + <RSSSmall className="self-center" aria-hidden /> 223 </a> 224 </div> 225 </div> ··· 356 <Dialog.Root open={open} onOpenChange={setOpen}> 357 <Dialog.Trigger asChild></Dialog.Trigger> 358 <Dialog.Portal> 359 + <Dialog.Overlay className="fixed inset-0 bg-primary data-[state=open]:animate-overlayShow opacity-10 blur-xs" /> 360 <Dialog.Content 361 className={` 362 z-20 opaque-container 363 fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 364 w-96 px-3 py-4 365 + max-w-(--radix-popover-content-available-width) 366 + max-h-(--radix-popover-content-available-height) 367 overflow-y-scroll no-scrollbar 368 flex flex-col gap-1 text-center justify-center 369 `}
+85 -48
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox.tsx
··· 19 import { publishComment } from "./commentAction"; 20 import { ButtonPrimary } from "components/Buttons"; 21 import { ShareSmall } from "components/Icons/ShareSmall"; 22 - import { useInteractionState } from "../Interactions"; 23 import { DotLoader } from "components/utils/DotLoader"; 24 import { rangeHasMark } from "src/utils/prosemirror/rangeHasMark"; 25 import { setMark } from "src/utils/prosemirror/setMark"; ··· 35 import { create } from "zustand"; 36 import { CloseTiny } from "components/Icons/CloseTiny"; 37 import { CloseFillTiny } from "components/Icons/CloseFillTiny"; 38 39 export function CommentBox(props: { 40 doc_uri: string; 41 replyTo?: string; 42 onSubmit?: () => void; 43 }) { 44 let mountRef = useRef<HTMLPreElement | null>(null); 45 - let quote = useInteractionState((s) => s.commentBox.quote); 46 let [editorState, setEditorState] = useState(() => 47 EditorState.create({ 48 schema: multiBlockSchema, ··· 58 "Mod-z": undo, 59 "Mod-y": redo, 60 "Shift-Mod-z": redo, 61 }), 62 keymap(baseKeymap), 63 autolink({ ··· 81 e.clipboardData?.getData("text") || 82 e.clipboardData?.getData("text/html"); 83 let html = e.clipboardData?.getData("text/html"); 84 if (!text && html) { 85 let xml = new DOMParser().parseFromString(html, "text/html"); 86 text = xml.textContent || ""; ··· 96 if (!quoteParam) return; 97 const quotePosition = decodeQuotePosition(quoteParam); 98 if (!quotePosition) return; 99 - useInteractionState.setState({ 100 commentBox: { quote: quotePosition }, 101 }); 102 return true; ··· 124 }, 125 }, 126 ); 127 return () => { 128 view.current?.destroy(); 129 view.current = null; 130 }; 131 }, []); 132 - let [loading, setLoading] = useState(false); 133 return ( 134 <div className=" flex flex-col"> 135 {quote && ( ··· 138 <button 139 className="text-border absolute -top-3 right-1 bg-bg-page p-1 rounded-full" 140 onClick={() => 141 - useInteractionState.setState({ commentBox: { quote: null } }) 142 } 143 > 144 <CloseFillTiny /> ··· 148 <div className="w-full relative group"> 149 <pre 150 ref={mountRef} 151 - className={`border whitespace-pre-wrap input-with-border min-h-32 h-fit !px-2 !py-[6px]`} 152 /> 153 <IOSBS view={view} /> 154 </div> ··· 175 </div> 176 <ButtonPrimary 177 compact 178 - onClick={async () => { 179 - setLoading(true); 180 - let [plaintext, facets] = docToFacetedText(editorState.doc); 181 - let comment = await publishComment({ 182 - document: props.doc_uri, 183 - comment: { 184 - plaintext, 185 - facets, 186 - replyTo: props.replyTo, 187 - attachment: quote 188 - ? { 189 - $type: "pub.leaflet.comment#linearDocumentQuote", 190 - document: props.doc_uri, 191 - quote, 192 - } 193 - : undefined, 194 - }, 195 - }); 196 - 197 - let tr = editorState.tr; 198 - tr = tr.replaceWith( 199 - 0, 200 - editorState.doc.content.size, 201 - multiBlockSchema.nodes.paragraph.createAndFill()!, 202 - ); 203 - view.current?.dispatch(tr); 204 - setLoading(false); 205 - props.onSubmit?.(); 206 - useInteractionState.setState((s) => ({ 207 - commentBox: { 208 - quote: null, 209 - }, 210 - localComments: [ 211 - ...s.localComments, 212 - { 213 - record: comment.record, 214 - uri: comment.uri, 215 - bsky_profiles: { record: comment.profile as Json }, 216 - }, 217 - ], 218 - })); 219 - }} 220 > 221 {loading ? <DotLoader /> : <ShareSmall />} 222 </ButtonPrimary>
··· 19 import { publishComment } from "./commentAction"; 20 import { ButtonPrimary } from "components/Buttons"; 21 import { ShareSmall } from "components/Icons/ShareSmall"; 22 + import { useInteractionState, setInteractionState } from "../Interactions"; 23 import { DotLoader } from "components/utils/DotLoader"; 24 import { rangeHasMark } from "src/utils/prosemirror/rangeHasMark"; 25 import { setMark } from "src/utils/prosemirror/setMark"; ··· 35 import { create } from "zustand"; 36 import { CloseTiny } from "components/Icons/CloseTiny"; 37 import { CloseFillTiny } from "components/Icons/CloseFillTiny"; 38 + import { betterIsUrl } from "src/utils/isURL"; 39 40 export function CommentBox(props: { 41 doc_uri: string; 42 replyTo?: string; 43 onSubmit?: () => void; 44 + autoFocus?: boolean; 45 }) { 46 let mountRef = useRef<HTMLPreElement | null>(null); 47 + let { commentBox: { quote } } = useInteractionState(props.doc_uri); 48 + let [loading, setLoading] = useState(false); 49 + 50 + const handleSubmit = async () => { 51 + if (loading || !view.current) return; 52 + 53 + setLoading(true); 54 + let currentState = view.current.state; 55 + let [plaintext, facets] = docToFacetedText(currentState.doc); 56 + let comment = await publishComment({ 57 + document: props.doc_uri, 58 + comment: { 59 + plaintext, 60 + facets, 61 + replyTo: props.replyTo, 62 + attachment: quote 63 + ? { 64 + $type: "pub.leaflet.comment#linearDocumentQuote", 65 + document: props.doc_uri, 66 + quote, 67 + } 68 + : undefined, 69 + }, 70 + }); 71 + 72 + let tr = currentState.tr; 73 + tr = tr.replaceWith( 74 + 0, 75 + currentState.doc.content.size, 76 + multiBlockSchema.nodes.paragraph.createAndFill()!, 77 + ); 78 + view.current.dispatch(tr); 79 + setLoading(false); 80 + props.onSubmit?.(); 81 + setInteractionState(props.doc_uri, (s) => ({ 82 + commentBox: { 83 + quote: null, 84 + }, 85 + localComments: [ 86 + ...s.localComments, 87 + { 88 + record: comment.record, 89 + uri: comment.uri, 90 + bsky_profiles: { record: comment.profile as Json }, 91 + }, 92 + ], 93 + })); 94 + }; 95 + 96 let [editorState, setEditorState] = useState(() => 97 EditorState.create({ 98 schema: multiBlockSchema, ··· 108 "Mod-z": undo, 109 "Mod-y": redo, 110 "Shift-Mod-z": redo, 111 + "Ctrl-Enter": () => { handleSubmit(); return true; }, 112 + "Meta-Enter": () => { handleSubmit(); return true; }, 113 }), 114 keymap(baseKeymap), 115 autolink({ ··· 133 e.clipboardData?.getData("text") || 134 e.clipboardData?.getData("text/html"); 135 let html = e.clipboardData?.getData("text/html"); 136 + if (text && betterIsUrl(text)) { 137 + let selection = view.state.selection as TextSelection; 138 + let tr = view.state.tr; 139 + let { from, to } = selection; 140 + if (selection.empty) { 141 + tr.insertText(text, selection.from); 142 + tr.addMark( 143 + from, 144 + from + text.length, 145 + multiBlockSchema.marks.link.create({ href: text }), 146 + ); 147 + } else { 148 + tr.addMark( 149 + from, 150 + to, 151 + multiBlockSchema.marks.link.create({ href: text }), 152 + ); 153 + } 154 + view.dispatch(tr); 155 + return true; 156 + } 157 if (!text && html) { 158 let xml = new DOMParser().parseFromString(html, "text/html"); 159 text = xml.textContent || ""; ··· 169 if (!quoteParam) return; 170 const quotePosition = decodeQuotePosition(quoteParam); 171 if (!quotePosition) return; 172 + setInteractionState(props.doc_uri, { 173 commentBox: { quote: quotePosition }, 174 }); 175 return true; ··· 197 }, 198 }, 199 ); 200 + 201 + if (props.autoFocus) { 202 + view.current.focus(); 203 + } 204 + 205 return () => { 206 view.current?.destroy(); 207 view.current = null; 208 }; 209 }, []); 210 + 211 return ( 212 <div className=" flex flex-col"> 213 {quote && ( ··· 216 <button 217 className="text-border absolute -top-3 right-1 bg-bg-page p-1 rounded-full" 218 onClick={() => 219 + setInteractionState(props.doc_uri, { commentBox: { quote: null } }) 220 } 221 > 222 <CloseFillTiny /> ··· 226 <div className="w-full relative group"> 227 <pre 228 ref={mountRef} 229 + className={`border whitespace-pre-wrap input-with-border min-h-32 h-fit px-2! py-[6px]!`} 230 /> 231 <IOSBS view={view} /> 232 </div> ··· 253 </div> 254 <ButtonPrimary 255 compact 256 + onClick={handleSubmit} 257 > 258 {loading ? <DotLoader /> : <ShareSmall />} 259 </ButtonPrimary>
+7 -4
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
··· 1 "use client"; 2 import { CloseTiny } from "components/Icons/CloseTiny"; 3 - import { useInteractionState } from "../Interactions"; 4 import { useIdentityData } from "components/IdentityProvider"; 5 import { CommentBox } from "./CommentBox"; 6 import { Json } from "supabase/database.types"; ··· 13 import { BlueskyTiny } from "components/Icons/BlueskyTiny"; 14 import { Popover } from "components/Popover"; 15 import { AppBskyActorProfile, AtUri } from "@atproto/api"; 16 - import { timeAgo } from "app/discover/PubListing"; 17 import { BlueskyLogin } from "app/login/LoginForm"; 18 import { usePathname } from "next/navigation"; 19 import { QuoteContent } from "../Quotes"; 20 21 export type Comment = { 22 record: Json; ··· 25 }; 26 export function Comments(props: { document_uri: string; comments: Comment[] }) { 27 let { identity } = useIdentityData(); 28 - let localComments = useInteractionState((l) => l.localComments); 29 let comments = useMemo(() => { 30 return [...localComments, ...props.comments]; 31 }, [props.comments, localComments]); ··· 44 Comments 45 <button 46 className="text-tertiary" 47 - onClick={() => useInteractionState.setState({ drawerOpen: false })} 48 > 49 <CloseTiny /> 50 </button> ··· 191 <CommentBox 192 doc_uri={props.document} 193 replyTo={props.comment_uri} 194 onSubmit={() => { 195 setReplyBoxOpen(false); 196 }}
··· 1 "use client"; 2 import { CloseTiny } from "components/Icons/CloseTiny"; 3 + import { useInteractionState, setInteractionState } from "../Interactions"; 4 import { useIdentityData } from "components/IdentityProvider"; 5 import { CommentBox } from "./CommentBox"; 6 import { Json } from "supabase/database.types"; ··· 13 import { BlueskyTiny } from "components/Icons/BlueskyTiny"; 14 import { Popover } from "components/Popover"; 15 import { AppBskyActorProfile, AtUri } from "@atproto/api"; 16 import { BlueskyLogin } from "app/login/LoginForm"; 17 import { usePathname } from "next/navigation"; 18 import { QuoteContent } from "../Quotes"; 19 + import { timeAgo } from "src/utils/timeAgo"; 20 21 export type Comment = { 22 record: Json; ··· 25 }; 26 export function Comments(props: { document_uri: string; comments: Comment[] }) { 27 let { identity } = useIdentityData(); 28 + let { localComments } = useInteractionState(props.document_uri); 29 let comments = useMemo(() => { 30 return [...localComments, ...props.comments]; 31 }, [props.comments, localComments]); ··· 44 Comments 45 <button 46 className="text-tertiary" 47 + onClick={() => 48 + setInteractionState(props.document_uri, { drawerOpen: false }) 49 + } 50 > 51 <CloseTiny /> 52 </button> ··· 193 <CommentBox 194 doc_uri={props.document} 195 replyTo={props.comment_uri} 196 + autoFocus={true} 197 onSubmit={() => { 198 setReplyBoxOpen(false); 199 }}
+109 -21
app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx
··· 6 import { create } from "zustand"; 7 import type { Comment } from "./Comments"; 8 import { QuotePosition } from "../quotePosition"; 9 10 - export let useInteractionState = create(() => ({ 11 - drawerOpen: undefined as boolean | undefined, 12 - drawer: undefined as undefined | "comments" | "quotes", 13 - localComments: [] as Comment[], 14 - commentBox: { quote: null as QuotePosition | null }, 15 - })); 16 - export function openInteractionDrawer(drawer: "comments" | "quotes") { 17 flushSync(() => { 18 - useInteractionState.setState({ drawerOpen: true, drawer }); 19 }); 20 let el = document.getElementById("interaction-drawer"); 21 let isOffscreen = false; 22 if (el) { 23 const rect = el.getBoundingClientRect(); 24 - const windowHeight = 25 - window.innerHeight || document.documentElement.clientHeight; 26 const windowWidth = 27 window.innerWidth || document.documentElement.clientWidth; 28 - isOffscreen = rect.right > windowWidth; 29 } 30 31 - if (el && isOffscreen) el.scrollIntoView({ behavior: "smooth" }); 32 } 33 34 export const Interactions = (props: { ··· 38 className?: string; 39 showComments?: boolean; 40 }) => { 41 - let { drawerOpen, drawer } = useInteractionState(); 42 43 return ( 44 <div ··· 48 className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 49 onClick={() => { 50 if (!drawerOpen || drawer !== "quotes") 51 - openInteractionDrawer("quotes"); 52 - else useInteractionState.setState({ drawerOpen: false }); 53 }} 54 > 55 - <QuoteTiny /> {props.quotesCount}{" "} 56 - {!props.compact && `Quote${props.quotesCount === 1 ? "" : "s"}`} 57 </button> 58 {props.showComments === false ? null : ( 59 <button 60 className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 61 onClick={() => { 62 if (!drawerOpen || drawer !== "comments") 63 - openInteractionDrawer("comments"); 64 - else useInteractionState.setState({ drawerOpen: false }); 65 }} 66 > 67 - <CommentTiny /> {props.commentsCount}{" "} 68 - {!props.compact && `Comment${props.commentsCount === 1 ? "" : "s"}`} 69 </button> 70 )} 71 </div>
··· 6 import { create } from "zustand"; 7 import type { Comment } from "./Comments"; 8 import { QuotePosition } from "../quotePosition"; 9 + import { useContext } from "react"; 10 + import { PostPageContext } from "../PostPageContext"; 11 12 + type InteractionState = { 13 + drawerOpen: undefined | boolean; 14 + drawer: undefined | "comments" | "quotes"; 15 + localComments: Comment[]; 16 + commentBox: { quote: QuotePosition | null }; 17 + }; 18 + 19 + const defaultInteractionState: InteractionState = { 20 + drawerOpen: undefined, 21 + drawer: undefined, 22 + localComments: [], 23 + commentBox: { quote: null }, 24 + }; 25 + 26 + export let useInteractionStateStore = create<{ 27 + [document_uri: string]: InteractionState; 28 + }>(() => ({})); 29 + 30 + export function useInteractionState(document_uri?: string) { 31 + return useInteractionStateStore((state) => { 32 + if (!document_uri || !state[document_uri]) { 33 + return defaultInteractionState; 34 + } 35 + return state[document_uri]; 36 + }); 37 + } 38 + 39 + export function setInteractionState( 40 + document_uri: string, 41 + update: 42 + | Partial<InteractionState> 43 + | ((state: InteractionState) => Partial<InteractionState>), 44 + ) { 45 + useInteractionStateStore.setState((state) => { 46 + if (!state[document_uri]) { 47 + state[document_uri] = { ...defaultInteractionState }; 48 + } 49 + 50 + const currentDocState = state[document_uri]; 51 + const updatedState = 52 + typeof update === "function" ? update(currentDocState) : update; 53 + 54 + const newState = { 55 + ...state, 56 + [document_uri]: { 57 + ...currentDocState, 58 + ...updatedState, 59 + }, 60 + }; 61 + 62 + // Update query parameter when drawer state changes 63 + if ( 64 + typeof window !== "undefined" && 65 + (updatedState.drawerOpen !== undefined || 66 + updatedState.drawer !== undefined) 67 + ) { 68 + const url = new URL(window.location.href); 69 + const newDocState = newState[document_uri]; 70 + 71 + if (newDocState.drawerOpen && newDocState.drawer) { 72 + url.searchParams.set("interactionDrawer", newDocState.drawer); 73 + } else { 74 + url.searchParams.delete("interactionDrawer"); 75 + } 76 + 77 + window.history.replaceState({}, "", url.toString()); 78 + } 79 + 80 + return newState; 81 + }); 82 + } 83 + export function openInteractionDrawer( 84 + drawer: "comments" | "quotes", 85 + document_uri: string, 86 + ) { 87 flushSync(() => { 88 + setInteractionState(document_uri, { drawerOpen: true, drawer }); 89 }); 90 let el = document.getElementById("interaction-drawer"); 91 let isOffscreen = false; 92 if (el) { 93 const rect = el.getBoundingClientRect(); 94 const windowWidth = 95 window.innerWidth || document.documentElement.clientWidth; 96 + isOffscreen = rect.right > windowWidth - 64; 97 } 98 99 + if (el && isOffscreen) 100 + el.scrollIntoView({ 101 + behavior: "smooth", 102 + block: "center", 103 + inline: "center", 104 + }); 105 } 106 107 export const Interactions = (props: { ··· 111 className?: string; 112 showComments?: boolean; 113 }) => { 114 + const data = useContext(PostPageContext); 115 + const document_uri = data?.uri; 116 + if (!document_uri) 117 + throw new Error("document_uri not available in PostPageContext"); 118 + 119 + let { drawerOpen, drawer } = useInteractionState(document_uri); 120 121 return ( 122 <div ··· 126 className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 127 onClick={() => { 128 if (!drawerOpen || drawer !== "quotes") 129 + openInteractionDrawer("quotes", document_uri); 130 + else setInteractionState(document_uri, { drawerOpen: false }); 131 }} 132 > 133 + <span className="sr-only">Post quotes</span> 134 + <QuoteTiny aria-hidden /> {props.quotesCount}{" "} 135 + {!props.compact && ( 136 + <span 137 + aria-hidden 138 + >{`Quote${props.quotesCount === 1 ? "" : "s"}`}</span> 139 + )} 140 </button> 141 {props.showComments === false ? null : ( 142 <button 143 className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 144 onClick={() => { 145 if (!drawerOpen || drawer !== "comments") 146 + openInteractionDrawer("comments", document_uri); 147 + else setInteractionState(document_uri, { drawerOpen: false }); 148 }} 149 > 150 + <span className="sr-only">Post comments</span> 151 + <CommentTiny aria-hidden /> {props.commentsCount}{" "} 152 + {!props.compact && ( 153 + <span 154 + aria-hidden 155 + >{`Comment${props.commentsCount === 1 ? "" : "s"}`}</span> 156 + )} 157 </button> 158 )} 159 </div>
+5 -3
app/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx
··· 2 import { CloseTiny } from "components/Icons/CloseTiny"; 3 import { useContext } from "react"; 4 import { useIsMobile } from "src/hooks/isMobile"; 5 - import { useInteractionState } from "./Interactions"; 6 import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 7 import { AtUri } from "@atproto/api"; 8 import { Json } from "supabase/database.types"; ··· 25 did: string; 26 }) => { 27 let data = useContext(PostPageContext); 28 29 return ( 30 <div className="flex flex-col gap-2"> ··· 32 Quotes 33 <button 34 className="text-tertiary" 35 - onClick={() => useInteractionState.setState({ drawerOpen: false })} 36 > 37 <CloseTiny /> 38 </button> ··· 127 blocks={content} 128 did={props.did} 129 preview 130 - className="!py-0" 131 /> 132 </div> 133 </div>
··· 2 import { CloseTiny } from "components/Icons/CloseTiny"; 3 import { useContext } from "react"; 4 import { useIsMobile } from "src/hooks/isMobile"; 5 + import { setInteractionState } from "./Interactions"; 6 import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 7 import { AtUri } from "@atproto/api"; 8 import { Json } from "supabase/database.types"; ··· 25 did: string; 26 }) => { 27 let data = useContext(PostPageContext); 28 + const document_uri = data?.uri; 29 + if (!document_uri) throw new Error('document_uri not available in PostPageContext'); 30 31 return ( 32 <div className="flex flex-col gap-2"> ··· 34 Quotes 35 <button 36 className="text-tertiary" 37 + onClick={() => setInteractionState(document_uri, { drawerOpen: false })} 38 > 39 <CloseTiny /> 40 </button> ··· 129 blocks={content} 130 did={props.did} 131 preview 132 + className="py-0!" 133 /> 134 </div> 135 </div>
+8 -8
app/lish/[did]/[publication]/[rkey]/PostContent.tsx
··· 116 postBlockWrapper 117 min-h-7 118 pt-1 pb-2 119 - ${isList && "isListItem !pb-0 "} 120 ${alignment} 121 `; 122 ··· 156 } 157 case PubLeafletBlocksUnorderedList.isMain(b.block): { 158 return ( 159 - <ul className="-ml-[1px] sm:ml-[9px] pb-2"> 160 {b.block.children.map((child, i) => ( 161 <ListItem 162 bskyPostData={bskyPostData} ··· 194 <div className="pt-2 pb-2 px-3 grow min-w-0"> 195 <div className="flex flex-col w-full min-w-0 h-full grow "> 196 <div 197 - className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-none resize-none align-top border h-[24px] line-clamp-1`} 198 style={{ 199 overflow: "hidden", 200 textOverflow: "ellipsis", ··· 205 </div> 206 207 <div 208 - className={`linkBlockDescription text-sm bg-transparent border-none outline-none resize-none align-top grow line-clamp-2`} 209 > 210 {b.block.description} 211 </div> ··· 236 alt={b.block.alt} 237 height={b.block.aspectRatio?.height} 238 width={b.block.aspectRatio?.width} 239 - className={`!pt-3 sm:!pt-4 rounded-md ${className}`} 240 src={blobRefToSrc(b.block.image.ref, did)} 241 /> 242 {b.block.alt && ( ··· 259 return ( 260 // highly unfortunate hack so that the border-l on blockquote is the height of just the text rather than the height of the block, which includes padding. 261 <blockquote 262 - className={` blockquote !py-0 !mb-2 last:!mb-3 last:sm:!mb-4 first:!mt-2 sm:first:pt-3 ${className} ${PubLeafletBlocksBlockquote.isMain(previousBlock?.block) ? "!-mt-2" : "!mt-1"}`} 263 {...blockProps} 264 > 265 <TextBlock ··· 337 ) : null; 338 339 return ( 340 - <li className={`!pb-0 flex flex-row gap-2`}> 341 <div 342 - className={`listMarker shrink-0 mx-2 z-[1] mt-[14px] h-[5px] w-[5px] ${props.item.content?.$type !== "null" ? "rounded-full bg-secondary" : ""}`} 343 /> 344 <div className="flex flex-col w-full"> 345 <Block
··· 116 postBlockWrapper 117 min-h-7 118 pt-1 pb-2 119 + ${isList && "isListItem pb-0! "} 120 ${alignment} 121 `; 122 ··· 156 } 157 case PubLeafletBlocksUnorderedList.isMain(b.block): { 158 return ( 159 + <ul className="-ml-px sm:ml-[9px] pb-2"> 160 {b.block.children.map((child, i) => ( 161 <ListItem 162 bskyPostData={bskyPostData} ··· 194 <div className="pt-2 pb-2 px-3 grow min-w-0"> 195 <div className="flex flex-col w-full min-w-0 h-full grow "> 196 <div 197 + className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 198 style={{ 199 overflow: "hidden", 200 textOverflow: "ellipsis", ··· 205 </div> 206 207 <div 208 + className={`linkBlockDescription text-sm bg-transparent border-none outline-hidden resize-none align-top grow line-clamp-2`} 209 > 210 {b.block.description} 211 </div> ··· 236 alt={b.block.alt} 237 height={b.block.aspectRatio?.height} 238 width={b.block.aspectRatio?.width} 239 + className={`pt-3! sm:pt-4! rounded-md ${className}`} 240 src={blobRefToSrc(b.block.image.ref, did)} 241 /> 242 {b.block.alt && ( ··· 259 return ( 260 // highly unfortunate hack so that the border-l on blockquote is the height of just the text rather than the height of the block, which includes padding. 261 <blockquote 262 + className={` blockquote py-0! mb-2! last:mb-3! sm:last:mb-4! first:mt-2! sm:first:pt-3 ${className} ${PubLeafletBlocksBlockquote.isMain(previousBlock?.block) ? "-mt-2!" : "mt-1!"}`} 263 {...blockProps} 264 > 265 <TextBlock ··· 337 ) : null; 338 339 return ( 340 + <li className={`pb-0! flex flex-row gap-2`}> 341 <div 342 + className={`listMarker shrink-0 mx-2 z-1 mt-[14px] h-[5px] w-[5px] ${props.item.content?.$type !== "null" ? "rounded-full bg-secondary" : ""}`} 343 /> 344 <div className="flex flex-col w-full"> 345 <Block
+11 -4
app/lish/[did]/[publication]/[rkey]/QuoteHandler.tsx
··· 3 import { CopyTiny } from "components/Icons/CopyTiny"; 4 import { Separator } from "components/Layout"; 5 import { useSmoker } from "components/Toast"; 6 - import { useEffect, useMemo, useState } from "react"; 7 import { 8 encodeQuotePosition, 9 decodeQuotePosition, ··· 11 } from "./quotePosition"; 12 import { useIdentityData } from "components/IdentityProvider"; 13 import { CommentTiny } from "components/Icons/CommentTiny"; 14 - import { useInteractionState } from "./Interactions/Interactions"; 15 16 export function QuoteHandler() { 17 let [position, setPosition] = useState<{ ··· 128 export const QuoteOptionButtons = (props: { position: string }) => { 129 let smoker = useSmoker(); 130 let { identity } = useIdentityData(); 131 let [url, position] = useMemo(() => { 132 let currentUrl = new URL(window.location.href); 133 let pos = decodeQuotePosition(props.position); ··· 135 currentUrl.pathname = currentUrl.pathname.split("/l-quote/")[0]; 136 } 137 currentUrl.pathname = currentUrl.pathname + `/l-quote/${props.position}`; 138 139 currentUrl.hash = `#${pos?.start.block.join(".")}_${pos?.start.offset}`; 140 return [currentUrl.toString(), pos]; ··· 145 <div className="">Share via</div> 146 147 <a 148 - className="flex gap-1 items-center hover:font-bold px-1 hover:!no-underline" 149 role="link" 150 href={`https://bsky.app/intent/compose?text=${encodeURIComponent(url)}`} 151 target="_blank" ··· 183 className="flex gap-1 items-center hover:font-bold px-1" 184 onClick={() => { 185 if (!position) return; 186 - useInteractionState.setState({ 187 drawer: "comments", 188 drawerOpen: true, 189 commentBox: { quote: position },
··· 3 import { CopyTiny } from "components/Icons/CopyTiny"; 4 import { Separator } from "components/Layout"; 5 import { useSmoker } from "components/Toast"; 6 + import { useEffect, useMemo, useState, useContext } from "react"; 7 import { 8 encodeQuotePosition, 9 decodeQuotePosition, ··· 11 } from "./quotePosition"; 12 import { useIdentityData } from "components/IdentityProvider"; 13 import { CommentTiny } from "components/Icons/CommentTiny"; 14 + import { setInteractionState } from "./Interactions/Interactions"; 15 + import { PostPageContext } from "./PostPageContext"; 16 17 export function QuoteHandler() { 18 let [position, setPosition] = useState<{ ··· 129 export const QuoteOptionButtons = (props: { position: string }) => { 130 let smoker = useSmoker(); 131 let { identity } = useIdentityData(); 132 + const data = useContext(PostPageContext); 133 + const document_uri = data?.uri; 134 + if (!document_uri) throw new Error('document_uri not available in PostPageContext'); 135 let [url, position] = useMemo(() => { 136 let currentUrl = new URL(window.location.href); 137 let pos = decodeQuotePosition(props.position); ··· 139 currentUrl.pathname = currentUrl.pathname.split("/l-quote/")[0]; 140 } 141 currentUrl.pathname = currentUrl.pathname + `/l-quote/${props.position}`; 142 + 143 + // Clear existing query parameters 144 + currentUrl.search = ""; 145 146 currentUrl.hash = `#${pos?.start.block.join(".")}_${pos?.start.offset}`; 147 return [currentUrl.toString(), pos]; ··· 152 <div className="">Share via</div> 153 154 <a 155 + className="flex gap-1 items-center hover:font-bold px-1 hover:no-underline!" 156 role="link" 157 href={`https://bsky.app/intent/compose?text=${encodeURIComponent(url)}`} 158 target="_blank" ··· 190 className="flex gap-1 items-center hover:font-bold px-1" 191 onClick={() => { 192 if (!position) return; 193 + setInteractionState(document_uri, { 194 drawer: "comments", 195 drawerOpen: true, 196 commentBox: { quote: position },
+2 -2
app/lish/[did]/[publication]/[rkey]/StaticPostContent.tsx
··· 157 className?: string; 158 }) { 159 return ( 160 - <li className={`!pb-0 flex flex-row gap-2`}> 161 <div 162 - className={`listMarker shrink-0 mx-2 z-[1] mt-[14px] h-[5px] w-[5px] rounded-full bg-secondary`} 163 /> 164 <div className="flex flex-col"> 165 <Block block={{ block: props.item.content }} did={props.did} isList />
··· 157 className?: string; 158 }) { 159 return ( 160 + <li className={`pb-0! flex flex-row gap-2`}> 161 <div 162 + className={`listMarker shrink-0 mx-2 z-1 mt-[14px] h-[5px] w-[5px] rounded-full bg-secondary`} 163 /> 164 <div className="flex flex-col"> 165 <Block block={{ block: props.item.content }} did={props.did} isList />
+3 -23
app/lish/[did]/[publication]/dashboard/Actions.tsx
··· 22 export const Actions = (props: { publication: string }) => { 23 return ( 24 <> 25 - <Media mobile> 26 - <SpeedyLink 27 - href="/home" 28 - className="hover:no-underline" 29 - style={{ textDecorationLine: "none !important" }} 30 - > 31 - <ActionButton icon={<HomeSmall />} label="Go Home" /> 32 - </SpeedyLink> 33 - </Media> 34 <NewDraftActionButton publication={props.publication} /> 35 <PublicationShareButton /> 36 <PublicationThemeButton /> 37 <PublicationSettingsButton publication={props.publication} /> 38 - <hr className="border-border-light" /> 39 - <Media mobile={false}> 40 - <SpeedyLink 41 - href="/home" 42 - className="hover:no-underline" 43 - style={{ textDecorationLine: "none !important" }} 44 - > 45 - <ActionButton icon={<HomeSmall />} label="Go Home" /> 46 - </SpeedyLink> 47 - </Media> 48 </> 49 ); 50 }; ··· 64 <ActionButton 65 id="pub-share-button" 66 icon=<ShareSmall /> 67 - secondary 68 label="Share" 69 onClick={() => {}} 70 /> ··· 72 > 73 <MenuItem onSelect={() => {}}> 74 <SpeedyLink 75 - href={getPublicationURL(pub!)} 76 className="text-secondary hover:no-underline" 77 > 78 <div>Viewer Mode</div> ··· 85 onSelect={(e) => { 86 e.preventDefault(); 87 let rect = (e.currentTarget as Element)?.getBoundingClientRect(); 88 - navigator.clipboard.writeText(getPublicationURL(pub!)); 89 smoker({ 90 position: { 91 x: rect ? rect.left + (rect.right - rect.left) / 2 : 0, ··· 133 return ( 134 <Popover 135 asChild 136 - className="max-w-xs pb-0 !bg-white" 137 side={isMobile ? "top" : "right"} 138 align={isMobile ? "center" : "start"} 139 trigger={
··· 22 export const Actions = (props: { publication: string }) => { 23 return ( 24 <> 25 <NewDraftActionButton publication={props.publication} /> 26 <PublicationShareButton /> 27 <PublicationThemeButton /> 28 <PublicationSettingsButton publication={props.publication} /> 29 </> 30 ); 31 }; ··· 45 <ActionButton 46 id="pub-share-button" 47 icon=<ShareSmall /> 48 label="Share" 49 onClick={() => {}} 50 /> ··· 52 > 53 <MenuItem onSelect={() => {}}> 54 <SpeedyLink 55 + href={getPublicationURL(pub?.publication!)} 56 className="text-secondary hover:no-underline" 57 > 58 <div>Viewer Mode</div> ··· 65 onSelect={(e) => { 66 e.preventDefault(); 67 let rect = (e.currentTarget as Element)?.getBoundingClientRect(); 68 + navigator.clipboard.writeText(getPublicationURL(pub?.publication!)); 69 smoker({ 70 position: { 71 x: rect ? rect.left + (rect.right - rect.left) / 2 : 0, ··· 113 return ( 114 <Popover 115 asChild 116 + className="max-w-xs pb-0 bg-white!" 117 side={isMobile ? "top" : "right"} 118 align={isMobile ? "center" : "start"} 119 trigger={
+49 -104
app/lish/[did]/[publication]/dashboard/DraftList.tsx
··· 1 "use client"; 2 3 import { NewDraftSecondaryButton } from "./NewDraftButton"; 4 - import React, { useState } from "react"; 5 import { usePublicationData } from "./PublicationSWRProvider"; 6 - import { Menu, MenuItem } from "components/Layout"; 7 - import { deleteDraft } from "./deleteDraft"; 8 - import { DeleteSmall } from "components/Icons/DeleteSmall"; 9 - import { ButtonPrimary } from "components/Buttons"; 10 - import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny"; 11 - import { SpeedyLink } from "components/SpeedyLink"; 12 13 - export function DraftList() { 14 let { data: pub_data } = usePublicationData(); 15 - if (!pub_data) return null; 16 return ( 17 - <div className="flex flex-col gap-4 pb-4"> 18 - <NewDraftSecondaryButton fullWidth publication={pub_data?.uri} /> 19 - {pub_data.leaflets_in_publications 20 - .filter((d) => !d.doc) 21 - .map((d) => { 22 - return ( 23 - <React.Fragment key={d.leaflet}> 24 - <Draft id={d.leaflet} {...d} /> 25 - <hr className="last:hidden border-border-light" /> 26 - </React.Fragment> 27 - ); 28 - })} 29 - </div> 30 - ); 31 - } 32 33 - function Draft(props: { id: string; title: string; description: string }) { 34 - let [open, setOpen] = useState(false); 35 - return ( 36 - <div className="flex flex-row gap-2 items-start"> 37 - <SpeedyLink 38 - key={props.id} 39 - href={`/${props.id}`} 40 - className="flex flex-col gap-0 hover:!no-underline grow" 41 - > 42 - {props.title ? ( 43 - <h3 className="text-primary">{props.title}</h3> 44 - ) : ( 45 - <h3 className="text-tertiary italic">Untitled</h3> 46 - )} 47 - <div className="text-secondary italic">{props.description}</div> 48 - </SpeedyLink> 49 - <Menu 50 - open={open} 51 - onOpenChange={(o) => setOpen(o)} 52 - align="end" 53 - asChild 54 - trigger={ 55 - <button className="text-secondary rounded-md selected-outline !border-transparent hover:!border-border "> 56 - <MoreOptionsVerticalTiny /> 57 - </button> 58 - } 59 - > 60 - <> 61 - <DeleteDraft id={props.id} /> 62 - </> 63 - </Menu> 64 </div> 65 ); 66 } 67 - 68 - export function DeleteDraft(props: { id: string }) { 69 - let { mutate } = usePublicationData(); 70 - let [state, setState] = useState<"normal" | "confirm">("normal"); 71 - 72 - if (state === "normal") { 73 - return ( 74 - <MenuItem 75 - className="justify-end" 76 - onSelect={(e) => { 77 - if (state === "normal") { 78 - e.preventDefault(); 79 - return setState("confirm"); 80 - } 81 - }} 82 - > 83 - Delete Draft 84 - <DeleteSmall /> 85 - </MenuItem> 86 - ); 87 - } 88 - if (state === "confirm") { 89 - return ( 90 - <div className="flex flex-col items-center font-bold text-secondary px-2 py-1"> 91 - Are you sure? 92 - <div className="text-sm text-tertiary font-normal"> 93 - This action cannot be undone! 94 - </div> 95 - <ButtonPrimary 96 - className="mt-2" 97 - onClick={async () => { 98 - await mutate((data) => { 99 - if (!data) return data; 100 - return { 101 - ...data, 102 - leaflets_in_publications: data.leaflets_in_publications.filter( 103 - (d) => d.leaflet !== props.id, 104 - ), 105 - }; 106 - }, false); 107 - await deleteDraft(props.id); 108 - }} 109 - > 110 - Delete 111 - </ButtonPrimary> 112 - </div> 113 - ); 114 - } 115 - }
··· 1 "use client"; 2 3 import { NewDraftSecondaryButton } from "./NewDraftButton"; 4 + import React from "react"; 5 import { usePublicationData } from "./PublicationSWRProvider"; 6 + import { LeafletList } from "app/home/HomeLayout"; 7 8 + export function DraftList(props: { 9 + searchValue: string; 10 + showPageBackground: boolean; 11 + }) { 12 let { data: pub_data } = usePublicationData(); 13 + if (!pub_data?.publication) return null; 14 + let { leaflets_in_publications, ...publication } = pub_data.publication; 15 return ( 16 + <div className="flex flex-col gap-4"> 17 + <NewDraftSecondaryButton 18 + fullWidth 19 + publication={pub_data?.publication?.uri} 20 + /> 21 22 + <LeafletList 23 + searchValue={props.searchValue} 24 + showPreview={false} 25 + defaultDisplay="list" 26 + cardBorderHidden={!props.showPageBackground} 27 + leaflets={leaflets_in_publications 28 + .filter((l) => !l.documents) 29 + .map((l) => { 30 + return { 31 + token: { 32 + ...l.permission_tokens!, 33 + leaflets_in_publications: [ 34 + { 35 + ...l, 36 + publications: { 37 + ...publication, 38 + }, 39 + }, 40 + ], 41 + }, 42 + added_at: "", 43 + }; 44 + })} 45 + initialFacts={pub_data.leaflet_data.facts || {}} 46 + titles={{ 47 + ...leaflets_in_publications.reduce( 48 + (acc, leaflet) => { 49 + if (leaflet.title && leaflet.permission_tokens) 50 + acc[leaflet.permission_tokens.root_entity] = leaflet.title; 51 + return acc; 52 + }, 53 + {} as { [l: string]: string }, 54 + ), 55 + }} 56 + /> 57 + <div className="spacer h-12 w-full bg-transparent shrink-0 " /> 58 </div> 59 ); 60 }
+1 -1
app/lish/[did]/[publication]/dashboard/NewDraftButton.tsx
··· 17 router.push(`/${newLeaflet}`); 18 }} 19 icon=<AddTiny className="m-1 shrink-0" /> 20 - label="New Draft" 21 /> 22 ); 23 }
··· 17 router.push(`/${newLeaflet}`); 18 }} 19 icon=<AddTiny className="m-1 shrink-0" /> 20 + label="New" 21 /> 22 ); 23 }
+74 -71
app/lish/[did]/[publication]/dashboard/PublicationDashboard.tsx
··· 1 "use client"; 2 - import { BlobRef } from "@atproto/lexicon"; 3 - import { useState } from "react"; 4 - import { useIsMobile } from "src/hooks/isMobile"; 5 - import { theme } from "tailwind.config"; 6 - import { usePublicationData } from "./PublicationSWRProvider"; 7 import { PubLeafletPublication } from "lexicons/api"; 8 9 - type Tabs = { [tabName: string]: React.ReactNode }; 10 - export function PublicationDashboard<T extends Tabs>(props: { 11 - name: string; 12 - tabs: T; 13 - defaultTab: keyof T; 14 - icon: BlobRef | null; 15 - did: string; 16 }) { 17 - let { data: pub } = usePublicationData(); 18 - let showPageBackground = !!(pub?.record as PubLeafletPublication.Record) 19 - ?.theme?.showPageBackground; 20 - let [tab, setTab] = useState(props.defaultTab); 21 - let content = props.tabs[tab]; 22 23 - return ( 24 - <> 25 - <div className="pubDashHeader flex flex-row gap-2 w-full justify-between border-b border-border text-secondary items-center "> 26 - <div className="max-w-full w-[1000px] h-full "> 27 - <div 28 - className={`flex gap-2 h-fit py-0.5 pl-1 pr-2 w-fit rounded-md ${showPageBackground ? "bg-none sm:bg-[rgba(var(--bg-page),0.8)]" : ""}`} 29 - > 30 - {props.icon && ( 31 - <div 32 - className="pubDashLogo shrink-0 w-6 h-6 rounded-full border-2 border-bg-page " 33 - style={{ 34 - backgroundImage: `url(/api/atproto_images?did=${props.did}&cid=${(props.icon.ref as unknown as { $link: string })["$link"]})`, 35 - backgroundRepeat: "no-repeat", 36 - backgroundPosition: "center", 37 - backgroundSize: "cover", 38 - }} 39 - /> 40 - )} 41 - <div className="pubDashName font-bold grow text-tertiary max-w-full truncate sm:block hidden"> 42 - {props.name} 43 - </div> 44 - </div> 45 - </div> 46 - <div className="pubDashTabs flex flex-row gap-1"> 47 - {Object.keys(props.tabs).map((t) => ( 48 - <Tab 49 - key={t} 50 - name={t} 51 - selected={t === tab} 52 - onSelect={() => setTab(t)} 53 - showPageBackground={showPageBackground} 54 - /> 55 - ))} 56 - </div> 57 - </div> 58 - <div 59 - className={`pubDashContent py-4 px-3 sm:px-4 h-full overflow-auto ${showPageBackground ? "rounded-b-md border border-border border-t-0 bg-[rgba(var(--bg-page),var(--bg-page-alpha))]" : ""}`} 60 - > 61 - {content} 62 - </div> 63 - </> 64 ); 65 - } 66 67 - function Tab(props: { 68 - name: string; 69 - selected: boolean; 70 - showPageBackground: boolean; 71 - onSelect: () => void; 72 - }) { 73 return ( 74 - <div 75 - className={`pubTabs border border-b-0 px-2 pt-1 pb-0.5 rounded-t-md border-border hover:cursor-pointer ${props.selected ? "text-accent-contrast font-bold -mb-[1px]" : ""} ${props.showPageBackground ? "bg-[rgba(var(--bg-page),var(--bg-page-alpha))]" : ""}`} 76 - onClick={() => props.onSelect()} 77 - > 78 - {props.name} 79 - </div> 80 ); 81 }
··· 1 "use client"; 2 + 3 + import { DraftList } from "./DraftList"; 4 + import { GetPublicationDataReturnType } from "app/api/rpc/[command]/get_publication_data"; 5 + import { Actions } from "./Actions"; 6 + import React, { useState } from "react"; 7 + import { PublishedPostsList } from "./PublishedPostsLists"; 8 import { PubLeafletPublication } from "lexicons/api"; 9 + import { PublicationSubscribers } from "./PublicationSubscribers"; 10 + import { AtUri } from "@atproto/syntax"; 11 + import { 12 + HomeDashboardControls, 13 + DashboardLayout, 14 + PublicationDashboardControls, 15 + } from "components/PageLayouts/DashboardLayout"; 16 + import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 17 18 + export default function PublicationDashboard({ 19 + publication, 20 + record, 21 + }: { 22 + record: PubLeafletPublication.Record; 23 + publication: Exclude< 24 + GetPublicationDataReturnType["result"]["publication"], 25 + null 26 + >; 27 }) { 28 + let [searchValue, setSearchValue] = useState(""); 29 + let [debouncedSearchValue, setDebouncedSearchValue] = useState(""); 30 31 + useDebouncedEffect( 32 + () => { 33 + setDebouncedSearchValue(searchValue); 34 + }, 35 + 200, 36 + [searchValue], 37 ); 38 39 return ( 40 + <DashboardLayout 41 + id={publication.uri} 42 + hasBackgroundImage={!!record?.theme?.backgroundImage} 43 + defaultTab="Drafts" 44 + tabs={{ 45 + Drafts: { 46 + content: ( 47 + <DraftList 48 + searchValue={debouncedSearchValue} 49 + showPageBackground={!!record.theme?.showPageBackground} 50 + /> 51 + ), 52 + controls: ( 53 + <PublicationDashboardControls 54 + defaultDisplay={"list"} 55 + hasBackgroundImage={!!record?.theme?.backgroundImage} 56 + searchValue={searchValue} 57 + setSearchValueAction={setSearchValue} 58 + /> 59 + ), 60 + }, 61 + Published: { 62 + content: ( 63 + <PublishedPostsList 64 + searchValue={debouncedSearchValue} 65 + showPageBackground={!!record.theme?.showPageBackground} 66 + /> 67 + ), 68 + controls: null, 69 + }, 70 + Subscribers: { 71 + content: ( 72 + <PublicationSubscribers 73 + showPageBackground={!!record.theme?.showPageBackground} 74 + /> 75 + ), 76 + controls: null, 77 + }, 78 + }} 79 + actions={<Actions publication={publication.uri} />} 80 + currentPage="pub" 81 + publication={publication.uri} 82 + /> 83 ); 84 }
+2 -2
app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx
··· 12 publication_data: GetPublicationDataReturnType["result"]; 13 children: React.ReactNode; 14 }) { 15 - let key = `publication-data-${props.publication_did}`; 16 return ( 17 <PublicationContext 18 value={{ name: props.publication_rkey, did: props.publication_did }} ··· 32 33 export function usePublicationData() { 34 let { name, did } = useContext(PublicationContext); 35 - let key = `publication-data-${did}`; 36 let { data, mutate } = useSWR( 37 key, 38 async () =>
··· 12 publication_data: GetPublicationDataReturnType["result"]; 13 children: React.ReactNode; 14 }) { 15 + let key = `publication-data-${props.publication_did}-${props.publication_rkey}`; 16 return ( 17 <PublicationContext 18 value={{ name: props.publication_rkey, did: props.publication_did }} ··· 32 33 export function usePublicationData() { 34 let { name, did } = useContext(PublicationContext); 35 + let key = `publication-data-${did}-${name}`; 36 let { data, mutate } = useSWR( 37 key, 38 async () =>
+188 -40
app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx
··· 1 "use client"; 2 import { AppBskyActorProfile } from "lexicons/api"; 3 import { usePublicationData } from "./PublicationSWRProvider"; 4 - import { blobRefToSrc } from "src/utils/blobRefToSrc"; 5 import { ButtonPrimary } from "components/Buttons"; 6 import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 7 import { useSmoker } from "components/Toast"; 8 9 - export function PublicationSubscribers() { 10 let { data: publication } = usePublicationData(); 11 - let smoker = useSmoker(); 12 13 if (!publication) return <div>null</div>; 14 - if (publication.publication_subscriptions.length === 0) 15 return ( 16 - <div className="italic text-tertiary flex flex-col gap-0 text-center justify-center pt-4"> 17 <p className="font-bold"> No subscribers yet </p> 18 <p>Start sharing your publication!</p> 19 <ButtonPrimary ··· 21 onClick={(e) => { 22 e.preventDefault(); 23 let rect = (e.currentTarget as Element)?.getBoundingClientRect(); 24 - navigator.clipboard.writeText(getPublicationURL(publication!)); 25 smoker({ 26 position: { 27 x: rect ? rect.left + (rect.right - rect.left) / 2 : 0, ··· 37 ); 38 39 return ( 40 - <div> 41 - <h2>{publication.publication_subscriptions.length} Subscribers </h2> 42 - <div className="flex gap-2 flex-col"> 43 - {publication.publication_subscriptions 44 .sort((a, b) => { 45 return b.created_at.localeCompare(a.created_at); 46 }) 47 .map((subscriber, index) => { 48 if (!subscriber.identities?.bsky_profiles) return null; 49 let handle = subscriber.identities?.bsky_profiles.handle; 50 let profile = subscriber.identities?.bsky_profiles 51 ?.record as AppBskyActorProfile.Record | null; 52 if (!profile) return null; 53 return ( 54 - <div key={subscriber.identities.bsky_profiles?.did}> 55 - <a 56 - target="_blank" 57 - href={`https://bsky.app/profile/${subscriber.identities.bsky_profiles?.did}`} 58 - className="flex text-primary p-2 gap-1" 59 - > 60 - <div className="flex flex-row gap-2"> 61 - {profile.avatar && ( 62 - <img 63 - className="rounded-full w-8 h-8" 64 - src={ 65 - profile?.avatar && 66 - blobRefToSrc( 67 - profile.avatar.ref, 68 - subscriber.identities.bsky_profiles?.did, 69 - ) 70 - } 71 - alt={profile.displayName} 72 - /> 73 - )} 74 - <div className="flex flex-col gap-1"> 75 - <h3>{profile.displayName}</h3> 76 - <p>@{handle}</p> 77 - </div> 78 - </div> 79 - </a> 80 - {index !== publication.publication_subscriptions.length - 1 && ( 81 - <hr className="border-border" /> 82 - )} 83 - </div> 84 ); 85 })} 86 </div> 87 </div> 88 ); 89 }
··· 1 "use client"; 2 import { AppBskyActorProfile } from "lexicons/api"; 3 import { usePublicationData } from "./PublicationSWRProvider"; 4 import { ButtonPrimary } from "components/Buttons"; 5 import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 6 import { useSmoker } from "components/Toast"; 7 + import { Menu, MenuItem, Separator } from "components/Layout"; 8 + import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny"; 9 + import { Checkbox } from "components/Checkbox"; 10 + import { useEffect, useState } from "react"; 11 12 + type subscriber = { email: string | undefined; did: string | undefined }; 13 + 14 + // HELLO! THIS FILE HAS CHECKBOXES AND A HEADER COMMENTED OUT 15 + // the checkboxes would let you select users and and the eader provided a count and let you select all and do actions 16 + // I also removed some props from SubscriberListItem around emails since we dont have them yet. 17 + // WHen we get emails in, we can uncomment some of this stuff 18 + 19 + export function PublicationSubscribers(props: { 20 + showPageBackground?: boolean; 21 + }) { 22 + let smoker = useSmoker(); 23 let { data: publication } = usePublicationData(); 24 + // let [checkedSubscribers, setCheckedSubscribers] = useState<subscriber[]>([]); 25 + // let [checkAll, setCheckAll] = useState(false); 26 27 if (!publication) return <div>null</div>; 28 + let subscribers = publication.publication?.publication_subscriptions || []; 29 + 30 + // useEffect(() => { 31 + // const allSubscribersSelected = subscribers.every((subscriber) => 32 + // checkedSubscribers.some( 33 + // (checked) => 34 + // checked.email === "dummyemail@email.com" && 35 + // checked.did === subscriber.identities?.bsky_profiles?.did, 36 + // ), 37 + // ); 38 + 39 + // if (allSubscribersSelected && subscribers.length > 0) { 40 + // setCheckAll(true); 41 + // } else { 42 + // setCheckAll(false); 43 + // } 44 + // }, [checkedSubscribers]); 45 + 46 + if (subscribers.length === 0) 47 return ( 48 + <div 49 + className={`italic text-tertiary flex flex-col gap-0 text-center justify-center mt-4 border rounded-md ${props.showPageBackground ? "border-border-light p-2" : "border-transparent"}`} 50 + style={ 51 + props.showPageBackground 52 + ? { 53 + backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha)) ", 54 + } 55 + : { backgroundColor: "transparent" } 56 + } 57 + > 58 <p className="font-bold"> No subscribers yet </p> 59 <p>Start sharing your publication!</p> 60 <ButtonPrimary ··· 62 onClick={(e) => { 63 e.preventDefault(); 64 let rect = (e.currentTarget as Element)?.getBoundingClientRect(); 65 + navigator.clipboard.writeText( 66 + getPublicationURL(publication.publication!), 67 + ); 68 smoker({ 69 position: { 70 x: rect ? rect.left + (rect.right - rect.left) / 2 : 0, ··· 80 ); 81 82 return ( 83 + <div 84 + className={`rounded-md ${props.showPageBackground ? "border-border-light p-2" : "border-transparent"}`} 85 + style={ 86 + props.showPageBackground 87 + ? { 88 + backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha)) ", 89 + } 90 + : { backgroundColor: "transparent" } 91 + } 92 + > 93 + {/*<div className="subscriberListHeader flex gap-2 "> 94 + <Checkbox 95 + checked={checkAll} 96 + onChange={() => { 97 + if (checkAll === false) { 98 + const allSubscribers = subscribers.map((subscriber) => ({ 99 + email: "dummyemail@email.com", 100 + did: subscriber.identities?.bsky_profiles?.did, 101 + })); 102 + setCheckedSubscribers(allSubscribers); 103 + } else { 104 + setCheckedSubscribers([]); 105 + } 106 + }} 107 + className="!font-bold text-secondary mb-1" 108 + > 109 + {subscribers.length} Subscriber{subscribers.length !== 1 && "s"} 110 + </Checkbox> 111 + {checkedSubscribers.length !== 0 && ( 112 + <SubscriberOptions 113 + checkedSubscribers={checkedSubscribers} 114 + allSelected={checkedSubscribers.length === subscribers.length} 115 + /> 116 + )} 117 + </div>*/} 118 + <div className="!font-bold text-secondary mb-1"> 119 + {subscribers.length} Subscriber{subscribers.length !== 1 && "s"} 120 + </div> 121 + <hr className="mb-2 border-border " /> 122 + <div className="subscriberListContent flex gap-3 flex-col "> 123 + {subscribers 124 .sort((a, b) => { 125 return b.created_at.localeCompare(a.created_at); 126 }) 127 .map((subscriber, index) => { 128 if (!subscriber.identities?.bsky_profiles) return null; 129 let handle = subscriber.identities?.bsky_profiles.handle; 130 + let did = subscriber.identities?.bsky_profiles.did; 131 let profile = subscriber.identities?.bsky_profiles 132 ?.record as AppBskyActorProfile.Record | null; 133 if (!profile) return null; 134 return ( 135 + <SubscriberListItem 136 + key={`${subscriber.identities.bsky_profiles?.did}`} 137 + handle={handle ? handle : undefined} 138 + did={`${subscriber.identities.bsky_profiles?.did}`} 139 + createdAt={subscriber.created_at} 140 + /> 141 ); 142 })} 143 </div> 144 </div> 145 ); 146 } 147 + 148 + const SubscriberListItem = (props: { 149 + handle: string | undefined; 150 + did: string | undefined; 151 + createdAt: string; 152 + }) => { 153 + return ( 154 + // <Checkbox 155 + // className="!font-normal" 156 + // checked={props.checked} 157 + // onChange={() => { 158 + // if (props.checked === false) { 159 + // const newCheckedSubscribers = [ 160 + // ...props.checkedSubscribers, 161 + // { 162 + // email: props.email, 163 + // did: props.did, 164 + // }, 165 + // ]; 166 + // props.setCheckedSubscribers(newCheckedSubscribers); 167 + // } else { 168 + // const newCheckedSubscribers = props.checkedSubscribers.filter( 169 + // (subscriber) => 170 + // !( 171 + // subscriber.email === props.email && subscriber.did === props.did 172 + // ), 173 + // ); 174 + // props.setCheckedSubscribers(newCheckedSubscribers); 175 + // } 176 + // console.log(props.checkedSubscribers); 177 + // }} 178 + // > 179 + <> 180 + <div className="flex items-end flex-row gap-2 w-full"> 181 + {/*<a 182 + target="_blank" 183 + href={`mailto:${props.email}`} 184 + className="font-bold text-primary" 185 + > 186 + {props.email} 187 + </a> 188 + 189 + {props.handle && props.email && ( 190 + <Separator classname="sm:block hidden" /> 191 + )}*/} 192 + {props.handle && ( 193 + <a 194 + target="_blank" 195 + href={`https://bsky.app/profile/${props.did}`} 196 + className={"font-bold"} 197 + > 198 + @{props.handle} 199 + </a> 200 + )} 201 + <div className="px-1 py-0 h-max rounded-md text-sm italic text-tertiary"> 202 + {new Date(props.createdAt).toLocaleString(undefined, { 203 + year: "2-digit", 204 + month: "2-digit", 205 + day: "2-digit", 206 + })} 207 + </div> 208 + </div> 209 + </> 210 + // </Checkbox> 211 + ); 212 + }; 213 + 214 + const SubscriberOptions = (props: { 215 + checkedSubscribers: subscriber[]; 216 + allSelected: boolean; 217 + }) => { 218 + return ( 219 + <Menu 220 + asChild 221 + className="" 222 + trigger={ 223 + <ButtonPrimary compact className="-mt-[1px]"> 224 + {props.allSelected ? "All" : props.checkedSubscribers.length} Selected{" "} 225 + <MoreOptionsVerticalTiny /> 226 + </ButtonPrimary> 227 + } 228 + > 229 + <MenuItem className="justify-center" onSelect={() => {}}> 230 + Export {props.allSelected ? "All" : "Selected"} 231 + </MenuItem> 232 + <MenuItem className="justify-center" onSelect={() => {}}> 233 + Remove {props.allSelected ? "All" : "Selected"} 234 + </MenuItem> 235 + </Menu> 236 + ); 237 + };
+39 -22
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
··· 18 import { QuoteTiny } from "components/Icons/QuoteTiny"; 19 import { CommentTiny } from "components/Icons/CommentTiny"; 20 21 - export function PublishedPostsList() { 22 - let { data: publication } = usePublicationData(); 23 let params = useParams(); 24 if (!publication) return null; 25 if (publication.documents_in_publications.length === 0) 26 return ( ··· 29 </div> 30 ); 31 return ( 32 - <div className="publishedList w-full flex flex-col gap-4 pb-4"> 33 {publication.documents_in_publications 34 .sort((a, b) => { 35 let aRecord = a.documents?.data! as PubLeafletDocument.Record; ··· 49 ); 50 let uri = new AtUri(doc.documents.uri); 51 let record = doc.documents.data as PubLeafletDocument.Record; 52 - let quotes = 53 - doc.documents.document_mentions_in_bsky[0]?.count || 0; 54 - let comments = 55 - doc.documents.comments_on_documents[0]?.count || 0; 56 57 return ( 58 <Fragment key={doc.documents?.uri}> 59 <div className="flex gap-2 w-full "> 60 - <div className="publishedPost grow flex flex-col hover:!no-underline"> 61 <div className="flex justify-between gap-2"> 62 <a 63 - className="hover:!no-underline" 64 target="_blank" 65 href={`${getPublicationURL(publication)}/${uri.rkey}`} 66 > ··· 100 )} 101 </p> 102 ) : null} 103 - {(comments > 0 || quotes > 0) && record.publishedAt ? " | " : ""} 104 {quotes > 0 && ( 105 <SpeedyLink 106 href={`${getPublicationURL(publication)}/${uri.rkey}?interactionDrawer=quotes`} ··· 121 </div> 122 </div> 123 </div> 124 - <hr className="last:hidden border-border-light" /> 125 </Fragment> 126 ); 127 })} ··· 136 alignOffset={20} 137 asChild 138 trigger={ 139 - <button className="text-secondary rounded-md selected-outline !border-transparent hover:!border-border h-min"> 140 <MoreOptionsVerticalTiny /> 141 </button> 142 } ··· 149 }; 150 151 function OptionsMenu(props: { document_uri: string }) { 152 - let { mutate, data: publication } = usePublicationData(); 153 let [state, setState] = useState<"normal" | "confirm">("normal"); 154 155 - let postLink = publication 156 - ? `${getPublicationURL(publication)}/${new AtUri(props.document_uri).rkey}` 157 : null; 158 159 if (state === "normal") { ··· 203 if (!data) return data; 204 return { 205 ...data, 206 - leaflets_in_publications: data.leaflets_in_publications.filter( 207 - (l) => l.doc !== props.document_uri, 208 - ), 209 - documents_in_publications: 210 - data.documents_in_publications.filter( 211 - (d) => d.documents?.uri !== props.document_uri, 212 - ), 213 }; 214 }, false); 215 await deletePost(props.document_uri);
··· 18 import { QuoteTiny } from "components/Icons/QuoteTiny"; 19 import { CommentTiny } from "components/Icons/CommentTiny"; 20 21 + export function PublishedPostsList(props: { 22 + searchValue: string; 23 + showPageBackground: boolean; 24 + }) { 25 + let { data } = usePublicationData(); 26 let params = useParams(); 27 + let { publication } = data!; 28 if (!publication) return null; 29 if (publication.documents_in_publications.length === 0) 30 return ( ··· 33 </div> 34 ); 35 return ( 36 + <div className="publishedList w-full flex flex-col gap-2 pb-4"> 37 {publication.documents_in_publications 38 .sort((a, b) => { 39 let aRecord = a.documents?.data! as PubLeafletDocument.Record; ··· 53 ); 54 let uri = new AtUri(doc.documents.uri); 55 let record = doc.documents.data as PubLeafletDocument.Record; 56 + let quotes = doc.documents.document_mentions_in_bsky[0]?.count || 0; 57 + let comments = doc.documents.comments_on_documents[0]?.count || 0; 58 59 return ( 60 <Fragment key={doc.documents?.uri}> 61 <div className="flex gap-2 w-full "> 62 + <div 63 + className={`publishedPost grow flex flex-col hover:no-underline! rounded-lg border ${props.showPageBackground ? "border-border-light py-1 px-2" : "border-transparent px-1"}`} 64 + style={{ 65 + backgroundColor: props.showPageBackground 66 + ? "rgba(var(--bg-page), var(--bg-page-alpha))" 67 + : "transparent", 68 + }} 69 + > 70 <div className="flex justify-between gap-2"> 71 <a 72 + className="hover:no-underline!" 73 target="_blank" 74 href={`${getPublicationURL(publication)}/${uri.rkey}`} 75 > ··· 109 )} 110 </p> 111 ) : null} 112 + {(comments > 0 || quotes > 0) && record.publishedAt 113 + ? " | " 114 + : ""} 115 {quotes > 0 && ( 116 <SpeedyLink 117 href={`${getPublicationURL(publication)}/${uri.rkey}?interactionDrawer=quotes`} ··· 132 </div> 133 </div> 134 </div> 135 + {!props.showPageBackground && ( 136 + <hr className="last:hidden border-border-light" /> 137 + )} 138 </Fragment> 139 ); 140 })} ··· 149 alignOffset={20} 150 asChild 151 trigger={ 152 + <button className="text-secondary rounded-md selected-outline border-transparent! hover:border-border! h-min"> 153 <MoreOptionsVerticalTiny /> 154 </button> 155 } ··· 162 }; 163 164 function OptionsMenu(props: { document_uri: string }) { 165 + let { mutate, data } = usePublicationData(); 166 let [state, setState] = useState<"normal" | "confirm">("normal"); 167 168 + let postLink = data?.publication 169 + ? `${getPublicationURL(data?.publication)}/${new AtUri(props.document_uri).rkey}` 170 : null; 171 172 if (state === "normal") { ··· 216 if (!data) return data; 217 return { 218 ...data, 219 + publication: { 220 + ...data.publication!, 221 + leaflets_in_publications: 222 + data.publication?.leaflets_in_publications.filter( 223 + (l) => l.doc !== props.document_uri, 224 + ) || [], 225 + documents_in_publications: 226 + data.publication?.documents_in_publications.filter( 227 + (d) => d.documents?.uri !== props.document_uri, 228 + ) || [], 229 + }, 230 }; 231 }, false); 232 await deletePost(props.document_uri);
+20 -63
app/lish/[did]/[publication]/dashboard/page.tsx
··· 1 import { supabaseServerClient } from "supabase/serverClient"; 2 import { Metadata } from "next"; 3 - 4 - import { Sidebar } from "components/ActionBar/Sidebar"; 5 - 6 - import { Media } from "components/Media"; 7 - import { Footer } from "components/ActionBar/Footer"; 8 - import { PublicationDashboard } from "./PublicationDashboard"; 9 - import { DraftList } from "./DraftList"; 10 import { getIdentityData } from "actions/getIdentityData"; 11 - import { Actions } from "./Actions"; 12 - import React from "react"; 13 import { get_publication_data } from "app/api/rpc/[command]/get_publication_data"; 14 import { PublicationSWRDataProvider } from "./PublicationSWRProvider"; 15 - import { PublishedPostsList } from "./PublishedPostsLists"; 16 - import { PubLeafletPublication, PubLeafletThemeColor } from "lexicons/api"; 17 - import { PublicationSubscribers } from "./PublicationSubscribers"; 18 - import { 19 - PublicationThemeProvider, 20 - PublicationThemeProviderDashboard, 21 - } from "components/ThemeManager/PublicationThemeProvider"; 22 - import { blobRefToSrc } from "src/utils/blobRefToSrc"; 23 import { AtUri } from "@atproto/syntax"; 24 25 export async function generateMetadata(props: { 26 params: Promise<{ publication: string; did: string }>; ··· 28 let did = decodeURIComponent((await props.params).did); 29 if (!did) return { title: "Publication 404" }; 30 31 - let { result: publication } = await get_publication_data.handler( 32 { 33 did, 34 publication_name: decodeURIComponent((await props.params).publication), 35 }, 36 { supabase: supabaseServerClient }, 37 ); 38 let record = 39 (publication?.record as PubLeafletPublication.Record) || undefined; 40 if (!publication) return { title: "404 Publication" }; ··· 49 let identity = await getIdentityData(); 50 if (!identity || !identity.atp_did) 51 return ( 52 - <div className="p-4 text-lg text-center flex flex-col gap-4"> 53 - <p>Sorry, looks like you&apos;re not logged in.</p> 54 <p> 55 - This may be a glitch on our end. If the issue persists please{" "} 56 <a href="mailto:contact@leaflet.pub">send us a note</a>. 57 </p> 58 - </div> 59 ); 60 let did = decodeURIComponent(params.did); 61 if (!did) return <PubNotFound />; 62 - let { result: publication } = await get_publication_data.handler( 63 { 64 did, 65 publication_name: decodeURIComponent((await props.params).publication), 66 }, 67 { supabase: supabaseServerClient }, 68 ); 69 70 - if (!publication || identity.atp_did !== publication.identity_did) 71 return <PubNotFound />; 72 - let record = publication?.record as PubLeafletPublication.Record | null; 73 let uri = new AtUri(publication.uri); 74 - 75 - let showPageBackground = !!record?.theme?.showPageBackground; 76 77 try { 78 return ( 79 <PublicationSWRDataProvider 80 publication_did={did} 81 publication_rkey={uri.rkey} 82 - publication_data={publication} 83 > 84 <PublicationThemeProviderDashboard record={record}> 85 - <div className="pubDashWrapper relative w-max h-full flex items-stretch pwa-padding"> 86 - <div className="flex sm:flex-row flex-col max-h-full h-full"> 87 - <div 88 - className="pubDashSidebarWrapper flex justify-end items-start " 89 - style={{ width: `calc(50vw - ((var(--page-width-units)/2))` }} 90 - > 91 - <div className="pubDashSidebar relative w-16 justify-items-end"> 92 - <Sidebar className="mt-6 p-2 "> 93 - <Actions publication={publication.uri} /> 94 - </Sidebar> 95 - </div> 96 - </div> 97 - <div 98 - className={`pubDash grow sm:h-full h-32 w-full flex flex-col items-stretch pt-2 sm:pt-6 ml-[6px] sm:ml-0 max-w-[var(--page-width-units)] ${showPageBackground ? "sm:pb-8 pb-1" : "pb-0"}`} 99 - > 100 - <PublicationDashboard 101 - did={did} 102 - icon={record?.icon ? record.icon : null} 103 - name={publication.name} 104 - tabs={{ 105 - Drafts: <DraftList />, 106 - Published: <PublishedPostsList />, 107 - Subscribers: <PublicationSubscribers />, 108 - }} 109 - defaultTab={"Drafts"} 110 - /> 111 - </div> 112 - <Footer> 113 - <Actions publication={publication.uri} /> 114 - </Footer> 115 - </div> 116 - </div> 117 </PublicationThemeProviderDashboard> 118 </PublicationSWRDataProvider> 119 ); ··· 125 126 const PubNotFound = () => { 127 return ( 128 - <div className="p-4 text-lg text-center flex flex-col gap-4"> 129 - <p>Sorry, publication not found!</p> 130 <p> 131 This may be a glitch on our end. If the issue persists please{" "} 132 <a href="mailto:contact@leaflet.pub">send us a note</a>. 133 </p> 134 - </div> 135 ); 136 };
··· 1 import { supabaseServerClient } from "supabase/serverClient"; 2 import { Metadata } from "next"; 3 import { getIdentityData } from "actions/getIdentityData"; 4 import { get_publication_data } from "app/api/rpc/[command]/get_publication_data"; 5 import { PublicationSWRDataProvider } from "./PublicationSWRProvider"; 6 + import { PubLeafletPublication } from "lexicons/api"; 7 + import { PublicationThemeProviderDashboard } from "components/ThemeManager/PublicationThemeProvider"; 8 import { AtUri } from "@atproto/syntax"; 9 + import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 10 + import PublicationDashboard from "./PublicationDashboard"; 11 + import Link from "next/link"; 12 13 export async function generateMetadata(props: { 14 params: Promise<{ publication: string; did: string }>; ··· 16 let did = decodeURIComponent((await props.params).did); 17 if (!did) return { title: "Publication 404" }; 18 19 + let { result: publication_data } = await get_publication_data.handler( 20 { 21 did, 22 publication_name: decodeURIComponent((await props.params).publication), 23 }, 24 { supabase: supabaseServerClient }, 25 ); 26 + let { publication } = publication_data; 27 let record = 28 (publication?.record as PubLeafletPublication.Record) || undefined; 29 if (!publication) return { title: "404 Publication" }; ··· 38 let identity = await getIdentityData(); 39 if (!identity || !identity.atp_did) 40 return ( 41 + <NotFoundLayout> 42 + <p>Looks like you&apos;re not logged in.</p> 43 <p> 44 + If the issue persists please{" "} 45 <a href="mailto:contact@leaflet.pub">send us a note</a>. 46 </p> 47 + </NotFoundLayout> 48 ); 49 let did = decodeURIComponent(params.did); 50 if (!did) return <PubNotFound />; 51 + let { result: publication_data } = await get_publication_data.handler( 52 { 53 did, 54 publication_name: decodeURIComponent((await props.params).publication), 55 }, 56 { supabase: supabaseServerClient }, 57 ); 58 + let { publication, leaflet_data } = publication_data; 59 + let record = publication?.record as PubLeafletPublication.Record | null; 60 61 + if (!publication || identity.atp_did !== publication.identity_did || !record) 62 return <PubNotFound />; 63 let uri = new AtUri(publication.uri); 64 65 try { 66 return ( 67 <PublicationSWRDataProvider 68 publication_did={did} 69 publication_rkey={uri.rkey} 70 + publication_data={publication_data} 71 > 72 <PublicationThemeProviderDashboard record={record}> 73 + <PublicationDashboard publication={publication} record={record} /> 74 </PublicationThemeProviderDashboard> 75 </PublicationSWRDataProvider> 76 ); ··· 82 83 const PubNotFound = () => { 84 return ( 85 + <NotFoundLayout> 86 + <p className="font-bold">Sorry, we can't find this publication!</p> 87 <p> 88 This may be a glitch on our end. If the issue persists please{" "} 89 <a href="mailto:contact@leaflet.pub">send us a note</a>. 90 </p> 91 + </NotFoundLayout> 92 ); 93 };
+19 -8
app/lish/[did]/[publication]/generateFeed.ts
··· 7 } from "lexicons/api"; 8 import { createElement } from "react"; 9 import { StaticPostContent } from "./[rkey]/StaticPostContent"; 10 - import { get_publication_data } from "app/api/rpc/[command]/get_publication_data"; 11 import { supabaseServerClient } from "supabase/serverClient"; 12 import { NextResponse } from "next/server"; 13 ··· 18 let renderToReadableStream = await import("react-dom/server").then( 19 (module) => module.renderToReadableStream, 20 ); 21 - let { result: publication } = await get_publication_data.handler( 22 - { 23 - did: did, 24 - publication_name: publication_name, 25 - }, 26 - { supabase: supabaseServerClient }, 27 - ); 28 29 let pubRecord = publication?.record as PubLeafletPublication.Record; 30 if (!publication || !pubRecord)
··· 7 } from "lexicons/api"; 8 import { createElement } from "react"; 9 import { StaticPostContent } from "./[rkey]/StaticPostContent"; 10 import { supabaseServerClient } from "supabase/serverClient"; 11 import { NextResponse } from "next/server"; 12 ··· 17 let renderToReadableStream = await import("react-dom/server").then( 18 (module) => module.renderToReadableStream, 19 ); 20 + let uri; 21 + if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(publication_name)) { 22 + uri = AtUri.make( 23 + did, 24 + "pub.leaflet.publication", 25 + publication_name, 26 + ).toString(); 27 + } 28 + let { data: publication } = await supabaseServerClient 29 + .from("publications") 30 + .select( 31 + `*, 32 + publication_subscriptions(*), 33 + documents_in_publications(documents(*)) 34 + `, 35 + ) 36 + .eq("identity_did", did) 37 + .or(`name.eq."${publication_name}", uri.eq."${uri}"`) 38 + .single(); 39 40 let pubRecord = publication?.record as PubLeafletPublication.Record; 41 if (!publication || !pubRecord)
+23 -8
app/lish/[did]/[publication]/layout.tsx
··· 1 import { PubLeafletPublication } from "lexicons/api"; 2 - import { get_publication_data } from "app/api/rpc/[command]/get_publication_data"; 3 import { supabaseServerClient } from "supabase/serverClient"; 4 import { Metadata } from "next"; 5 6 export default async function PublicationLayout(props: { 7 children: React.ReactNode; ··· 16 }>; 17 }): Promise<Metadata> { 18 let params = await props.params; 19 if (!params.did || !params.publication) return { title: "Publication 404" }; 20 - let { result: publication } = await get_publication_data.handler( 21 - { 22 - did: decodeURIComponent(params.did), 23 - publication_name: decodeURIComponent(params.publication), 24 - }, 25 - { supabase: supabaseServerClient }, 26 - ); 27 if (!publication) return { title: "Publication 404" }; 28 29 let pubRecord = publication?.record as PubLeafletPublication.Record;
··· 1 import { PubLeafletPublication } from "lexicons/api"; 2 import { supabaseServerClient } from "supabase/serverClient"; 3 import { Metadata } from "next"; 4 + import { AtUri } from "@atproto/syntax"; 5 6 export default async function PublicationLayout(props: { 7 children: React.ReactNode; ··· 16 }>; 17 }): Promise<Metadata> { 18 let params = await props.params; 19 + let did = decodeURIComponent(params.did); 20 if (!params.did || !params.publication) return { title: "Publication 404" }; 21 + 22 + let uri; 23 + let publication_name = decodeURIComponent(params.publication); 24 + if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(publication_name)) { 25 + uri = AtUri.make( 26 + did, 27 + "pub.leaflet.publication", 28 + publication_name, 29 + ).toString(); 30 + } 31 + let { data: publication } = await supabaseServerClient 32 + .from("publications") 33 + .select( 34 + `*, 35 + publication_subscriptions(*), 36 + documents_in_publications(documents(*)) 37 + `, 38 + ) 39 + .eq("identity_did", did) 40 + .or(`name.eq."${publication_name}", uri.eq."${uri}"`) 41 + .single(); 42 if (!publication) return { title: "Publication 404" }; 43 44 let pubRecord = publication?.record as PubLeafletPublication.Record;
+6 -5
app/lish/[did]/[publication]/page.tsx
··· 10 PublicationBackgroundProvider, 11 PublicationThemeProvider, 12 } from "components/ThemeManager/PublicationThemeProvider"; 13 import { SpeedyLink } from "components/SpeedyLink"; 14 import { QuoteTiny } from "components/Icons/QuoteTiny"; 15 import { CommentTiny } from "components/Icons/CommentTiny"; ··· 68 className={`pubWrapper flex flex-col sm:py-6 h-full ${showPageBackground ? "max-w-prose mx-auto sm:px-0 px-[6px] py-2" : "w-full overflow-y-scroll"}`} 69 > 70 <div 71 - className={`pub sm:max-w-prose max-w-[var(--page-width-units)] w-[1000px] mx-auto px-3 sm:px-4 py-5 ${showPageBackground ? "overflow-auto h-full bg-[rgba(var(--bg-page),var(--bg-page-alpha))] border border-border rounded-lg" : "h-fit"}`} 72 > 73 <div className="pubHeader flex flex-col pb-8 w-full text-center justify-center "> 74 {record?.icon && ( ··· 141 <div className="flex w-full grow flex-col "> 142 <SpeedyLink 143 href={`${getPublicationURL(publication)}/${uri.rkey}`} 144 - className="publishedPost hover:!no-underline flex flex-col" 145 > 146 <h3 className="text-primary">{doc_record.title}</h3> 147 <p className="italic text-secondary"> ··· 198 199 const PubNotFound = () => { 200 return ( 201 - <div className="p-4 text-lg text-center flex flex-col gap-4"> 202 - <p>Sorry, publication not found!</p> 203 <p> 204 This may be a glitch on our end. If the issue persists please{" "} 205 <a href="mailto:contact@leaflet.pub">send us a note</a>. 206 </p> 207 - </div> 208 ); 209 };
··· 10 PublicationBackgroundProvider, 11 PublicationThemeProvider, 12 } from "components/ThemeManager/PublicationThemeProvider"; 13 + import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 14 import { SpeedyLink } from "components/SpeedyLink"; 15 import { QuoteTiny } from "components/Icons/QuoteTiny"; 16 import { CommentTiny } from "components/Icons/CommentTiny"; ··· 69 className={`pubWrapper flex flex-col sm:py-6 h-full ${showPageBackground ? "max-w-prose mx-auto sm:px-0 px-[6px] py-2" : "w-full overflow-y-scroll"}`} 70 > 71 <div 72 + className={`pub sm:max-w-prose max-w-(--page-width-units) w-[1000px] mx-auto px-3 sm:px-4 py-5 ${showPageBackground ? "overflow-auto h-full bg-[rgba(var(--bg-page),var(--bg-page-alpha))] border border-border rounded-lg" : "h-fit"}`} 73 > 74 <div className="pubHeader flex flex-col pb-8 w-full text-center justify-center "> 75 {record?.icon && ( ··· 142 <div className="flex w-full grow flex-col "> 143 <SpeedyLink 144 href={`${getPublicationURL(publication)}/${uri.rkey}`} 145 + className="publishedPost hover:no-underline! flex flex-col" 146 > 147 <h3 className="text-primary">{doc_record.title}</h3> 148 <p className="italic text-secondary"> ··· 199 200 const PubNotFound = () => { 201 return ( 202 + <NotFoundLayout> 203 + <p className="font-bold">Sorry, we can't find this publication!</p> 204 <p> 205 This may be a glitch on our end. If the issue persists please{" "} 206 <a href="mailto:contact@leaflet.pub">send us a note</a>. 207 </p> 208 + </NotFoundLayout> 209 ); 210 };
+2 -2
app/lish/createPub/CreatePubForm.tsx
··· 207 208 return ( 209 <div className="flex flex-col gap-1"> 210 - <label className=" input-with-border flex flex-col text-sm text-tertiary font-bold italic leading-tight !py-1 !px-[6px]"> 211 <div>Choose your domain</div> 212 <div className="flex flex-row items-center"> 213 <Input 214 minLength={3} 215 maxLength={63} 216 placeholder="domain" 217 - className="appearance-none w-full font-normal bg-transparent text-base text-primary focus:outline-0 outline-none" 218 value={props.domain} 219 onChange={(e) => props.setDomain(e.currentTarget.value)} 220 />
··· 207 208 return ( 209 <div className="flex flex-col gap-1"> 210 + <label className=" input-with-border flex flex-col text-sm text-tertiary font-bold italic leading-tight py-1! px-[6px]!"> 211 <div>Choose your domain</div> 212 <div className="flex flex-row items-center"> 213 <Input 214 minLength={3} 215 maxLength={63} 216 placeholder="domain" 217 + className="appearance-none w-full font-normal bg-transparent text-base text-primary focus:outline-0 outline-hidden" 218 value={props.domain} 219 onChange={(e) => props.setDomain(e.currentTarget.value)} 220 />
+6 -4
app/lish/createPub/UpdatePubForm.tsx
··· 21 import { Checkbox } from "components/Checkbox"; 22 23 export const EditPubForm = () => { 24 - let { data: pubData } = usePublicationData(); 25 let record = pubData?.record as PubLeafletPublication.Record; 26 let [formState, setFormState] = useState<"normal" | "loading">("normal"); 27 ··· 180 }; 181 182 export function CustomDomainForm() { 183 - let { data: pubData } = usePublicationData(); 184 if (!pubData) return null; 185 let record = pubData?.record as PubLeafletPublication.Record; 186 let [state, setState] = useState< ··· 325 <div className="absolute right-0 top-0 bottom-0 flex justify-end items-center w-4 "> 326 {pending ? ( 327 <button 328 - className="group/pending px-1 py-0.5 flex gap-1 items-center rounded-full hover:bg-accent-1 hover:text-accent-2 hover:outline-accent-1 border-transparent outline outline-transparent selected-outline" 329 onClick={() => { 330 if (data?.error === "Verification_needed") { 331 props.setDomain(data.verification); ··· 356 }); 357 mutate("publication-data"); 358 }} 359 - className="group/domain flex gap-1 items-center rounded-full bg-none w-max font-bold px-1 py-0.5 hover:bg-accent-1 hover:text-accent-2 border-transparent outline outline-transparent hover:outline-accent-1 selected-outline" 360 > 361 <p className="group-hover/domain:block hidden w-max pl-1"> 362 set as default
··· 21 import { Checkbox } from "components/Checkbox"; 22 23 export const EditPubForm = () => { 24 + let { data } = usePublicationData(); 25 + let { publication: pubData } = data || {}; 26 let record = pubData?.record as PubLeafletPublication.Record; 27 let [formState, setFormState] = useState<"normal" | "loading">("normal"); 28 ··· 181 }; 182 183 export function CustomDomainForm() { 184 + let { data } = usePublicationData(); 185 + let { publication: pubData } = data || {}; 186 if (!pubData) return null; 187 let record = pubData?.record as PubLeafletPublication.Record; 188 let [state, setState] = useState< ··· 327 <div className="absolute right-0 top-0 bottom-0 flex justify-end items-center w-4 "> 328 {pending ? ( 329 <button 330 + className="group/pending px-1 py-0.5 flex gap-1 items-center rounded-full hover:bg-accent-1 hover:text-accent-2 hover:outline-accent-1 border-transparent outline-solid outline-transparent selected-outline" 331 onClick={() => { 332 if (data?.error === "Verification_needed") { 333 props.setDomain(data.verification); ··· 358 }); 359 mutate("publication-data"); 360 }} 361 + className="group/domain flex gap-1 items-center rounded-full bg-none w-max font-bold px-1 py-0.5 hover:bg-accent-1 hover:text-accent-2 border-transparent outline-solid outline-transparent hover:outline-accent-1 selected-outline" 362 > 363 <p className="group-hover/domain:block hidden w-max pl-1"> 364 set as default
+1 -1
app/login/LoginForm.tsx
··· 153 154 <ButtonPrimary 155 type="submit" 156 - className="place-self-end !px-[2px] absolute right-1 bottom-1" 157 > 158 <ArrowRightTiny />{" "} 159 </ButtonPrimary>
··· 153 154 <ButtonPrimary 155 type="submit" 156 + className="place-self-end px-[2px]! absolute right-1 bottom-1" 157 > 158 <ArrowRightTiny />{" "} 159 </ButtonPrimary>
+2 -2
app/templates/TemplateList.tsx
··· 43 target="_blank" 44 className="no-underline hover:no-underline" 45 > 46 - <ButtonPrimary className="bg-primary hover:!outline-none hover:scale-105 hover:rotate-3 transition-all"> 47 Preview 48 </ButtonPrimary> 49 </Link> 50 <ButtonPrimary 51 - className=" hover:!outline-none hover:scale-105 hover:-rotate-2 transition-all" 52 onClick={async () => { 53 let id = await createNewLeafletFromTemplate( 54 props.templateID,
··· 43 target="_blank" 44 className="no-underline hover:no-underline" 45 > 46 + <ButtonPrimary className="bg-primary hover:outline-hidden! hover:scale-105 hover:rotate-3 transition-all"> 47 Preview 48 </ButtonPrimary> 49 </Link> 50 <ButtonPrimary 51 + className=" hover:outline-hidden! hover:scale-105 hover:-rotate-2 transition-all" 52 onClick={async () => { 53 let id = await createNewLeafletFromTemplate( 54 props.templateID,
+1 -1
app/templates/page.tsx
··· 11 export default function Templates() { 12 return ( 13 <div className="flex h-full bg-bg-leaflet"> 14 - <div className="home relative max-w-screen-lg w-full h-full mx-auto flex sm:flex-row flex-col-reverse px-4 sm:px-6 "> 15 <div className="homeOptions z-10 shrink-0 sm:static absolute bottom-0 place-self-end sm:place-self-start flex sm:flex-col flex-row-reverse gap-2 sm:w-fit w-full items-center pb-2 pt-1 sm:pt-7"> 16 {/* NOT using <HomeButton /> b/c it does a permission check we don't need */} 17 <Link href="/home">
··· 11 export default function Templates() { 12 return ( 13 <div className="flex h-full bg-bg-leaflet"> 14 + <div className="home relative max-w-(--breakpoint-lg) w-full h-full mx-auto flex sm:flex-row flex-col-reverse px-4 sm:px-6 "> 15 <div className="homeOptions z-10 shrink-0 sm:static absolute bottom-0 place-self-end sm:place-self-start flex sm:flex-col flex-row-reverse gap-2 sm:w-fit w-full items-center pb-2 pt-1 sm:pt-7"> 16 {/* NOT using <HomeButton /> b/c it does a permission check we don't need */} 17 <Link href="/home">
+190 -221
appview/index.ts
··· 31 ); 32 const QUOTE_PARAM = "/l-quote/"; 33 async function main() { 34 - let startCursor; 35 - let client = new Client({ connectionString: process.env.DB_URL }); 36 - let db = drizzle(client); 37 - try { 38 - let file = (await readFile(cursorFile)).toString(); 39 - console.log("START CURSOR: " + file); 40 - startCursor = parseInt(file); 41 - if (Number.isNaN(startCursor)) startCursor = undefined; 42 - } catch (e) {} 43 - 44 - async function handleEvent(evt: Event) { 45 - if (evt.event === "identity") { 46 - if (evt.handle) 47 - await supabase 48 - .from("bsky_profiles") 49 - .update({ handle: evt.handle }) 50 - .eq("did", evt.did); 51 - } 52 - if ( 53 - evt.event == "account" || 54 - evt.event === "identity" || 55 - evt.event === "sync" 56 - ) 57 - return; 58 - if (evt.collection !== "app.bsky.feed.post") 59 - console.log(`${evt.event} in ${evt.collection}`); 60 - if (evt.collection === ids.PubLeafletDocument) { 61 - if (evt.event === "create" || evt.event === "update") { 62 - let record = PubLeafletDocument.validateRecord(evt.record); 63 - if (!record.success) { 64 - return; 65 - } 66 - await supabase.from("documents").upsert({ 67 - uri: evt.uri.toString(), 68 - data: record.value as Json, 69 - }); 70 - let publicationURI = new AtUri(record.value.publication); 71 - 72 - if (publicationURI.host !== evt.uri.host) { 73 - console.log("Unauthorized to create post!"); 74 - return; 75 - } 76 - await supabase.from("documents_in_publications").insert({ 77 - publication: record.value.publication, 78 - document: evt.uri.toString(), 79 - }); 80 - } 81 - if (evt.event === "delete") { 82 - await supabase.from("documents").delete().eq("uri", evt.uri.toString()); 83 - } 84 - } 85 - if (evt.collection === ids.PubLeafletPublication) { 86 - if (evt.event === "create" || evt.event === "update") { 87 - let record = PubLeafletPublication.validateRecord(evt.record); 88 - if (!record.success) return; 89 - let { error } = await supabase.from("publications").upsert({ 90 - uri: evt.uri.toString(), 91 - identity_did: evt.did, 92 - name: record.value.name, 93 - record: record.value as Json, 94 - }); 95 - 96 - if (error && error.code === "23503") { 97 - console.log("creating identity"); 98 - await createIdentity(db, { atp_did: evt.did }); 99 - await supabase.from("publications").upsert({ 100 - uri: evt.uri.toString(), 101 - identity_did: evt.did, 102 - name: record.value.name, 103 - record: record.value as Json, 104 - }); 105 - } 106 - } 107 - if (evt.event === "delete") { 108 - await supabase 109 - .from("publications") 110 - .delete() 111 - .eq("uri", evt.uri.toString()); 112 - } 113 - } 114 - if (evt.collection === ids.PubLeafletComment) { 115 - if (evt.event === "create" || evt.event === "update") { 116 - let record = PubLeafletComment.validateRecord(evt.record); 117 - if (!record.success) return; 118 - let { error } = await supabase.from("comments_on_documents").upsert({ 119 - uri: evt.uri.toString(), 120 - profile: evt.did, 121 - document: record.value.subject, 122 - record: record.value as Json, 123 - }); 124 - } 125 - if (evt.event === "delete") { 126 - await supabase 127 - .from("comments_on_documents") 128 - .delete() 129 - .eq("uri", evt.uri.toString()); 130 - } 131 - } 132 - if (evt.collection === ids.PubLeafletGraphSubscription) { 133 - if (evt.event === "create" || evt.event === "update") { 134 - let record = PubLeafletGraphSubscription.validateRecord(evt.record); 135 - if (!record.success) return; 136 - let { error } = await supabase 137 - .from("publication_subscriptions") 138 - .upsert({ 139 - uri: evt.uri.toString(), 140 - identity: evt.did, 141 - publication: record.value.publication, 142 - record: record.value as Json, 143 - }); 144 - if (error && error.code === "23503") { 145 - console.log("creating identity"); 146 - await createIdentity(db, { atp_did: evt.did }); 147 - await supabase.from("publication_subscriptions").upsert({ 148 - uri: evt.uri.toString(), 149 - identity: evt.did, 150 - publication: record.value.publication, 151 - record: record.value as Json, 152 - }); 153 - } 154 - } 155 - if (evt.event === "delete") { 156 - await supabase 157 - .from("publication_subscriptions") 158 - .delete() 159 - .eq("uri", evt.uri.toString()); 160 - } 161 - } 162 - // if (evt.collection === ids.AppBskyActorProfile) { 163 - // //only listen to updates because we should fetch it for the first time when they subscribe! 164 - // if (evt.event === "update") { 165 - // await supabaseServerClient 166 - // .from("bsky_profiles") 167 - // .update({ record: evt.record as Json }) 168 - // .eq("did", evt.did); 169 - // } 170 - // } 171 - if (evt.collection === "app.bsky.feed.post") { 172 - if (evt.event !== "create") return; 173 - 174 - // Early exit if no embed 175 - if ( 176 - !evt.record || 177 - typeof evt.record !== "object" || 178 - !("embed" in evt.record) 179 - ) 180 - return; 181 - 182 - // Quick check if embed might contain our quote param 183 - const embedStr = JSON.stringify(evt.record.embed); 184 - if (!embedStr.includes(QUOTE_PARAM)) return; 185 - 186 - // Now validate the record since we know it might be relevant 187 - let record = AppBskyFeedPost.validateRecord(evt.record); 188 - if (!record.success) return; 189 - 190 - let embed: string | null = null; 191 - if ( 192 - AppBskyEmbedExternal.isMain(record.value.embed) && 193 - record.value.embed.external.uri.includes(QUOTE_PARAM) 194 - ) { 195 - embed = record.value.embed.external.uri; 196 - } 197 - if ( 198 - AppBskyEmbedRecordWithMedia.isMain(record.value.embed) && 199 - AppBskyEmbedExternal.isMain(record.value.embed.media) && 200 - record.value.embed.media?.external?.uri.includes(QUOTE_PARAM) 201 - ) { 202 - embed = record.value.embed.media.external.uri; 203 - } 204 - if (embed) { 205 - console.log( 206 - "processing post mention: " + embed + " in " + evt.uri.toString(), 207 - ); 208 - await inngest.send({ 209 - name: "appview/index-bsky-post-mention", 210 - data: { post_uri: evt.uri.toString(), document_link: embed }, 211 - }); 212 - } 213 - } 214 - } 215 - async function timedHandleEvent(evt: Event) { 216 - const startTime = performance.now(); 217 - 218 - if (evt.event === "identity") { 219 - if (evt.handle) 220 - await supabase 221 - .from("bsky_profiles") 222 - .update({ handle: evt.handle }) 223 - .eq("did", evt.did); 224 - } 225 - 226 - await handleEvent(evt); 227 - if ( 228 - evt.event == "account" || 229 - evt.event === "identity" || 230 - evt.event === "sync" 231 - ) { 232 - const endTime = performance.now(); 233 - console.log( 234 - `${evt.event} in ${evt.event || "unknown"} took ${endTime - startTime}ms`, 235 - ); 236 - return; 237 - } 238 - 239 - const endTime = performance.now(); 240 - console.log( 241 - `${evt.event} in ${evt.collection || "unknown"} took ${endTime - startTime}ms`, 242 - ); 243 - } 244 - 245 - const runner = new MemoryRunner({ 246 - concurrency: 4, 247 - startCursor, 248 - setCursor: async (cursor) => { 249 - console.log(cursor); 250 - // persist cursor 251 - await writeFile(cursorFile, cursor.toString()); 252 - }, 253 - }); 254 let firehose = new Firehose({ 255 service: "wss://relay1.us-west.bsky.network", 256 subscriptionReconnectDelay: 3000, ··· 278 if (cleaningUp) return; 279 cleaningUp = true; 280 console.log("shutting down firehose..."); 281 - client.end(); 282 await firehose.destroy(); 283 await runner.destroy(); 284 process.exit(); ··· 289 } 290 291 main();
··· 31 ); 32 const QUOTE_PARAM = "/l-quote/"; 33 async function main() { 34 + const runner = new MemoryRunner({}); 35 let firehose = new Firehose({ 36 service: "wss://relay1.us-west.bsky.network", 37 subscriptionReconnectDelay: 3000, ··· 59 if (cleaningUp) return; 60 cleaningUp = true; 61 console.log("shutting down firehose..."); 62 await firehose.destroy(); 63 await runner.destroy(); 64 process.exit(); ··· 69 } 70 71 main(); 72 + 73 + async function handleEvent(evt: Event) { 74 + if (evt.event === "identity") { 75 + if (evt.handle) 76 + await supabase 77 + .from("bsky_profiles") 78 + .update({ handle: evt.handle }) 79 + .eq("did", evt.did); 80 + } 81 + if ( 82 + evt.event == "account" || 83 + evt.event === "identity" || 84 + evt.event === "sync" 85 + ) 86 + return; 87 + if (evt.collection !== "app.bsky.feed.post") 88 + console.log( 89 + `${evt.event} in ${evt.collection} ${evt.uri}: ${evt.seq} ${evt.time}`, 90 + ); 91 + if (evt.collection === ids.PubLeafletDocument) { 92 + if (evt.event === "create" || evt.event === "update") { 93 + let record = PubLeafletDocument.validateRecord(evt.record); 94 + if (!record.success) { 95 + console.log(record.error); 96 + return; 97 + } 98 + let docResult = await supabase.from("documents").upsert({ 99 + uri: evt.uri.toString(), 100 + data: record.value as Json, 101 + }); 102 + if (docResult.error) console.log(docResult.error); 103 + let publicationURI = new AtUri(record.value.publication); 104 + 105 + if (publicationURI.host !== evt.uri.host) { 106 + console.log("Unauthorized to create post!"); 107 + return; 108 + } 109 + let docInPublicationResult = await supabase 110 + .from("documents_in_publications") 111 + .upsert({ 112 + publication: record.value.publication, 113 + document: evt.uri.toString(), 114 + }); 115 + if (docInPublicationResult.error) 116 + console.log(docInPublicationResult.error); 117 + } 118 + if (evt.event === "delete") { 119 + await supabase.from("documents").delete().eq("uri", evt.uri.toString()); 120 + } 121 + } 122 + if (evt.collection === ids.PubLeafletPublication) { 123 + if (evt.event === "create" || evt.event === "update") { 124 + let record = PubLeafletPublication.validateRecord(evt.record); 125 + if (!record.success) return; 126 + let { error } = await supabase.from("publications").upsert({ 127 + uri: evt.uri.toString(), 128 + identity_did: evt.did, 129 + name: record.value.name, 130 + record: record.value as Json, 131 + }); 132 + 133 + if (error && error.code === "23503") { 134 + console.log("creating identity"); 135 + let client = new Client({ connectionString: process.env.DB_URL }); 136 + let db = drizzle(client); 137 + await createIdentity(db, { atp_did: evt.did }); 138 + client.end(); 139 + await supabase.from("publications").upsert({ 140 + uri: evt.uri.toString(), 141 + identity_did: evt.did, 142 + name: record.value.name, 143 + record: record.value as Json, 144 + }); 145 + } 146 + } 147 + if (evt.event === "delete") { 148 + await supabase 149 + .from("publications") 150 + .delete() 151 + .eq("uri", evt.uri.toString()); 152 + } 153 + } 154 + if (evt.collection === ids.PubLeafletComment) { 155 + if (evt.event === "create" || evt.event === "update") { 156 + let record = PubLeafletComment.validateRecord(evt.record); 157 + if (!record.success) return; 158 + let { error } = await supabase.from("comments_on_documents").upsert({ 159 + uri: evt.uri.toString(), 160 + profile: evt.did, 161 + document: record.value.subject, 162 + record: record.value as Json, 163 + }); 164 + } 165 + if (evt.event === "delete") { 166 + await supabase 167 + .from("comments_on_documents") 168 + .delete() 169 + .eq("uri", evt.uri.toString()); 170 + } 171 + } 172 + if (evt.collection === ids.PubLeafletGraphSubscription) { 173 + if (evt.event === "create" || evt.event === "update") { 174 + let record = PubLeafletGraphSubscription.validateRecord(evt.record); 175 + if (!record.success) return; 176 + let { error } = await supabase.from("publication_subscriptions").upsert({ 177 + uri: evt.uri.toString(), 178 + identity: evt.did, 179 + publication: record.value.publication, 180 + record: record.value as Json, 181 + }); 182 + if (error && error.code === "23503") { 183 + console.log("creating identity"); 184 + let client = new Client({ connectionString: process.env.DB_URL }); 185 + let db = drizzle(client); 186 + await createIdentity(db, { atp_did: evt.did }); 187 + client.end(); 188 + await supabase.from("publication_subscriptions").upsert({ 189 + uri: evt.uri.toString(), 190 + identity: evt.did, 191 + publication: record.value.publication, 192 + record: record.value as Json, 193 + }); 194 + } 195 + } 196 + if (evt.event === "delete") { 197 + await supabase 198 + .from("publication_subscriptions") 199 + .delete() 200 + .eq("uri", evt.uri.toString()); 201 + } 202 + } 203 + // if (evt.collection === ids.AppBskyActorProfile) { 204 + // //only listen to updates because we should fetch it for the first time when they subscribe! 205 + // if (evt.event === "update") { 206 + // await supabaseServerClient 207 + // .from("bsky_profiles") 208 + // .update({ record: evt.record as Json }) 209 + // .eq("did", evt.did); 210 + // } 211 + // } 212 + if (evt.collection === "app.bsky.feed.post") { 213 + if (evt.event !== "create") return; 214 + 215 + // Early exit if no embed 216 + if ( 217 + !evt.record || 218 + typeof evt.record !== "object" || 219 + !("embed" in evt.record) 220 + ) 221 + return; 222 + 223 + // Check if embed contains our quote param using optional chaining 224 + const embedRecord = evt.record as any; 225 + const hasQuoteParam = 226 + embedRecord.embed?.external?.uri?.includes(QUOTE_PARAM) || 227 + embedRecord.embed?.media?.external?.uri?.includes(QUOTE_PARAM); 228 + 229 + if (!hasQuoteParam) return; 230 + console.log("FOUND EMBED!!!"); 231 + 232 + // Now validate the record since we know it contains our quote param 233 + let record = AppBskyFeedPost.validateRecord(evt.record); 234 + if (!record.success) return; 235 + 236 + let embed: string | null = null; 237 + if ( 238 + AppBskyEmbedExternal.isMain(record.value.embed) && 239 + record.value.embed.external.uri.includes(QUOTE_PARAM) 240 + ) { 241 + embed = record.value.embed.external.uri; 242 + } 243 + if ( 244 + AppBskyEmbedRecordWithMedia.isMain(record.value.embed) && 245 + AppBskyEmbedExternal.isMain(record.value.embed.media) && 246 + record.value.embed.media?.external?.uri.includes(QUOTE_PARAM) 247 + ) { 248 + embed = record.value.embed.media.external.uri; 249 + } 250 + if (embed) { 251 + console.log( 252 + "processing post mention: " + embed + " in " + evt.uri.toString(), 253 + ); 254 + await inngest.send({ 255 + name: "appview/index-bsky-post-mention", 256 + data: { post_uri: evt.uri.toString(), document_link: embed }, 257 + }); 258 + } 259 + } 260 + }
+25 -18
components/ActionBar/ActionButton.tsx
··· 1 "use client"; 2 import { useContext, useEffect } from "react"; 3 import { SidebarContext } from "./Sidebar"; 4 import React, { forwardRef, type JSX } from "react"; ··· 6 7 type ButtonProps = Omit<JSX.IntrinsicElements["button"], "content">; 8 9 - export const ActionButton = forwardRef< 10 - HTMLButtonElement, 11 - ButtonProps & { 12 id?: string; 13 icon: React.ReactNode; 14 label: React.ReactNode; 15 primary?: boolean; 16 secondary?: boolean; 17 - } 18 - >((props, ref) => { 19 - let { id, icon, label, primary, secondary, ...buttonProps } = props; 20 let sidebar = useContext(SidebarContext); 21 let inOpenPopover = useContext(PopoverOpenContext); 22 useEffect(() => { ··· 30 return ( 31 <button 32 {...buttonProps} 33 - ref={ref} 34 - id={props.id} 35 className={` 36 actionButton relative font-bold 37 rounded-md border 38 - flex gap-2 items-center sm:justify-start justify-center 39 p-1 sm:mx-0 40 ${ 41 - props.primary 42 ? "w-full bg-accent-1 border-accent-1 text-accent-2 transparent-outline sm:hover:outline-accent-contrast focus:outline-accent-1 outline-offset-1 mx-1 first:ml-0" 43 - : props.secondary 44 ? "sm:w-full w-max bg-bg-page border-accent-contrast text-accent-contrast transparent-outline focus:outline-accent-contrast sm:hover:outline-accent-contrast outline-offset-1 mx-1 first:ml-0" 45 - : "sm:w-full w-max border-transparent text-accent-contrast sm:hover:border-accent-contrast" 46 } 47 `} 48 > 49 - <div className="shrink-0">{props.icon}</div> 50 <div 51 - className={`pr-1 w-max ${sidebar.open ? "block" : props.primary || props.secondary ? "sm:hidden block" : "hidden"}`} 52 > 53 - {props.label} 54 </div> 55 </button> 56 ); 57 - }); 58 - 59 - ActionButton.displayName = "ActionButton";
··· 1 "use client"; 2 + 3 import { useContext, useEffect } from "react"; 4 import { SidebarContext } from "./Sidebar"; 5 import React, { forwardRef, type JSX } from "react"; ··· 7 8 type ButtonProps = Omit<JSX.IntrinsicElements["button"], "content">; 9 10 + export const ActionButton = ( 11 + props: ButtonProps & { 12 id?: string; 13 icon: React.ReactNode; 14 label: React.ReactNode; 15 primary?: boolean; 16 secondary?: boolean; 17 + nav?: boolean; 18 + className?: string; 19 + subtext?: string; 20 + }, 21 + ) => { 22 + let { id, icon, label, primary, secondary, nav, ...buttonProps } = props; 23 let sidebar = useContext(SidebarContext); 24 let inOpenPopover = useContext(PopoverOpenContext); 25 useEffect(() => { ··· 33 return ( 34 <button 35 {...buttonProps} 36 className={` 37 actionButton relative font-bold 38 rounded-md border 39 + flex gap-2 items-start sm:justify-start justify-center 40 p-1 sm:mx-0 41 ${ 42 + primary 43 ? "w-full bg-accent-1 border-accent-1 text-accent-2 transparent-outline sm:hover:outline-accent-contrast focus:outline-accent-1 outline-offset-1 mx-1 first:ml-0" 44 + : secondary 45 ? "sm:w-full w-max bg-bg-page border-accent-contrast text-accent-contrast transparent-outline focus:outline-accent-contrast sm:hover:outline-accent-contrast outline-offset-1 mx-1 first:ml-0" 46 + : nav 47 + ? "w-full border-transparent text-secondary sm:hover:border-border justify-start!" 48 + : "sm:w-full border-transparent text-accent-contrast sm:hover:border-accent-contrast" 49 } 50 + ${props.className} 51 `} 52 > 53 + <div className="shrink-0">{icon}</div> 54 <div 55 + className={`flex flex-col pr-1 leading-snug max-w-full min-w-0 ${sidebar.open ? "block" : primary || secondary || nav ? "sm:hidden block" : "hidden"}`} 56 > 57 + <div className="truncate text-left pt-[1px]">{label}</div> 58 + {props.subtext && ( 59 + <div className="text-xs text-tertiary font-normal text-left"> 60 + {props.subtext} 61 + </div> 62 + )} 63 </div> 64 </button> 65 ); 66 + };
+138
components/ActionBar/Navigation.tsx
···
··· 1 + import { HomeSmall } from "components/Icons/HomeSmall"; 2 + import { ActionButton } from "./ActionButton"; 3 + import { Sidebar } from "./Sidebar"; 4 + import { useIdentityData } from "components/IdentityProvider"; 5 + import Link from "next/link"; 6 + import { DiscoverSmall } from "components/Icons/DiscoverSmall"; 7 + import { PublicationButtons } from "./Publications"; 8 + import { Popover } from "components/Popover"; 9 + import { MenuSmall } from "components/Icons/MenuSmall"; 10 + 11 + export type navPages = "home" | "reader" | "pub" | "discover"; 12 + 13 + export const DesktopNavigation = (props: { 14 + currentPage: navPages; 15 + publication?: string; 16 + }) => { 17 + let unreadNotifications = true; 18 + 19 + return ( 20 + <div className="flex flex-col gap-4"> 21 + <Sidebar alwaysOpen> 22 + <NavigationOptions 23 + currentPage={props.currentPage} 24 + publication={props.publication} 25 + /> 26 + </Sidebar> 27 + {/*<Sidebar alwaysOpen> 28 + <ActionButton 29 + icon={ 30 + unreadNotifications ? ( 31 + <NotificationsUnreadSmall /> 32 + ) : ( 33 + <NotificationsReadSmall /> 34 + ) 35 + } 36 + label="Notifications" 37 + /> 38 + </Sidebar>*/} 39 + </div> 40 + ); 41 + }; 42 + 43 + export const MobileNavigation = (props: { 44 + currentPage: navPages; 45 + publication?: string; 46 + }) => { 47 + let { identity } = useIdentityData(); 48 + let thisPublication = identity?.publications?.find( 49 + (pub) => pub.uri === props.publication, 50 + ); 51 + return ( 52 + <Popover 53 + onOpenAutoFocus={(e) => e.preventDefault()} 54 + asChild 55 + className="px-2! !max-w-[256px]" 56 + trigger={ 57 + <div className="shrink-0 p-1 pr-2 text-accent-contrast h-full flex gap-2 font-bold items-center"> 58 + <MenuSmall /> 59 + <div className="truncate max-w-[72px]"> 60 + {props.currentPage === "home" ? ( 61 + <>Home</> 62 + ) : props.currentPage === "reader" ? ( 63 + <>Reader</> 64 + ) : props.currentPage === "discover" ? ( 65 + <>Discover</> 66 + ) : props.currentPage === "pub" ? ( 67 + thisPublication && <>{thisPublication.name}</> 68 + ) : null} 69 + </div> 70 + </div> 71 + } 72 + > 73 + <NavigationOptions 74 + currentPage={props.currentPage} 75 + publication={props.publication} 76 + /> 77 + </Popover> 78 + ); 79 + }; 80 + 81 + const NavigationOptions = (props: { 82 + currentPage: navPages; 83 + publication?: string; 84 + }) => { 85 + let { identity } = useIdentityData(); 86 + let thisPublication = identity?.publications?.find( 87 + (pub) => pub.uri === props.publication, 88 + ); 89 + return ( 90 + <> 91 + <HomeButton current={props.currentPage === "home"} /> 92 + <ReaderButton current={props.currentPage === "discover"} /> 93 + <hr className="border-border-light my-1" /> 94 + <PublicationButtons currentPubUri={thisPublication?.uri} /> 95 + </> 96 + ); 97 + }; 98 + 99 + const HomeButton = (props: { current?: boolean }) => { 100 + return ( 101 + <Link href={"/home"} className="hover:!no-underline"> 102 + <ActionButton 103 + nav 104 + icon={<HomeSmall />} 105 + label="Home" 106 + className={props.current ? "bg-bg-page! border-border-light!" : ""} 107 + /> 108 + </Link> 109 + ); 110 + }; 111 + 112 + const ReaderButton = (props: { current?: boolean }) => { 113 + // let readerUnreads = true; 114 + // let subs = false; 115 + 116 + // if (subs) 117 + // return ( 118 + // <ActionButton 119 + // nav 120 + // icon={readerUnreads ? <ReaderUnreadSmall /> : <ReaderReadSmall />} 121 + // label="Reader" 122 + // className={` 123 + // ${readerUnreads ? "text-accent-contrast! border-accent-contrast" : props.current ? "bg-border-light! border-border" : ""} 124 + // `} 125 + // /> 126 + // ); 127 + return ( 128 + <Link href={"/discover"} className="hover:no-underline!"> 129 + <ActionButton 130 + nav 131 + icon={<DiscoverSmall />} 132 + label="Discover" 133 + subtext={"Explore publications!"} 134 + className={props.current ? "bg-bg-page! border-border-light!" : ""} 135 + /> 136 + </Link> 137 + ); 138 + };
+3 -3
components/ActionBar/Sidebar.tsx
··· 27 <div 28 className={` 29 actionSidebar 30 - ${!props.alwaysOpen && "absolute top-0 left-0 z-10"} 31 - h-fit w-max p-[6px] 32 - flex flex-col gap-2 justify-start border 33 rounded-md bg-bg-page ${open && !props.alwaysOpen ? "border-border-light" : "container"} 34 ${props.className} 35 `}
··· 27 <div 28 className={` 29 actionSidebar 30 + ${!props.alwaysOpen ? "absolute top-0 left-0 z-10 w-max" : "w-[192px] max-w-[192px]"} 31 + h-fit p-[6px] 32 + flex flex-col gap-1 justify-start border 33 rounded-md bg-bg-page ${open && !props.alwaysOpen ? "border-border-light" : "container"} 34 ${props.className} 35 `}
+2 -2
components/Blocks/Block.tsx
··· 253 if (!BlockTypeComponent) return <div>unknown block</div>; 254 return ( 255 <div 256 - className={`blockContentWrapper w-full grow flex gap-2 z-[1] ${alignmentStyle}`} 257 > 258 {props.listData && <ListMarker {...props} />} 259 {props.areYouSure ? ( ··· 385 let { rep } = useReplicache(); 386 return ( 387 <div 388 - className={`shrink-0 flex justify-end items-center h-3 z-[1] 389 ${props.className} 390 ${ 391 props.type === "heading"
··· 253 if (!BlockTypeComponent) return <div>unknown block</div>; 254 return ( 255 <div 256 + className={`blockContentWrapper w-full grow flex gap-2 z-1 ${alignmentStyle}`} 257 > 258 {props.listData && <ListMarker {...props} />} 259 {props.areYouSure ? ( ··· 385 let { rep } = useReplicache(); 386 return ( 387 <div 388 + className={`shrink-0 flex justify-end items-center h-3 z-1 389 ${props.className} 390 ${ 391 props.type === "heading"
+1 -1
components/Blocks/BlockCommandBar.tsx
··· 124 `} 125 > 126 <NestedCardThemeProvider> 127 - <div className="commandMenuResults w-full max-h-[var(--radix-popover-content-available-height)] overflow-auto flex flex-col group-data-[side=top]/cmd-menu:flex-col-reverse bg-bg-page py-1 gap-0.5 border border-border rounded-md shadow-md"> 128 {commandResults.length === 0 ? ( 129 <div className="w-full text-tertiary text-center italic py-2 px-2 "> 130 No blocks found
··· 124 `} 125 > 126 <NestedCardThemeProvider> 127 + <div className="commandMenuResults w-full max-h-(--radix-popover-content-available-height) overflow-auto flex flex-col group-data-[side=top]/cmd-menu:flex-col-reverse bg-bg-page py-1 gap-0.5 border border-border rounded-md shadow-md"> 128 {commandResults.length === 0 ? ( 129 <div className="w-full text-tertiary text-center italic py-2 px-2 "> 130 No blocks found
+3 -3
components/Blocks/BlueskyPostBlock/BlueskyEmbed.tsx
··· 31 className={` 32 overflow-hidden w-full object-cover 33 ${imageEmbed.images.length === 1 && "h-auto max-h-[800px]"} 34 - ${imageEmbed.images.length === 2 && "basis-1/2 aspect-1/1"} 35 ${imageEmbed.images.length === 3 && "basis-1/3 aspect-2/3"} 36 ${ 37 imageEmbed.images.length === 4 38 - ? "basis-1/2 aspect-[3/2]" 39 : `basis-1/${imageEmbed.images.length} ` 40 } 41 `} ··· 198 <a 199 href={props.postUrl} 200 target="_blank" 201 - className={`block-border flex flex-col p-3 font-normal !rounded-md border text-tertiary italic text-center hover:no-underline hover:border-accent-contrast ${props.postUrl === undefined && "pointer-events-none"} `} 202 > 203 <div> This media is not supported... </div>{" "} 204 {props.postUrl === undefined ? null : (
··· 31 className={` 32 overflow-hidden w-full object-cover 33 ${imageEmbed.images.length === 1 && "h-auto max-h-[800px]"} 34 + ${imageEmbed.images.length === 2 && "basis-1/2 aspect-square"} 35 ${imageEmbed.images.length === 3 && "basis-1/3 aspect-2/3"} 36 ${ 37 imageEmbed.images.length === 4 38 + ? "basis-1/2 aspect-3/2" 39 : `basis-1/${imageEmbed.images.length} ` 40 } 41 `} ··· 198 <a 199 href={props.postUrl} 200 target="_blank" 201 + className={`block-border flex flex-col p-3 font-normal rounded-md! border text-tertiary italic text-center hover:no-underline hover:border-accent-contrast ${props.postUrl === undefined && "pointer-events-none"} `} 202 > 203 <div> This media is not supported... </div>{" "} 204 {props.postUrl === undefined ? null : (
+1 -1
components/Blocks/BlueskyPostBlock/BlueskyEmpty.tsx
··· 88 <Separator /> 89 <Input 90 type="text" 91 - className="w-full grow border-none outline-none bg-transparent " 92 placeholder="bsky.app/post-url" 93 value={urlValue} 94 disabled={isLocked}
··· 88 <Separator /> 89 <Input 90 type="text" 91 + className="w-full grow border-none outline-hidden bg-transparent " 92 placeholder="bsky.app/post-url" 93 value={urlValue} 94 disabled={isLocked}
+4 -4
components/Blocks/ButtonBlock.tsx
··· 34 <a 35 href={url?.data.value} 36 target="_blank" 37 - className={`hover:outline-accent-contrast !rounded-md ${isSelected ? "block-border-selected !border-0" : "block-border !border-transparent !border-0"}`} 38 > 39 <ButtonPrimary role="link" type="submit"> 40 {text?.data.value} ··· 117 w-full bg-bg-page 118 text-tertiary hover:text-accent-contrast hover:cursor-pointer hover:p-0 119 flex flex-col gap-2 items-center justify-center hover:border-2 border-dashed rounded-lg 120 - ${isSelected ? "border-2 border-tertiary p-0" : "border border-border p-[1px]"} 121 `} 122 onSubmit={(e) => { 123 e.preventDefault(); ··· 169 <Input 170 type="text" 171 autoFocus 172 - className="w-full grow border-none outline-none bg-transparent" 173 placeholder="button text" 174 value={textValue} 175 disabled={isLocked} ··· 192 <Input 193 type="text" 194 id="button-block-url-input" 195 - className="w-full grow border-none outline-none bg-transparent" 196 placeholder="www.example.com" 197 value={urlValue} 198 disabled={isLocked}
··· 34 <a 35 href={url?.data.value} 36 target="_blank" 37 + className={`hover:outline-accent-contrast rounded-md! ${isSelected ? "block-border-selected border-0!" : "block-border border-transparent! border-0!"}`} 38 > 39 <ButtonPrimary role="link" type="submit"> 40 {text?.data.value} ··· 117 w-full bg-bg-page 118 text-tertiary hover:text-accent-contrast hover:cursor-pointer hover:p-0 119 flex flex-col gap-2 items-center justify-center hover:border-2 border-dashed rounded-lg 120 + ${isSelected ? "border-2 border-tertiary p-0" : "border border-border p-px"} 121 `} 122 onSubmit={(e) => { 123 e.preventDefault(); ··· 169 <Input 170 type="text" 171 autoFocus 172 + className="w-full grow border-none outline-hidden bg-transparent" 173 placeholder="button text" 174 value={textValue} 175 disabled={isLocked} ··· 192 <Input 193 type="text" 194 id="button-block-url-input" 195 + className="w-full grow border-none outline-hidden bg-transparent" 196 placeholder="www.example.com" 197 value={urlValue} 198 disabled={isLocked}
+2 -2
components/Blocks/CodeBlock.tsx
··· 123 data-entityid={props.entityID} 124 id={elementId.block(props.entityID).input} 125 block={props} 126 - className="codeBlockEditor !whitespace-nowrap !overflow-auto font-mono p-2" 127 value={content?.data.value} 128 onChange={async (e) => { 129 // Update the entity with the new value ··· 138 <pre 139 onClick={onClick} 140 onMouseDown={(e) => e.stopPropagation()} 141 - className="codeBlockRendered !overflow-auto font-mono p-2 w-full h-full" 142 > 143 {content?.data.value} 144 </pre>
··· 123 data-entityid={props.entityID} 124 id={elementId.block(props.entityID).input} 125 block={props} 126 + className="codeBlockEditor whitespace-nowrap! overflow-auto! font-mono p-2" 127 value={content?.data.value} 128 onChange={async (e) => { 129 // Update the entity with the new value ··· 138 <pre 139 onClick={onClick} 140 onMouseDown={(e) => e.stopPropagation()} 141 + className="codeBlockRendered overflow-auto! font-mono p-2 w-full h-full" 142 > 143 {content?.data.value} 144 </pre>
+5 -5
components/Blocks/DateTimeBlock.tsx
··· 24 if (!isClient && !initialPageLoad) 25 return ( 26 <div 27 - className={`flex flex-row gap-2 group/date w-64 z-[1] border border-transparent`} 28 > 29 <BlockCalendarSmall className="text-tertiary" /> 30 </div> ··· 119 return ( 120 <Popover 121 disabled={isLocked || !permissions.write} 122 - className="w-64 z-10 !px-2" 123 trigger={ 124 <div 125 - className={`flex flex-row gap-2 group/date w-64 z-[1] 126 - ${isSelected ? "block-border-selected !border-transparent" : "border border-transparent"} 127 ${alignment === "center" ? "justify-center" : alignment === "right" ? "justify-end" : "justify-start"} 128 `} 129 > ··· 182 chevron: "text-inherit", 183 month_grid: "w-full table-fixed", 184 weekdays: "text-secondary text-sm", 185 - selected: "!bg-accent-1 text-accent-2 rounded-md font-bold", 186 187 day: "h-[34px] text-center rounded-md sm:hover:bg-border-light", 188 outside: "text-border",
··· 24 if (!isClient && !initialPageLoad) 25 return ( 26 <div 27 + className={`flex flex-row gap-2 group/date w-64 z-1 border border-transparent`} 28 > 29 <BlockCalendarSmall className="text-tertiary" /> 30 </div> ··· 119 return ( 120 <Popover 121 disabled={isLocked || !permissions.write} 122 + className="w-64 z-10 px-2!" 123 trigger={ 124 <div 125 + className={`flex flex-row gap-2 group/date w-64 z-1 126 + ${isSelected ? "block-border-selected border-transparent!" : "border border-transparent"} 127 ${alignment === "center" ? "justify-center" : alignment === "right" ? "justify-end" : "justify-start"} 128 `} 129 > ··· 182 chevron: "text-inherit", 183 month_grid: "w-full table-fixed", 184 weekdays: "text-secondary text-sm", 185 + selected: "bg-accent-1! text-accent-2 rounded-md font-bold", 186 187 day: "h-[34px] text-center rounded-md sm:hover:bg-border-light", 188 outside: "text-border",
+3 -2
components/Blocks/EmbedBlock.tsx
··· 74 </label> 75 ); 76 } 77 78 return ( 79 <div ··· 111 className={`resizeHandle 112 cursor-ns-resize shrink-0 z-10 w-6 h-[5px] 113 absolute bottom-2 right-1/2 translate-x-1/2 translate-y-[2px] 114 - rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,_inset_0_0_0_1px_white] 115 ${isCanvasBlock ? "hidden group-hover/canvas-block:block" : ""}`} 116 {...heightHandle.handlers} 117 /> ··· 202 <Separator /> 203 <Input 204 type="text" 205 - className="w-full grow border-none outline-none bg-transparent " 206 placeholder="www.example.com" 207 value={linkValue} 208 disabled={isLocked}
··· 74 </label> 75 ); 76 } 77 + if (props.preview) return null; 78 79 return ( 80 <div ··· 112 className={`resizeHandle 113 cursor-ns-resize shrink-0 z-10 w-6 h-[5px] 114 absolute bottom-2 right-1/2 translate-x-1/2 translate-y-[2px] 115 + rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,inset_0_0_0_1px_white] 116 ${isCanvasBlock ? "hidden group-hover/canvas-block:block" : ""}`} 117 {...heightHandle.handlers} 118 /> ··· 203 <Separator /> 204 <Input 205 type="text" 206 + className="w-full grow border-none outline-hidden bg-transparent " 207 placeholder="www.example.com" 208 value={linkValue} 209 disabled={isLocked}
+4 -4
components/Blocks/ExternalLinkBlock.tsx
··· 70 externalLinkBlock flex relative group/linkBlock 71 h-[104px] w-full bg-bg-page overflow-hidden text-primary hover:no-underline no-underline 72 hover:border-accent-contrast shadow-sm 73 - ${isSelected ? "block-border-selected !outline-accent-contrast !border-accent-contrast" : "block-border"} 74 75 `} 76 > 77 <div className="pt-2 pb-2 px-3 grow min-w-0"> 78 <div className="flex flex-col w-full min-w-0 h-full grow "> 79 <div 80 - className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-none resize-none align-top border h-[24px] line-clamp-1`} 81 style={{ 82 overflow: "hidden", 83 textOverflow: "ellipsis", ··· 88 </div> 89 90 <div 91 - className={`linkBlockDescription text-sm bg-transparent border-none outline-none resize-none align-top grow line-clamp-2`} 92 > 93 {description?.data.value} 94 </div> ··· 172 } 173 type="url" 174 disabled={isLocked} 175 - className="w-full grow border-none outline-none bg-transparent " 176 placeholder="www.example.com" 177 value={linkValue} 178 onChange={(e) => setLinkValue(e.target.value)}
··· 70 externalLinkBlock flex relative group/linkBlock 71 h-[104px] w-full bg-bg-page overflow-hidden text-primary hover:no-underline no-underline 72 hover:border-accent-contrast shadow-sm 73 + ${isSelected ? "block-border-selected outline-accent-contrast! border-accent-contrast!" : "block-border"} 74 75 `} 76 > 77 <div className="pt-2 pb-2 px-3 grow min-w-0"> 78 <div className="flex flex-col w-full min-w-0 h-full grow "> 79 <div 80 + className={`linkBlockTitle bg-transparent -mb-0.5 border-none text-base font-bold outline-hidden resize-none align-top border h-[24px] line-clamp-1`} 81 style={{ 82 overflow: "hidden", 83 textOverflow: "ellipsis", ··· 88 </div> 89 90 <div 91 + className={`linkBlockDescription text-sm bg-transparent border-none outline-hidden resize-none align-top grow line-clamp-2`} 92 > 93 {description?.data.value} 94 </div> ··· 172 } 173 type="url" 174 disabled={isLocked} 175 + className="w-full grow border-none outline-hidden bg-transparent " 176 placeholder="www.example.com" 177 value={linkValue} 178 onChange={(e) => setLinkValue(e.target.value)}
+1 -1
components/Blocks/HorizontalRule.tsx
··· 8 return ( 9 <hr 10 className={`my-2 w-full border-border-light 11 - ${isSelected ? "block-border-selected !outline-offset-[3px]" : ""} 12 `} 13 /> 14 );
··· 8 return ( 9 <hr 10 className={`my-2 w-full border-border-light 11 + ${isSelected ? "block-border-selected outline-offset-[3px]!" : ""} 12 `} 13 /> 14 );
+4 -4
components/Blocks/ImageBlock.tsx
··· 114 let className = isFullBleed 115 ? "" 116 : isSelected 117 - ? "block-border-selected !border-transparent " 118 - : "block-border !border-transparent"; 119 120 let isLocalUpload = localImages.get(image.data.src); 121 ··· 156 export const FullBleedSelectionIndicator = () => { 157 return ( 158 <div 159 - className={`absolute top-3 sm:top-4 bottom-3 sm:bottom-4 left-3 sm:left-4 right-3 sm:right-4 border-2 border-bg-page rounded-lg outline-offset-1 outline outline-2 outline-tertiary`} 160 /> 161 ); 162 }; ··· 194 > 195 {entity_set.permissions.write ? ( 196 <AsyncValueAutosizeTextarea 197 - className="text-sm text-secondary outline-none bg-transparent min-w-0" 198 value={altText} 199 onFocus={(e) => { 200 e.currentTarget.setSelectionRange(
··· 114 let className = isFullBleed 115 ? "" 116 : isSelected 117 + ? "block-border-selected border-transparent! " 118 + : "block-border border-transparent!"; 119 120 let isLocalUpload = localImages.get(image.data.src); 121 ··· 156 export const FullBleedSelectionIndicator = () => { 157 return ( 158 <div 159 + className={`absolute top-3 sm:top-4 bottom-3 sm:bottom-4 left-3 sm:left-4 right-3 sm:right-4 border-2 border-bg-page rounded-lg outline-offset-1 outline-solid outline-2 outline-tertiary`} 160 /> 161 ); 162 }; ··· 194 > 195 {entity_set.permissions.write ? ( 196 <AsyncValueAutosizeTextarea 197 + className="text-sm text-secondary outline-hidden bg-transparent min-w-0" 198 value={altText} 199 onFocus={(e) => { 200 e.currentTarget.setSelectionRange(
+2 -2
components/Blocks/MailboxBlock.tsx
··· 316 <input 317 type="number" 318 value={code} 319 - className="appearance-none focus:outline-none focus:border-border w-20 border border-border-light bg-bg-page rounded-md p-1" 320 onChange={(e) => setCode(e.currentTarget.value)} 321 /> 322 ··· 358 value={email} 359 type="email" 360 onChange={(e) => setEmail(e.target.value)} 361 - className="w-full appearance-none focus:outline-none bg-transparent" 362 placeholder="youremail@email.com" 363 /> 364 </div>
··· 316 <input 317 type="number" 318 value={code} 319 + className="appearance-none focus:outline-hidden focus:border-border w-20 border border-border-light bg-bg-page rounded-md p-1" 320 onChange={(e) => setCode(e.currentTarget.value)} 321 /> 322 ··· 358 value={email} 359 type="email" 360 onChange={(e) => setEmail(e.target.value)} 361 + className="w-full appearance-none focus:outline-hidden bg-transparent" 362 placeholder="youremail@email.com" 363 /> 364 </div>
+1 -1
components/Blocks/MathBlock.tsx
··· 35 <BaseTextareaBlock 36 id={elementId.block(props.entityID).input} 37 block={props} 38 - className="bg-border-light rounded-md p-2 w-full min-h-[48px] whitespace-nowrap !overflow-auto border-border-light outline-border-light selected-outline" 39 placeholder="write some Tex here..." 40 value={content?.data.value} 41 onChange={async (e) => {
··· 35 <BaseTextareaBlock 36 id={elementId.block(props.entityID).input} 37 block={props} 38 + className="bg-border-light rounded-md p-2 w-full min-h-[48px] whitespace-nowrap overflow-auto! border-border-light outline-border-light selected-outline" 39 placeholder="write some Tex here..." 40 value={content?.data.value} 41 onChange={async (e) => {
+8 -8
components/Blocks/PageLinkBlock.tsx
··· 35 bg-bg-page shadow-sm 36 flex overflow-clip 37 ${isSelected ? "block-border-selected " : "block-border"} 38 - ${isOpen && "!border-tertiary"} 39 `} 40 onClick={(e) => { 41 if (!page) return; ··· 84 <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip "> 85 {leafletMetadata[0] && ( 86 <div 87 - className={`pageBlockOne outline-none resize-none align-top flex gap-2 ${leafletMetadata[0].type === "heading" ? "font-bold text-base" : ""}`} 88 > 89 {leafletMetadata[0].listData && ( 90 <ListMarker 91 {...leafletMetadata[0]} 92 className={ 93 leafletMetadata[0].type === "heading" 94 - ? "!pt-[12px]" 95 - : "!pt-[8px]" 96 } 97 /> 98 )} ··· 104 )} 105 {leafletMetadata[1] && ( 106 <div 107 - className={`pageBlockLineTwo outline-none resize-none align-top flex gap-2 ${leafletMetadata[1].type === "heading" ? "font-bold" : ""}`} 108 > 109 {leafletMetadata[1].listData && ( 110 - <ListMarker {...leafletMetadata[1]} className="!pt-[8px]" /> 111 )} 112 <RenderedTextBlock 113 entityID={leafletMetadata[1].value} ··· 117 )} 118 {leafletMetadata[2] && ( 119 <div 120 - className={`pageBlockLineThree outline-none resize-none align-top flex gap-2 ${leafletMetadata[2].type === "heading" ? "font-bold" : ""}`} 121 > 122 {leafletMetadata[2].listData && ( 123 - <ListMarker {...leafletMetadata[2]} className="!pt-[8px]" /> 124 )} 125 <RenderedTextBlock 126 entityID={leafletMetadata[2].value}
··· 35 bg-bg-page shadow-sm 36 flex overflow-clip 37 ${isSelected ? "block-border-selected " : "block-border"} 38 + ${isOpen && "border-tertiary!"} 39 `} 40 onClick={(e) => { 41 if (!page) return; ··· 84 <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip "> 85 {leafletMetadata[0] && ( 86 <div 87 + className={`pageBlockOne outline-hidden resize-none align-top flex gap-2 ${leafletMetadata[0].type === "heading" ? "font-bold text-base" : ""}`} 88 > 89 {leafletMetadata[0].listData && ( 90 <ListMarker 91 {...leafletMetadata[0]} 92 className={ 93 leafletMetadata[0].type === "heading" 94 + ? "pt-[12px]!" 95 + : "pt-[8px]!" 96 } 97 /> 98 )} ··· 104 )} 105 {leafletMetadata[1] && ( 106 <div 107 + className={`pageBlockLineTwo outline-hidden resize-none align-top flex gap-2 ${leafletMetadata[1].type === "heading" ? "font-bold" : ""}`} 108 > 109 {leafletMetadata[1].listData && ( 110 + <ListMarker {...leafletMetadata[1]} className="pt-[8px]!" /> 111 )} 112 <RenderedTextBlock 113 entityID={leafletMetadata[1].value} ··· 117 )} 118 {leafletMetadata[2] && ( 119 <div 120 + className={`pageBlockLineThree outline-hidden resize-none align-top flex gap-2 ${leafletMetadata[2].type === "heading" ? "font-bold" : ""}`} 121 > 122 {leafletMetadata[2].listData && ( 123 + <ListMarker {...leafletMetadata[2]} className="pt-[8px]!" /> 124 )} 125 <RenderedTextBlock 126 entityID={leafletMetadata[2].value}
+2 -2
components/Blocks/QuoteEmbedBlock.tsx
··· 39 </div> 40 <hr className="border-border-light" /> 41 <a 42 - className="quoteEmbedFooter flex max-w-full gap-2 px-3 py-2 hover:!no-underline text-secondary" 43 href="#" 44 > 45 <div className="flex flex-col w-[calc(100%-28px)] grow"> ··· 52 <div>celine</div> 53 </div> 54 </div> 55 - <div className=" shrink-0 pt-[1px] bg-test w-5 h-5 rounded-full"></div> 56 </a> 57 </div> 58 );
··· 39 </div> 40 <hr className="border-border-light" /> 41 <a 42 + className="quoteEmbedFooter flex max-w-full gap-2 px-3 py-2 hover:no-underline! text-secondary" 43 href="#" 44 > 45 <div className="flex flex-col w-[calc(100%-28px)] grow"> ··· 52 <div>celine</div> 53 </div> 54 </div> 55 + <div className=" shrink-0 pt-px bg-test w-5 h-5 rounded-full"></div> 56 </a> 57 </div> 58 );
+3 -3
components/Blocks/RSVPBlock/ContactDetailsForm.tsx
··· 25 }) { 26 let { status, entityID, setState, setStatus } = props; 27 let focusWithinStyles = 28 - "focus-within:border-tertiary focus-within:outline focus-within:outline-2 focus-within:outline-tertiary focus-within:outline-offset-1"; 29 let toaster = useToaster(); 30 let { data, mutate } = useRSVPData(); 31 let [contactFormState, setContactFormState] = useState< ··· 212 </div> 213 <div className="flex flex-row gap-2 w-full sm:w-32 h-fit"> 214 <InputWithLabel 215 - className="!appearance-none" 216 placeholder="0" 217 label="Plus ones?" 218 type="number" ··· 330 <Input 331 autoFocus 332 placeholder="000000" 333 - className="input-with-border !pt-5 w-full " 334 value={props.value} 335 autoComplete="one-time-code" 336 onChange={(e) => props.onChange(e.target.value)}
··· 25 }) { 26 let { status, entityID, setState, setStatus } = props; 27 let focusWithinStyles = 28 + "focus-within:border-tertiary focus-within:outline-solid focus-within:outline-2 focus-within:outline-tertiary focus-within:outline-offset-1"; 29 let toaster = useToaster(); 30 let { data, mutate } = useRSVPData(); 31 let [contactFormState, setContactFormState] = useState< ··· 212 </div> 213 <div className="flex flex-row gap-2 w-full sm:w-32 h-fit"> 214 <InputWithLabel 215 + className="appearance-none!" 216 placeholder="0" 217 label="Plus ones?" 218 type="number" ··· 330 <Input 331 autoFocus 332 placeholder="000000" 333 + className="input-with-border pt-5! w-full " 334 value={props.value} 335 autoComplete="one-time-code" 336 onChange={(e) => props.onChange(e.target.value)}
+3 -3
components/Blocks/RSVPBlock/SendUpdate.tsx
··· 134 </small> */} 135 <div className="flex gap-4 text-secondary"> 136 <Checkbox 137 - className="!w-fit" 138 checked={props.checked.GOING} 139 onChange={() => { 140 props.setChecked({ ··· 146 Going 147 </Checkbox> 148 <Checkbox 149 - className="!w-fit" 150 checked={props.checked.MAYBE} 151 onChange={() => { 152 props.setChecked({ ··· 158 Maybe 159 </Checkbox> 160 <Checkbox 161 - className="!w-fit" 162 checked={props.checked.NOT_GOING} 163 onChange={() => { 164 props.setChecked({
··· 134 </small> */} 135 <div className="flex gap-4 text-secondary"> 136 <Checkbox 137 + className="w-fit!" 138 checked={props.checked.GOING} 139 onChange={() => { 140 props.setChecked({ ··· 146 Going 147 </Checkbox> 148 <Checkbox 149 + className="w-fit!" 150 checked={props.checked.MAYBE} 151 onChange={() => { 152 props.setChecked({ ··· 158 Maybe 159 </Checkbox> 160 <Checkbox 161 + className="w-fit!" 162 checked={props.checked.NOT_GOING} 163 onChange={() => { 164 props.setChecked({
+6 -6
components/Blocks/RSVPBlock/index.tsx
··· 119 return ( 120 <div className="relative w-full sm:p-6 py-4 px-3 rounded-md border-[1.5px] border-accent-1"> 121 <RSVPBackground /> 122 - <div className="relative flex flex-row gap-2 items-center mx-auto z-[1] w-fit"> 123 <ButtonSecondary 124 type="button" 125 className={ 126 props.status === "MAYBE" 127 - ? "!text-accent-2 !bg-accent-1 text-lg" 128 : "" 129 } 130 onClick={() => props.setStatus("MAYBE")} ··· 135 type="button" 136 className={ 137 props.status === "GOING" 138 - ? "!text-accent-2 !bg-accent-1 text-lg" 139 : props.status === undefined 140 ? "text-lg" 141 : "" ··· 149 type="button" 150 className={ 151 props.status === "NOT_GOING" 152 - ? "!text-accent-2 !bg-accent-1 text-lg" 153 : "" 154 } 155 onClick={() => props.setStatus("NOT_GOING")} ··· 207 className={`relative w-full p-4 pb-5 rounded-md border-[1.5px] border-accent-1 font-bold items-center`} 208 > 209 <RSVPBackground /> 210 - <div className=" relative flex flex-col gap-1 sm:gap-2 z-[1] justify-center w-fit mx-auto"> 211 <div 212 className=" w-fit text-xl text-center text-accent-2" 213 style={{ ··· 225 </div> 226 {existingRSVP?.plus_ones && existingRSVP?.plus_ones > 0 ? ( 227 <div className="absolute -top-2 -right-6 rotate-12 h-fit w-10 bg-accent-1 font-bold text-accent-2 rounded-full -z-10"> 228 - <div className="w-full text-center pr-[4px] pb-[1px]"> 229 +{existingRSVP?.plus_ones} 230 </div> 231 </div>
··· 119 return ( 120 <div className="relative w-full sm:p-6 py-4 px-3 rounded-md border-[1.5px] border-accent-1"> 121 <RSVPBackground /> 122 + <div className="relative flex flex-row gap-2 items-center mx-auto z-1 w-fit"> 123 <ButtonSecondary 124 type="button" 125 className={ 126 props.status === "MAYBE" 127 + ? "text-accent-2! bg-accent-1! text-lg" 128 : "" 129 } 130 onClick={() => props.setStatus("MAYBE")} ··· 135 type="button" 136 className={ 137 props.status === "GOING" 138 + ? "text-accent-2! bg-accent-1! text-lg" 139 : props.status === undefined 140 ? "text-lg" 141 : "" ··· 149 type="button" 150 className={ 151 props.status === "NOT_GOING" 152 + ? "text-accent-2! bg-accent-1! text-lg" 153 : "" 154 } 155 onClick={() => props.setStatus("NOT_GOING")} ··· 207 className={`relative w-full p-4 pb-5 rounded-md border-[1.5px] border-accent-1 font-bold items-center`} 208 > 209 <RSVPBackground /> 210 + <div className=" relative flex flex-col gap-1 sm:gap-2 z-1 justify-center w-fit mx-auto"> 211 <div 212 className=" w-fit text-xl text-center text-accent-2" 213 style={{ ··· 225 </div> 226 {existingRSVP?.plus_ones && existingRSVP?.plus_ones > 0 ? ( 227 <div className="absolute -top-2 -right-6 rotate-12 h-fit w-10 bg-accent-1 font-bold text-accent-2 rounded-full -z-10"> 228 + <div className="w-full text-center pr-[4px] pb-px"> 229 +{existingRSVP?.plus_ones} 230 </div> 231 </div>
+3 -3
components/Blocks/TextBlock/RenderYJSFragment.tsx
··· 1 import { Doc, applyUpdate, XmlElement, XmlHook, XmlText } from "yjs"; 2 import { nodes, marks } from "prosemirror-schema-basic"; 3 - import { CSSProperties } from "react"; 4 import { theme } from "tailwind.config"; 5 import * as base64 from "base64-js"; 6 ··· 34 let deltas = node.toDelta() as Delta[]; 35 if (deltas.length === 0) return <br />; 36 return ( 37 - <> 38 {deltas.map((d, index) => { 39 if (d.attributes?.link) 40 return ( ··· 56 </span> 57 ); 58 })} 59 - </> 60 ); 61 } 62
··· 1 import { Doc, applyUpdate, XmlElement, XmlHook, XmlText } from "yjs"; 2 import { nodes, marks } from "prosemirror-schema-basic"; 3 + import { CSSProperties, Fragment } from "react"; 4 import { theme } from "tailwind.config"; 5 import * as base64 from "base64-js"; 6 ··· 34 let deltas = node.toDelta() as Delta[]; 35 if (deltas.length === 0) return <br />; 36 return ( 37 + <Fragment key={index}> 38 {deltas.map((d, index) => { 39 if (d.attributes?.link) 40 return ( ··· 56 </span> 57 ); 58 })} 59 + </Fragment> 60 ); 61 } 62
+2 -2
components/Blocks/TextBlock/index.tsx
··· 167 ${alignmentClass} 168 ${props.type === "blockquote" ? " blockquote " : ""} 169 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} 170 - w-full whitespace-pre-wrap outline-none ${props.className} `} 171 > 172 {content} 173 </div> ··· 379 className={` 380 ${alignmentClass} 381 grow resize-none align-top whitespace-pre-wrap bg-transparent 382 - outline-none 383 384 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} 385 ${props.className}`}
··· 167 ${alignmentClass} 168 ${props.type === "blockquote" ? " blockquote " : ""} 169 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} 170 + w-full whitespace-pre-wrap outline-hidden ${props.className} `} 171 > 172 {content} 173 </div> ··· 379 className={` 380 ${alignmentClass} 381 grow resize-none align-top whitespace-pre-wrap bg-transparent 382 + outline-hidden 383 384 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : ""} 385 ${props.className}`}
+1 -1
components/Blocks/index.tsx
··· 95 96 return ( 97 <div 98 - className={`blocks w-full flex flex-col outline-none h-fit min-h-full`} 99 onClick={async (e) => { 100 if (!permissions.write) return; 101 if (useUIState.getState().selectedBlocks.length > 1) return;
··· 95 96 return ( 97 <div 98 + className={`blocks w-full flex flex-col outline-hidden h-fit min-h-full`} 99 onClick={async (e) => { 100 if (!permissions.write) return; 101 if (useUIState.getState().selectedBlocks.length > 1) return;
+5 -5
components/Canvas.tsx
··· 55 canvasWrapper 56 h-full w-fit mx-auto 57 max-w-[calc(100vw-12px)] 58 - ${!narrowWidth ? "sm:max-w-[calc(100vw-128px)] lg:max-w-[calc(var(--page-width-units)*2 + 24px))]" : " sm:max-w-[var(--page-width-units)]"} 59 rounded-lg 60 overflow-y-scroll 61 `} ··· 161 ${canvasFocused ? "sm:block hidden" : "hidden"} 162 w-[8px] h-12 163 absolute top-1/2 right-0 -translate-y-1/2 translate-x-[3px] 164 - rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,_inset_0_0_0_1px_white]`} 165 /> 166 ); 167 } ··· 186 <div className="font-normal">or double click anywhere</div> 187 </div> 188 } 189 - className="w-fit p-2 rounded-full bg-accent-1 border-2 outline outline-transparent hover:outline-1 hover:outline-accent-1 border-accent-1 text-accent-2" 190 onMouseDown={() => { 191 let page = document.getElementById( 192 elementId.page(props.entityID).canvasScrollArea, ··· 395 hidden group-hover/canvas-block:block 396 w-[5px] h-6 -ml-[3px] 397 absolute top-1/2 right-3 -translate-y-1/2 translate-x-[2px] 398 - rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,_inset_0_0_0_1px_white]`} 399 {...widthHandle.handlers} 400 /> 401 )} ··· 408 w-[8px] h-[8px] 409 absolute bottom-0 -right-0 410 -translate-y-1/2 -translate-x-1/2 411 - rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,_inset_0_0_0_1px_white]`} 412 {...rotateHandle.handlers} 413 /> 414 )}
··· 55 canvasWrapper 56 h-full w-fit mx-auto 57 max-w-[calc(100vw-12px)] 58 + ${!narrowWidth ? "sm:max-w-[calc(100vw-128px)] lg:max-w-[calc(var(--page-width-units)*2 + 24px))]" : " sm:max-w-(--page-width-units)"} 59 rounded-lg 60 overflow-y-scroll 61 `} ··· 161 ${canvasFocused ? "sm:block hidden" : "hidden"} 162 w-[8px] h-12 163 absolute top-1/2 right-0 -translate-y-1/2 translate-x-[3px] 164 + rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,inset_0_0_0_1px_white]`} 165 /> 166 ); 167 } ··· 186 <div className="font-normal">or double click anywhere</div> 187 </div> 188 } 189 + className="w-fit p-2 rounded-full bg-accent-1 border-2 outline-solid outline-transparent hover:outline-1 hover:outline-accent-1 border-accent-1 text-accent-2" 190 onMouseDown={() => { 191 let page = document.getElementById( 192 elementId.page(props.entityID).canvasScrollArea, ··· 395 hidden group-hover/canvas-block:block 396 w-[5px] h-6 -ml-[3px] 397 absolute top-1/2 right-3 -translate-y-1/2 translate-x-[2px] 398 + rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,inset_0_0_0_1px_white]`} 399 {...widthHandle.handlers} 400 /> 401 )} ··· 408 w-[8px] h-[8px] 409 absolute bottom-0 -right-0 410 -translate-y-1/2 -translate-x-1/2 411 + rounded-full bg-white border-2 border-[#8C8C8C] shadow-[0_0_0_1px_white,inset_0_0_0_1px_white]`} 412 {...rotateHandle.handlers} 413 /> 414 )}
+19
components/Icons/DiscoverSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const DiscoverSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + {...props} 12 + > 13 + <path 14 + d="M9.64065 3.31253C10.1895 3.0803 10.794 2.99599 11.3799 3.11722C14.3589 4.07474 17.3532 4.98462 20.3341 5.93656C22.634 6.67144 23.6052 9.42405 22.8126 11.9073C22.0195 14.3908 19.6316 16.0723 17.3311 15.3379C16.8984 15.1997 16.5126 14.9905 16.1768 14.7247C16.2579 15.9524 15.9531 17.2605 15.2559 18.4239C13.6542 21.0957 10.4061 22.3031 7.94044 20.8253C7.20913 20.3868 6.73474 19.7706 6.25977 19.085L1.73143 12.5323C1.40407 11.9161 1.28828 11.19 1.33104 10.4981C0.939931 10.5372 0.579602 10.2601 0.515604 9.86137C0.421342 9.27278 0.528354 8.61795 0.81248 8.01957C1.09245 7.43014 1.52027 6.94541 2.02049 6.65531C2.51666 6.36761 3.16289 6.23639 3.76953 6.53715C3.9583 6.63077 4.0903 6.79455 4.15234 6.98148C4.89188 6.80551 5.63743 6.86056 6.22462 7.03226L6.47657 7.11625L6.48731 7.12113L6.87599 7.28422C6.98277 7.18686 7.11194 7.07056 7.25587 6.94828C7.38624 6.83753 7.53576 6.71687 7.69337 6.59574C7.39699 6.57978 7.14544 6.35553 7.10743 6.05082C7.01872 5.33128 7.22912 4.62394 7.58009 4.0928C7.91814 3.58141 8.47646 3.10981 9.16018 3.09671C9.352 3.09317 9.5235 3.17857 9.64065 3.31253ZM13.1006 11.8116C11.3913 10.9381 8.98548 11.7132 7.68361 13.8848C6.33504 16.135 6.867 18.7239 8.58302 19.753C10.2995 20.7818 12.8357 20.0316 14.1846 17.7813C15.4848 15.6117 15.035 13.1269 13.4629 12.0293L13.1006 11.8116ZM8.62502 14.6944C9.53877 12.9 11.3932 12.0126 12.7666 12.712C14.1398 13.4115 14.3354 15.6904 13.5987 17.2266C12.8619 18.7627 10.8315 19.9088 9.45803 19.21C8.08459 18.5107 7.71138 16.4887 8.62502 14.6944ZM12.8252 15.6583C12.5605 15.5862 12.2843 15.7437 12.209 16.0108C11.9544 16.9164 11.348 17.5593 10.5908 17.7364C10.322 17.7993 10.1517 18.0692 10.211 18.3389C10.2705 18.6083 10.537 18.7758 10.8057 18.7129C11.998 18.434 12.8376 17.4515 13.169 16.2735C13.244 16.0063 13.0899 15.7308 12.8252 15.6583ZM5.85547 8.2266C4.97336 7.96914 3.68282 8.11567 2.97851 9.28324C2.35667 10.3146 2.57612 11.438 2.80858 11.8916L5.69629 16.0713C5.77272 15.1107 6.07668 14.1333 6.61134 13.2413C7.48409 11.7856 8.84564 10.7657 10.3028 10.377L9.4844 9.98149L9.47951 9.97954L8.30763 9.40043C8.2637 9.3787 8.22477 9.35022 8.18849 9.32035L6.74317 8.58402L6.02637 8.28324L5.85547 8.2266ZM12.9815 13.7989C12.8162 13.5797 12.5043 13.5386 12.2842 13.7071C12.0642 13.8761 12.0193 14.1909 12.1846 14.4102C12.2116 14.446 12.2398 14.4947 12.2637 14.5479C12.2881 14.6022 12.3012 14.6467 12.3057 14.669C12.3597 14.9397 12.6218 15.112 12.8916 15.0547C13.1615 14.9973 13.3369 14.7318 13.2832 14.461C13.2419 14.2534 13.1318 13.9983 12.9815 13.7989ZM19.9542 7.12699C18.5161 6.66773 16.6983 7.69499 16.043 9.74614C15.388 11.7975 16.2739 13.6882 17.712 14.1475C19.1501 14.6063 20.9671 13.5785 21.6222 11.5274C22.2768 9.47656 21.3918 7.58669 19.9542 7.12699ZM16.9376 10.0362C17.4516 8.5672 18.7799 7.69494 19.9044 8.08793C21.029 8.48141 21.4151 10.2077 21.0098 11.461C20.6045 12.7142 19.1677 13.8027 18.043 13.4092C16.9185 13.0156 16.4235 11.5054 16.9376 10.0362ZM20.253 10.3516C20.0491 10.3247 19.8601 10.4698 19.8311 10.6758C19.7294 11.3989 19.329 11.9579 18.7628 12.1739C18.5695 12.2476 18.4703 12.4649 18.5411 12.6592C18.6122 12.8536 18.8262 12.9515 19.0196 12.878C19.8995 12.5426 20.4401 11.7072 20.5714 10.7735C20.6 10.5675 20.457 10.3787 20.253 10.3516ZM11.0713 4.33011C10.6841 4.25272 10.0696 4.38611 9.6094 4.78128C9.32981 5.02143 9.10865 5.35827 9.05568 5.81449C9.06779 5.81012 9.07976 5.80506 9.09182 5.80082C9.4432 5.67716 9.78179 5.59962 10.0293 5.55277C10.1539 5.52919 10.2579 5.51245 10.3321 5.50199C10.4893 5.47983 10.638 5.47119 10.7901 5.543L13.6914 6.91312C13.8423 6.98455 13.9584 7.11404 14.0137 7.27152C14.0689 7.42905 14.0586 7.60294 13.9854 7.75297C13.5962 8.55099 13.3649 9.42277 13.3233 10.4864L13.7012 10.7139C13.7767 10.7535 13.8517 10.7955 13.9258 10.8399C14.1641 10.9827 14.3832 11.1444 14.584 11.3213C14.5588 10.678 14.6459 10.0123 14.8526 9.36528C15.2838 8.01572 16.186 6.9037 17.2891 6.28714L11.0713 4.33011ZM20.1739 8.90727C20.0289 8.76099 19.7921 8.76247 19.6456 8.9102C19.4996 9.05792 19.4981 9.29617 19.6426 9.44242C19.6677 9.46772 19.6956 9.50228 19.7198 9.54106C19.7443 9.58047 19.7586 9.61417 19.7647 9.63188C19.832 9.82743 20.0447 9.9297 20.2393 9.86039C20.4341 9.79056 20.5388 9.5748 20.4718 9.37895C20.4192 9.22583 20.3085 9.04328 20.1739 8.90727ZM12.3799 8.45512C12.0926 8.54994 11.8161 8.64566 11.6192 8.72856C11.4367 8.80543 11.1533 9.01215 10.8604 9.25785L12.1123 9.86332C12.1628 9.36625 12.2524 8.8992 12.3799 8.45512ZM10.2617 6.78129C10.0537 6.82066 9.78049 6.8842 9.50686 6.98051C9.39451 7.02005 9.28445 7.06426 9.18068 7.11332C8.87141 7.25956 8.46403 7.565 8.09865 7.87309L9.64748 8.66215C9.73004 8.58613 9.82307 8.4994 9.92483 8.41117C10.2477 8.13123 10.7217 7.75005 11.1348 7.57621C11.2995 7.50692 11.5001 7.43309 11.71 7.36039L10.4268 6.75297C10.3799 6.7604 10.324 6.7695 10.2617 6.78129Z" 15 + fill="currentColor" 16 + /> 17 + </svg> 18 + ); 19 + };
+18
components/Icons/MenuSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const MenuSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M19.9541 16.6455C20.6444 16.6455 21.2039 17.2053 21.2041 17.8955C21.2041 18.5859 20.6445 19.1455 19.9541 19.1455H4.04492C3.35476 19.1453 2.79492 18.5857 2.79492 17.8955C2.79509 17.2054 3.35486 16.6457 4.04492 16.6455H19.9541ZM19.9541 10.75C20.6444 10.75 21.2041 11.3097 21.2041 12C21.2041 12.6904 20.6445 13.25 19.9541 13.25H4.04492C3.35476 13.2498 2.79492 12.6902 2.79492 12C2.79495 11.3098 3.35478 10.7502 4.04492 10.75H19.9541ZM19.9541 4.85449C20.6445 4.85449 21.2041 5.41414 21.2041 6.10449C21.2039 6.79471 20.6444 7.35449 19.9541 7.35449H4.04492C3.35486 7.35426 2.79509 6.79457 2.79492 6.10449C2.79492 5.41428 3.35476 4.85472 4.04492 4.85449H19.9541Z" 14 + fill="currentColor" 15 + /> 16 + </svg> 17 + ); 18 + };
+55
components/Icons/NotificationSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const NotificationsUnreadSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + {...props} 12 + > 13 + <path 14 + d="M14.3024 1.43058C14.4867 1.24672 15.1979 0.543997 16.3942 0.836833C17.357 1.07263 17.7754 1.74014 17.9362 2.2255C18.1981 3.01633 18.0108 3.8152 17.82 4.33975C17.7969 4.40332 17.7727 4.46469 17.7487 4.52334C18.9071 5.32788 19.6985 6.48773 20.1706 7.88075C20.8486 9.8818 20.8697 12.3569 20.4616 14.9862C20.6157 15.1662 20.7567 15.3499 20.8825 15.537C21.5041 16.4613 21.7973 17.5195 21.4958 18.5887C21.0327 20.23 19.3745 21.253 17.4616 21.7391C17.2568 21.7911 17.0461 21.8384 16.8307 21.8797C16.1912 22.7044 15.1828 23.234 14.0554 23.2342C12.9086 23.2342 11.8844 22.6865 11.2468 21.8377C11.011 21.7957 10.774 21.7481 10.5368 21.6961C8.02478 21.1453 5.85985 20.1213 4.3962 18.8748C2.96429 17.6553 2.04288 16.066 2.50558 14.4247C2.89584 13.0411 4.13541 12.0966 5.66085 11.5428C6.53657 8.84034 7.70841 6.59211 9.27803 5.12978C10.4535 4.03476 11.8336 3.39971 13.4079 3.32608C13.4153 3.27496 13.4236 3.22252 13.4333 3.16983C13.5309 2.64013 13.7678 1.96392 14.3024 1.43058ZM13.3718 16.1415C13.1138 16.038 12.8422 16.1672 12.7653 16.4305C12.65 16.8259 12.6911 17.3483 12.8786 17.9227C12.4745 18.1673 12.1568 18.5345 11.9772 18.9686C11.872 19.2231 11.8142 19.501 11.8142 19.7909C11.8142 20.0147 11.8486 20.2312 11.9128 20.4354C12.1933 21.3271 13.0404 21.9842 14.0554 21.9842C15.0444 21.984 15.8734 21.3599 16.1745 20.5028C16.2531 20.279 16.2965 20.0395 16.2966 19.7909C16.2966 19.55 16.2562 19.3172 16.1823 19.0995C15.8876 18.2313 15.0526 17.5968 14.0554 17.5965C13.9626 17.5965 13.8706 17.6025 13.7809 17.6131C13.6611 17.222 13.6602 16.9421 13.6999 16.8055C13.7767 16.5423 13.6295 16.245 13.3718 16.1415ZM15.1657 5.00186C13.1827 4.51697 11.6095 5.00028 10.3005 6.21962C9.15557 7.28623 8.18904 8.94185 7.40108 11.0917C9.19338 10.785 11.3088 10.8436 13.4645 11.3163C13.869 11.4051 14.1062 11.8005 13.9938 12.1991C13.8813 12.5975 13.4617 12.8482 13.0573 12.7596C10.7306 12.2495 8.52421 12.2783 6.85225 12.703C5.13792 13.1386 4.20347 13.9198 3.97042 14.746C3.73772 15.5721 4.14841 16.6474 5.43136 17.7401C6.61793 18.7506 8.41814 19.6448 10.5847 20.1698C10.5709 20.0454 10.5642 19.919 10.5642 19.7909C10.5642 19.4052 10.6278 19.0346 10.7458 18.6893C7.80726 17.9008 5.76926 16.4462 6.09347 15.2967C6.44449 14.0544 9.42306 13.6382 12.7468 14.3671C16.0704 15.0959 18.4806 16.6938 18.1305 17.9364C18.0403 18.2562 17.7761 18.5215 17.3766 18.7264C17.4866 19.061 17.5466 19.4191 17.5466 19.7909C17.5465 19.9314 17.5377 20.0699 17.5212 20.2059C18.9994 19.7567 19.8152 19.0317 20.0309 18.2674C20.1833 17.727 20.0672 17.0908 19.5964 16.3905C19.1243 15.6887 18.3265 14.976 17.2477 14.3446C16.8964 14.139 16.798 13.6857 17.028 13.3329C17.2581 12.9804 17.7296 12.8608 18.0807 13.0663C18.4468 13.2805 18.7922 13.5085 19.113 13.7469C19.3429 11.6614 19.239 9.82077 18.7467 8.36805C18.1614 6.64093 17.0317 5.45828 15.1657 5.00186ZM16.6081 10.7997C16.8481 10.4428 17.332 10.348 17.6891 10.5878C18.0462 10.8277 18.1416 11.3116 17.902 11.6688C17.662 12.0261 17.1773 12.1217 16.82 11.8817C16.4628 11.6417 16.3681 11.1569 16.6081 10.7997ZM15.3757 6.62001C15.6081 6.27939 16.0775 6.19011 16.4235 6.42079C17.4996 7.13865 17.8434 8.29284 17.9235 9.08582C17.9646 9.4953 17.6614 9.85914 17.2468 9.89832C16.8322 9.93727 16.4621 9.6368 16.4206 9.22742C16.3626 8.65359 16.1303 8.01964 15.5827 7.65419C15.2369 7.42351 15.1438 6.96063 15.3757 6.62001ZM6.36202 2.73819C6.67743 2.46989 7.15121 2.50785 7.41963 2.82315C7.68787 3.13856 7.64995 3.61236 7.33467 3.88077C6.43652 4.64508 5.56938 5.65156 4.96652 7.31435C4.82536 7.70365 4.39491 7.90455 4.00558 7.76356C3.61644 7.62248 3.41476 7.19281 3.55539 6.80361C4.26248 4.85333 5.30341 3.6391 6.36202 2.73819ZM4.32003 1.66593C4.64351 1.40808 5.11532 1.46006 5.37374 1.78312C5.6321 2.1067 5.57904 2.58027 5.25558 2.83878C4.77905 3.21946 4.42558 3.58659 4.0466 4.23819C3.83836 4.59583 3.37909 4.71745 3.02121 4.50967C2.6633 4.30142 2.54164 3.8413 2.74972 3.48331C3.23405 2.65069 3.7184 2.14651 4.32003 1.66593ZM16.0261 2.29093C15.808 2.23756 15.6732 2.27495 15.5895 2.31339C15.491 2.35871 15.4235 2.42393 15.362 2.48526C15.1228 2.72383 14.9801 3.06822 14.9138 3.41983C15.1176 3.45367 15.3245 3.49655 15.5339 3.54776C15.8419 3.62311 16.1355 3.71434 16.4137 3.8212C16.5569 3.42086 16.6124 3.01823 16.5085 2.70401C16.4821 2.62444 16.44 2.54283 16.3747 2.47452C16.3146 2.4117 16.2132 2.33677 16.0261 2.29093Z" 15 + fill="currentColor" 16 + /> 17 + </svg> 18 + ); 19 + }; 20 + 21 + export const NotificationsReadSmall = (props: Props) => { 22 + return ( 23 + <svg 24 + width="24" 25 + height="24" 26 + viewBox="0 0 24 24" 27 + fill="none" 28 + xmlns="http://www.w3.org/2000/svg" 29 + {...props} 30 + > 31 + <path 32 + d="M2.40963 18.0472C2.40983 17.3641 2.99636 17.3642 2.99655 18.0472C2.99654 18.7307 3.28805 19.1528 3.38815 19.263C3.48823 19.3732 3.66105 19.7179 4.28463 19.7181C4.90847 19.7182 4.90847 20.3255 4.28463 20.3255C3.66107 20.3256 3.56221 20.5899 3.38815 20.7816C3.21412 20.9732 2.99855 21.2572 2.9985 21.9232C2.9985 22.5896 2.41159 22.6066 2.41159 21.9232C2.41151 21.2398 2.04325 20.8276 2.00143 20.7816C1.959 20.735 1.52689 20.3255 1.03854 20.3255C0.549966 20.3254 0.554718 19.7182 1.03854 19.7181C1.52186 19.7181 1.83806 19.3644 1.93014 19.263C2.02172 19.1622 2.40963 18.7307 2.40963 18.0472ZM18.3989 13.7962C18.3991 13.1543 18.8958 13.1543 18.896 13.7962C18.896 14.4386 19.1424 14.8352 19.227 14.9388C19.3117 15.0425 19.4577 15.3663 19.9848 15.3665C20.5125 15.3666 20.5125 15.9378 19.9848 15.9378C19.4575 15.9379 19.3742 16.1864 19.227 16.3665C19.0798 16.5466 18.897 16.8131 18.8969 17.4388C18.8969 18.0651 18.4008 18.0811 18.4008 17.4388C18.4007 16.7986 18.0911 16.4117 18.0542 16.3665C18.0188 16.3233 17.653 15.9381 17.2397 15.9378C16.8263 15.9378 16.8303 15.3665 17.2397 15.3665C17.6486 15.3663 17.916 15.0338 17.9936 14.9388C18.0711 14.844 18.3989 14.4386 18.3989 13.7962ZM5.68893 1.79134C6.32061 1.54967 6.8992 1.76772 7.31002 2.04427C7.71467 2.31686 8.06882 2.71494 8.35299 3.07649C8.63964 3.44125 8.90666 3.83827 9.10299 4.11653C9.3417 4.45493 9.26161 4.92361 8.9233 5.16243C8.58506 5.40111 8.1173 5.31974 7.87838 4.98177C7.65546 4.66583 7.42734 4.32673 7.17233 4.00227C6.91507 3.67506 6.67898 3.42822 6.47311 3.28938C6.3745 3.22296 6.30685 3.19837 6.27096 3.19075C6.24474 3.18528 6.23525 3.18881 6.22506 3.1927C6.18423 3.20859 5.9833 3.32114 5.7192 3.88899C5.59862 4.14843 5.29699 5.01402 4.93209 6.10677C4.85847 6.12987 4.80511 6.14661 4.79831 6.15462C4.79485 6.15884 4.79121 6.16214 4.78854 6.16536C4.4966 6.19308 4.21288 6.31734 3.99459 6.54427C3.75682 6.79162 3.64898 7.10768 3.64791 7.40462C3.64696 7.70171 3.75272 8.02065 3.99264 8.27083C4.04882 8.32937 4.1096 8.38044 4.1733 8.42513C3.95587 9.09776 3.75091 9.74532 3.5776 10.2884C3.85066 10.3144 4.12174 10.3779 4.38034 10.4691C5.16136 10.7446 5.92693 11.3005 6.54928 12.0501C6.71967 11.9646 6.90306 11.9158 7.08932 11.8929C7.52605 11.8392 7.97349 11.9251 8.39889 12.1146C8.52073 11.5922 8.77238 11.1032 9.17428 10.6868C10.661 9.14683 13.2111 9.62241 14.8227 11.1781C14.9553 11.306 15.0799 11.4413 15.1977 11.5814C15.3522 11.0931 15.5412 10.4983 15.7387 9.87044C16.2338 8.29641 16.7876 6.52428 17.0298 5.71028C17.333 4.69126 17.7647 3.91964 18.3823 3.49056C19.0569 3.02218 19.7993 3.06179 20.4389 3.34017C21.047 3.60494 21.6053 4.09649 22.0825 4.63802C22.5684 5.18942 23.0142 5.84393 23.3862 6.50032C23.59 6.86052 23.4639 7.31757 23.104 7.52181C22.7438 7.72592 22.2859 7.59954 22.0815 7.23958C21.7486 6.6521 21.3605 6.08653 20.9575 5.62923C20.546 5.16239 20.1597 4.85528 19.8403 4.71614C19.5524 4.5908 19.3833 4.62119 19.2368 4.72298C19.0332 4.86476 18.7324 5.24367 18.4663 6.13802C18.2201 6.9656 17.6633 8.74963 17.1694 10.3197C16.922 11.1061 16.6893 11.8418 16.519 12.3802C16.4339 12.6494 16.3652 12.87 16.3169 13.0228C16.2928 13.0988 16.274 13.1581 16.2612 13.1986C16.2548 13.2188 16.2489 13.235 16.2456 13.2454C16.2441 13.2501 16.2425 13.2536 16.2417 13.2562V13.2591L16.2407 13.2601C16.2268 13.3038 16.2077 13.3449 16.187 13.3841C16.5566 14.5982 16.4051 15.8801 15.5122 16.805C14.0254 18.345 11.4753 17.8696 9.86373 16.3138C9.18414 15.6576 8.69133 14.8276 8.45944 13.972C8.05454 13.5525 7.64913 13.397 7.38424 13.3831C7.59712 13.8598 7.7328 14.345 7.7778 14.8138C7.86052 15.6775 7.64387 16.5969 6.91061 17.1702C6.17739 17.7433 5.23363 17.7322 4.41549 17.4437C3.58744 17.1515 2.77404 16.5464 2.13327 15.7269C1.49254 14.9074 1.10184 13.972 1.01803 13.098C0.936885 12.25 1.1447 11.3498 1.84616 10.7747C2.09807 9.97534 2.5868 8.4405 3.06979 6.96224C3.59658 5.34994 4.14583 3.71844 4.3608 3.25618C4.68344 2.56265 5.10288 2.01575 5.68893 1.79134ZM13.7807 12.2572C12.4881 11.0094 10.9295 11.0284 10.2534 11.7288C9.57757 12.4294 9.61347 13.987 10.9057 15.2347C12.1981 16.4823 13.7567 16.464 14.4331 15.764C15.1092 15.0636 15.0731 13.505 13.7807 12.2572ZM3.88229 11.8841C3.3528 11.6973 2.99993 11.7759 2.80905 11.9251C2.61843 12.0746 2.45867 12.3971 2.51217 12.9554C2.5648 13.5041 2.82326 14.1742 3.31491 14.8031C3.8066 15.4319 4.39472 15.8452 4.91452 16.0286C5.44348 16.2152 5.79586 16.1376 5.98678 15.9886C6.17756 15.8394 6.33808 15.5159 6.28463 14.9574C6.23201 14.4087 5.97256 13.7385 5.48092 13.1097C4.98934 12.481 4.40201 12.0676 3.88229 11.8841ZM11.9194 14.7786C12.0925 14.5637 12.4074 14.5295 12.6225 14.7025C12.661 14.7334 12.7453 14.7848 12.8491 14.8284C12.8979 14.849 12.9439 14.8642 12.9819 14.8743C13.0173 14.8837 13.0359 14.8858 13.0395 14.8861C13.3156 14.8914 13.5361 15.1197 13.5307 15.3958C13.5253 15.6718 13.2969 15.8912 13.021 15.8861C12.823 15.8822 12.6163 15.8145 12.4614 15.7493C12.2997 15.6813 12.1271 15.5876 11.9956 15.4818C11.7806 15.3087 11.7465 14.9937 11.9194 14.7786ZM3.50924 12.1361L3.57955 12.1497L3.6733 12.1878C3.88055 12.2932 3.99317 12.533 3.92916 12.7659C3.90752 12.8445 3.8653 12.9118 3.81393 12.9681C3.81694 12.9884 3.81922 13.0124 3.82467 13.0384C3.87694 13.2878 4.02405 13.6447 4.28561 13.9671C4.45941 14.1814 4.42741 14.4972 4.21334 14.6712C3.99906 14.8451 3.68332 14.812 3.50924 14.598C3.14412 14.1481 2.92999 13.6431 2.84616 13.2425C2.80618 13.0513 2.78536 12.8362 2.82565 12.6468C2.84499 12.5561 2.89269 12.4054 3.02291 12.2845C3.16403 12.1538 3.34353 12.1108 3.50924 12.1361ZM11.0669 11.7454C11.2971 11.7138 11.5265 11.8478 11.6069 12.0755C11.6924 12.3193 11.576 12.5828 11.3471 12.6908C11.3428 12.696 11.3346 12.705 11.3256 12.721C11.2978 12.7706 11.2672 12.8577 11.2592 12.9779C11.244 13.2098 11.3168 13.5618 11.6518 13.9544C11.831 14.1644 11.806 14.4802 11.5962 14.6595C11.3862 14.8387 11.0704 14.8145 10.8911 14.6048C10.4014 14.0312 10.2273 13.4258 10.2612 12.9115C10.2779 12.6589 10.3462 12.4249 10.4546 12.2318C10.5444 12.0719 10.6842 11.9039 10.8803 11.807L10.9672 11.7699L11.0669 11.7454ZM5.92428 5.79427C5.92428 5.2357 6.35593 5.23572 6.35592 5.79427C6.35593 6.35264 6.57034 6.69727 6.64401 6.78743C6.71763 6.87749 6.84534 7.15945 7.30416 7.1595C7.76238 7.15988 7.76238 7.65527 7.30416 7.65559C6.84534 7.65559 6.77205 7.872 6.64401 8.02864C6.51604 8.18525 6.35794 8.41728 6.35788 8.96126C6.35788 9.50588 5.92623 9.51978 5.92623 8.96126C5.92613 8.40366 5.65582 8.067 5.62448 8.02864C5.59372 7.99102 5.27579 7.65582 4.91647 7.65559C4.557 7.65559 4.56049 7.1595 4.91647 7.1595C5.27178 7.15932 5.50406 6.87024 5.57174 6.78743C5.63909 6.70504 5.92426 6.3528 5.92428 5.79427Z" 33 + fill="currentColor" 34 + /> 35 + </svg> 36 + ); 37 + }; 38 + 39 + export const ReaderRead = (props: Props) => { 40 + return ( 41 + <svg 42 + width="24" 43 + height="24" 44 + viewBox="0 0 24 24" 45 + fill="none" 46 + xmlns="http://www.w3.org/2000/svg" 47 + {...props} 48 + > 49 + <path 50 + d="M5.3939 5.10098C6.94949 3.45228 9.5127 3.07248 11.1166 4.58535C11.8822 5.30769 12.2281 6.27357 12.2132 7.26504C12.4456 7.20657 12.6982 7.16362 12.9515 7.15469C13.2516 7.14413 13.6091 7.17916 13.9379 7.34707C14.6404 7.7064 15.0081 8.33088 15.142 8.9418C16.5998 8.30589 18.3571 8.43563 19.7631 9.42032C21.8907 10.9106 22.4521 13.8268 20.9711 15.9418C20.5099 16.6002 19.9071 17.0981 19.2328 17.4281C19.2725 17.8704 19.3144 18.3372 19.389 18.8285C19.4945 19.5226 19.6505 20.0808 19.8754 20.4223C20.103 20.7681 20.0071 21.2335 19.6615 21.4613C19.3156 21.6891 18.8503 21.5932 18.6224 21.2475C18.2076 20.6174 18.0171 19.7803 17.9066 19.0531C17.8466 18.658 17.8029 18.2404 17.7679 17.8647C16.6347 18.0087 15.441 17.7451 14.4291 17.0365C12.3744 15.5975 11.7805 12.8288 13.0756 10.7358L11.4466 9.68106C11.2965 9.90817 11.1261 10.1258 10.9349 10.3285C9.3795 11.9772 6.81619 12.3575 5.21226 10.8451C4.85829 10.5112 4.59328 10.1249 4.41246 9.70841C4.12505 9.84619 3.7775 10.0195 3.40464 10.2016C3.08808 10.3561 2.76192 10.5118 2.47203 10.6361C2.20246 10.7518 1.89418 10.872 1.63999 10.9145C1.23165 10.9825 0.845053 10.7066 0.776711 10.2982C0.7085 9.88981 0.984507 9.50334 1.39292 9.43497C1.44871 9.42563 1.60626 9.37614 1.8812 9.25821C2.1356 9.14908 2.43339 9.00579 2.74644 8.85294C3.20082 8.63107 3.71706 8.37021 4.11265 8.19278C4.12147 7.09833 4.57394 5.97021 5.3939 5.10098ZM18.9027 10.6488C17.7826 9.86431 16.3636 9.87558 15.3129 10.5473C16.188 11.0878 17.0483 11.6416 17.7211 12.0824C18.1169 12.3418 18.449 12.5624 18.682 12.7182C18.7984 12.796 18.8907 12.8585 18.9535 12.9008C18.9844 12.9216 19.0085 12.9377 19.0248 12.9486C19.0328 12.9541 19.0392 12.9585 19.0433 12.9613C19.0454 12.9627 19.0471 12.9645 19.0482 12.9652H19.0492V12.9662C19.2776 13.1212 19.3377 13.4321 19.183 13.6606C19.0279 13.8888 18.7161 13.9473 18.4877 13.7924L18.4867 13.7934C18.4857 13.7927 18.4847 13.7908 18.4828 13.7895C18.4789 13.7868 18.4729 13.7829 18.4652 13.7777C18.4493 13.767 18.4248 13.7507 18.3939 13.7299C18.3771 13.7186 18.3582 13.7059 18.3373 13.6918C18.3409 13.7007 18.3455 13.7094 18.349 13.7182C18.4391 13.9478 18.4846 14.151 18.514 14.3129C18.5488 14.5038 18.5528 14.5587 18.5697 14.6166C18.6475 14.8814 18.4955 15.1597 18.2308 15.2377C17.9663 15.3152 17.6888 15.1642 17.6107 14.8998C17.5741 14.7753 17.5483 14.5898 17.5306 14.4926C17.5076 14.366 17.4762 14.2309 17.4183 14.0834C17.3064 13.7986 17.0738 13.4138 16.516 12.9633L14.3353 11.5512C13.4914 12.949 13.8764 14.8184 15.2894 15.808C16.7626 16.8395 18.752 16.4941 19.7416 15.0815C20.731 13.6686 20.3757 11.6807 18.9027 10.6488ZM10.0873 5.67715C9.23327 4.87156 7.62151 4.92548 6.48472 6.13028C5.34805 7.33535 5.38756 8.94767 6.24156 9.75333C7.09567 10.5587 8.70742 10.5041 9.8441 9.29923C9.97166 9.16397 10.0826 9.02211 10.181 8.87833C8.43436 8.11224 8.03413 8.1053 7.73863 8.15372C7.49417 8.19384 7.43342 8.19931 7.34215 8.21719C7.27583 8.2302 7.20421 8.24884 7.00426 8.31387C6.74185 8.39904 6.45989 8.25486 6.37437 7.99258C6.28923 7.73019 6.43242 7.44723 6.69469 7.36172C6.90022 7.29488 7.02051 7.26011 7.14976 7.23477C7.25177 7.2148 7.38857 7.1961 7.5648 7.16739L7.50523 7.1293C7.27353 6.9792 7.20675 6.66966 7.3568 6.43789C7.50691 6.20642 7.81653 6.14058 8.0482 6.29043L10.6127 7.95059C10.8517 7.07384 10.6453 6.20394 10.0873 5.67715ZM13.0043 8.65372C12.9388 8.65604 12.868 8.66411 12.7933 8.67618L13.6478 9.14883C13.5794 8.94601 13.4513 8.7833 13.2552 8.68301C13.2369 8.67373 13.1626 8.64818 13.0043 8.65372Z" 51 + fill="currentColor" 52 + /> 53 + </svg> 54 + ); 55 + };
+1 -1
components/Icons/PopoverArrow.tsx
··· 11 height="8" 12 viewBox="0 0 16 8" 13 fill="none" 14 - className="-mt-[1px]" 15 xmlns="http://www.w3.org/2000/svg" 16 > 17 <path
··· 11 height="8" 12 viewBox="0 0 16 8" 13 fill="none" 14 + className="-mt-px" 15 xmlns="http://www.w3.org/2000/svg" 16 > 17 <path
+1
components/Icons/PublishSmall.tsx
··· 8 viewBox="0 0 24 24" 9 fill="none" 10 xmlns="http://www.w3.org/2000/svg" 11 > 12 <path 13 fillRule="evenodd"
··· 8 viewBox="0 0 24 24" 9 fill="none" 10 xmlns="http://www.w3.org/2000/svg" 11 + {...props} 12 > 13 <path 14 fillRule="evenodd"
+36
components/Icons/ReaderSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const ReaderUnreadSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="25" 7 + height="24" 8 + viewBox="0 0 25 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M2.82968 18.0472C2.82987 17.3641 3.41641 17.3642 3.41659 18.0472C3.41658 18.7307 3.7081 19.1528 3.80819 19.263C3.90827 19.3732 4.08109 19.7179 4.70468 19.7181C5.32851 19.7182 5.32851 20.3255 4.70468 20.3255C4.08111 20.3256 3.98225 20.5899 3.80819 20.7816C3.63417 20.9732 3.41859 21.2572 3.41854 21.9232C3.41854 22.5896 2.83163 22.6066 2.83163 21.9232C2.83155 21.2398 2.4633 20.8276 2.42147 20.7816C2.37905 20.735 1.94693 20.3255 1.45858 20.3255C0.97001 20.3254 0.974762 19.7182 1.45858 19.7181C1.9419 19.7181 2.25811 19.3644 2.35018 19.263C2.44176 19.1622 2.82968 18.7307 2.82968 18.0472ZM18.8189 13.7962C18.8191 13.1543 19.3158 13.1543 19.316 13.7962C19.316 14.4386 19.5624 14.8352 19.6471 14.9388C19.7317 15.0425 19.8778 15.3663 20.4049 15.3665C20.9325 15.3666 20.9325 15.9378 20.4049 15.9378C19.8775 15.9379 19.7943 16.1864 19.6471 16.3665C19.4999 16.5466 19.3171 16.8131 19.317 17.4388C19.317 18.0651 18.8209 18.0811 18.8209 17.4388C18.8207 16.7986 18.5111 16.4117 18.4742 16.3665C18.4388 16.3233 18.073 15.9381 17.6598 15.9378C17.2464 15.9378 17.2504 15.3665 17.6598 15.3665C18.0686 15.3663 18.336 15.0338 18.4137 14.9388C18.4911 14.844 18.8189 14.4386 18.8189 13.7962ZM6.10897 1.79134C6.74065 1.54967 7.31924 1.76772 7.73007 2.04427C8.13472 2.31686 8.48886 2.71494 8.77304 3.07649C9.05968 3.44125 9.32671 3.83827 9.52304 4.11653C9.76174 4.45493 9.68165 4.92361 9.34335 5.16243C9.0051 5.40111 8.53735 5.31974 8.29843 4.98177C8.07551 4.66583 7.84739 4.32673 7.59237 4.00227C7.33511 3.67506 7.09903 3.42822 6.89315 3.28938C6.79455 3.22296 6.72689 3.19837 6.69101 3.19075C6.66479 3.18528 6.65529 3.18881 6.64511 3.1927C6.60428 3.20859 6.40334 3.32114 6.13925 3.88899C6.01867 4.14843 5.71704 5.01402 5.35214 6.10677C5.27851 6.12987 5.22515 6.14661 5.21835 6.15462C5.21489 6.15884 5.21125 6.16214 5.20858 6.16536C4.91664 6.19308 4.63293 6.31734 4.41464 6.54427C4.17686 6.79162 4.06902 7.10768 4.06796 7.40462C4.067 7.70171 4.17276 8.02065 4.41268 8.27083C4.46886 8.32937 4.52964 8.38044 4.59335 8.42513C4.37591 9.09776 4.17095 9.74532 3.99765 10.2884C4.2707 10.3144 4.54179 10.3779 4.80038 10.4691C5.5814 10.7446 6.34697 11.3005 6.96933 12.0501C7.13971 11.9646 7.3231 11.9158 7.50936 11.8929C7.9461 11.8392 8.39353 11.9251 8.81893 12.1146C8.94078 11.5922 9.19242 11.1032 9.59433 10.6868C11.081 9.14683 13.6312 9.62241 15.2428 11.1781C15.3753 11.306 15.4999 11.4413 15.6178 11.5814C15.7723 11.0931 15.9613 10.4983 16.1588 9.87044C16.6539 8.29641 17.2076 6.52428 17.4498 5.71028C17.7531 4.69126 18.1848 3.91964 18.8023 3.49056C19.4769 3.02218 20.2194 3.06179 20.859 3.34017C21.4671 3.60494 22.0253 4.09649 22.5025 4.63802C22.9884 5.18942 23.4343 5.84393 23.8062 6.50032C24.0101 6.86052 23.884 7.31757 23.524 7.52181C23.1638 7.72592 22.7059 7.59954 22.5016 7.23958C22.1686 6.6521 21.7805 6.08653 21.3775 5.62923C20.9661 5.16239 20.5798 4.85528 20.2603 4.71614C19.9724 4.5908 19.8033 4.62119 19.6568 4.72298C19.4532 4.86476 19.1524 5.24367 18.8863 6.13802C18.6401 6.9656 18.0833 8.74963 17.5894 10.3197C17.3421 11.1061 17.1093 11.8418 16.9391 12.3802C16.854 12.6494 16.7853 12.87 16.7369 13.0228C16.7128 13.0988 16.6941 13.1581 16.6812 13.1986C16.6748 13.2188 16.6689 13.235 16.6656 13.2454C16.6641 13.2501 16.6625 13.2536 16.6617 13.2562V13.2591L16.6607 13.2601C16.6469 13.3038 16.6277 13.3449 16.607 13.3841C16.9766 14.5982 16.8251 15.8801 15.9322 16.805C14.4454 18.345 11.8953 17.8696 10.2838 16.3138C9.60418 15.6576 9.11137 14.8276 8.87948 13.972C8.47459 13.5525 8.06917 13.397 7.80429 13.3831C8.01717 13.8598 8.15284 14.345 8.19784 14.8138C8.28056 15.6775 8.06391 16.5969 7.33065 17.1702C6.59744 17.7433 5.65367 17.7322 4.83554 17.4437C4.00748 17.1515 3.19408 16.5464 2.55331 15.7269C1.91258 14.9074 1.52188 13.972 1.43808 13.098C1.35693 12.25 1.56474 11.3498 2.2662 10.7747C2.51811 9.97534 3.00684 8.4405 3.48983 6.96224C4.01662 5.34994 4.56587 3.71844 4.78085 3.25618C5.10348 2.56265 5.52293 2.01575 6.10897 1.79134ZM14.2008 12.2572C12.9082 11.0094 11.3496 11.0284 10.6734 11.7288C9.99762 12.4294 10.0335 13.987 11.3258 15.2347C12.6181 16.4823 14.1767 16.464 14.8531 15.764C15.5292 15.0636 15.4931 13.505 14.2008 12.2572ZM4.30233 11.8841C3.77284 11.6973 3.41998 11.7759 3.22909 11.9251C3.03847 12.0746 2.87871 12.3971 2.93222 12.9554C2.98485 13.5041 3.24331 14.1742 3.73495 14.8031C4.22664 15.4319 4.81476 15.8452 5.33456 16.0286C5.86353 16.2152 6.2159 16.1376 6.40683 15.9886C6.59761 15.8394 6.75812 15.5159 6.70468 14.9574C6.65205 14.4087 6.3926 13.7385 5.90097 13.1097C5.40938 12.481 4.82205 12.0676 4.30233 11.8841ZM12.3394 14.7786C12.5125 14.5637 12.8275 14.5295 13.0426 14.7025C13.081 14.7334 13.1653 14.7848 13.2691 14.8284C13.318 14.849 13.3639 14.8642 13.4019 14.8743C13.4373 14.8837 13.456 14.8858 13.4596 14.8861C13.7357 14.8914 13.9561 15.1197 13.9508 15.3958C13.9454 15.6718 13.7169 15.8912 13.441 15.8861C13.243 15.8822 13.0364 15.8145 12.8814 15.7493C12.7197 15.6813 12.5471 15.5876 12.4156 15.4818C12.2007 15.3087 12.1665 14.9937 12.3394 14.7786ZM3.92929 12.1361L3.9996 12.1497L4.09335 12.1878C4.3006 12.2932 4.41321 12.533 4.34921 12.7659C4.32756 12.8445 4.28534 12.9118 4.23397 12.9681C4.23698 12.9884 4.23927 13.0124 4.24472 13.0384C4.29698 13.2878 4.4441 13.6447 4.70565 13.9671C4.87945 14.1814 4.84746 14.4972 4.63339 14.6712C4.41911 14.8451 4.10336 14.812 3.92929 14.598C3.56417 14.1481 3.35003 13.6431 3.2662 13.2425C3.22622 13.0513 3.2054 12.8362 3.24569 12.6468C3.26503 12.5561 3.31274 12.4054 3.44296 12.2845C3.58407 12.1538 3.76357 12.1108 3.92929 12.1361ZM11.4869 11.7454C11.7171 11.7138 11.9466 11.8478 12.0269 12.0755C12.1125 12.3193 11.996 12.5828 11.7672 12.6908C11.7629 12.696 11.7547 12.705 11.7457 12.721C11.7178 12.7706 11.6872 12.8577 11.6793 12.9779C11.6641 13.2098 11.7368 13.5618 12.0719 13.9544C12.2511 14.1644 12.226 14.4802 12.0162 14.6595C11.8063 14.8387 11.4905 14.8145 11.3111 14.6048C10.8215 14.0312 10.6473 13.4258 10.6812 12.9115C10.698 12.6589 10.7662 12.4249 10.8746 12.2318C10.9645 12.0719 11.1042 11.9039 11.3004 11.807L11.3873 11.7699L11.4869 11.7454ZM6.34433 5.79427C6.34433 5.2357 6.77597 5.23572 6.77597 5.79427C6.77598 6.35264 6.99039 6.69727 7.06405 6.78743C7.13767 6.87749 7.26539 7.15945 7.72421 7.1595C8.18243 7.15988 8.18243 7.65527 7.72421 7.65559C7.26539 7.65559 7.19209 7.872 7.06405 8.02864C6.93609 8.18525 6.77798 8.41728 6.77792 8.96126C6.77792 9.50588 6.34628 9.51978 6.34628 8.96126C6.34618 8.40366 6.07586 8.067 6.04452 8.02864C6.01377 7.99102 5.69583 7.65582 5.33651 7.65559C4.97705 7.65559 4.98054 7.1595 5.33651 7.1595C5.69183 7.15932 5.92411 6.87024 5.99179 6.78743C6.05914 6.70504 6.3443 6.3528 6.34433 5.79427Z" 14 + fill="currentColor" 15 + /> 16 + </svg> 17 + ); 18 + }; 19 + 20 + export const ReaderReadSmall = (props: Props) => { 21 + return ( 22 + <svg 23 + width="24" 24 + height="24" 25 + viewBox="0 0 24 24" 26 + fill="none" 27 + xmlns="http://www.w3.org/2000/svg" 28 + {...props} 29 + > 30 + <path 31 + d="M5.3939 5.10098C6.94949 3.45228 9.5127 3.07248 11.1166 4.58535C11.8822 5.30769 12.2281 6.27357 12.2132 7.26504C12.4456 7.20657 12.6982 7.16362 12.9515 7.15469C13.2516 7.14413 13.6091 7.17916 13.9379 7.34707C14.6404 7.7064 15.0081 8.33088 15.142 8.9418C16.5998 8.30589 18.3571 8.43563 19.7631 9.42032C21.8907 10.9106 22.4521 13.8268 20.9711 15.9418C20.5099 16.6002 19.9071 17.0981 19.2328 17.4281C19.2725 17.8704 19.3144 18.3372 19.389 18.8285C19.4945 19.5226 19.6505 20.0808 19.8754 20.4223C20.103 20.7681 20.0071 21.2335 19.6615 21.4613C19.3156 21.6891 18.8503 21.5932 18.6224 21.2475C18.2076 20.6174 18.0171 19.7803 17.9066 19.0531C17.8466 18.658 17.8029 18.2404 17.7679 17.8647C16.6347 18.0087 15.441 17.7451 14.4291 17.0365C12.3744 15.5975 11.7805 12.8288 13.0756 10.7358L11.4466 9.68106C11.2965 9.90817 11.1261 10.1258 10.9349 10.3285C9.3795 11.9772 6.81619 12.3575 5.21226 10.8451C4.85829 10.5112 4.59328 10.1249 4.41246 9.70841C4.12505 9.84619 3.7775 10.0195 3.40464 10.2016C3.08808 10.3561 2.76192 10.5118 2.47203 10.6361C2.20246 10.7518 1.89418 10.872 1.63999 10.9145C1.23165 10.9825 0.845053 10.7066 0.776711 10.2982C0.7085 9.88981 0.984507 9.50334 1.39292 9.43497C1.44871 9.42563 1.60626 9.37614 1.8812 9.25821C2.1356 9.14908 2.43339 9.00579 2.74644 8.85294C3.20082 8.63107 3.71706 8.37021 4.11265 8.19278C4.12147 7.09833 4.57394 5.97021 5.3939 5.10098ZM18.9027 10.6488C17.7826 9.86431 16.3636 9.87558 15.3129 10.5473C16.188 11.0878 17.0483 11.6416 17.7211 12.0824C18.1169 12.3418 18.449 12.5624 18.682 12.7182C18.7984 12.796 18.8907 12.8585 18.9535 12.9008C18.9844 12.9216 19.0085 12.9377 19.0248 12.9486C19.0328 12.9541 19.0392 12.9585 19.0433 12.9613C19.0454 12.9627 19.0471 12.9645 19.0482 12.9652H19.0492V12.9662C19.2776 13.1212 19.3377 13.4321 19.183 13.6606C19.0279 13.8888 18.7161 13.9473 18.4877 13.7924L18.4867 13.7934C18.4857 13.7927 18.4847 13.7908 18.4828 13.7895C18.4789 13.7868 18.4729 13.7829 18.4652 13.7777C18.4493 13.767 18.4248 13.7507 18.3939 13.7299C18.3771 13.7186 18.3582 13.7059 18.3373 13.6918C18.3409 13.7007 18.3455 13.7094 18.349 13.7182C18.4391 13.9478 18.4846 14.151 18.514 14.3129C18.5488 14.5038 18.5528 14.5587 18.5697 14.6166C18.6475 14.8814 18.4955 15.1597 18.2308 15.2377C17.9663 15.3152 17.6888 15.1642 17.6107 14.8998C17.5741 14.7753 17.5483 14.5898 17.5306 14.4926C17.5076 14.366 17.4762 14.2309 17.4183 14.0834C17.3064 13.7986 17.0738 13.4138 16.516 12.9633L14.3353 11.5512C13.4914 12.949 13.8764 14.8184 15.2894 15.808C16.7626 16.8395 18.752 16.4941 19.7416 15.0815C20.731 13.6686 20.3757 11.6807 18.9027 10.6488ZM10.0873 5.67715C9.23327 4.87156 7.62151 4.92548 6.48472 6.13028C5.34805 7.33535 5.38756 8.94767 6.24156 9.75333C7.09567 10.5587 8.70742 10.5041 9.8441 9.29923C9.97166 9.16397 10.0826 9.02211 10.181 8.87833C8.43436 8.11224 8.03413 8.1053 7.73863 8.15372C7.49417 8.19384 7.43342 8.19931 7.34215 8.21719C7.27583 8.2302 7.20421 8.24884 7.00426 8.31387C6.74185 8.39904 6.45989 8.25487 6.37437 7.99258C6.28923 7.73019 6.43242 7.44723 6.69469 7.36172C6.90022 7.29488 7.02051 7.26011 7.14976 7.23477C7.25177 7.2148 7.38857 7.1961 7.5648 7.16739L7.50523 7.1293C7.27353 6.9792 7.20675 6.66966 7.3568 6.43789C7.50691 6.20642 7.81653 6.14058 8.0482 6.29043L10.6127 7.95059C10.8517 7.07384 10.6453 6.20394 10.0873 5.67715ZM13.0043 8.65372C12.9388 8.65604 12.868 8.66411 12.7933 8.67618L13.6478 9.14883C13.5794 8.94601 13.4513 8.7833 13.2552 8.68301C13.2369 8.67373 13.1626 8.64818 13.0043 8.65372Z" 32 + fill="currentColor" 33 + /> 34 + </svg> 35 + ); 36 + };
+21
components/Icons/SearchSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const SearchSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + {...props} 12 + > 13 + <path 14 + fillRule="evenodd" 15 + clipRule="evenodd" 16 + d="M15.3882 13.4918C16.0902 13.4116 16.8207 13.6407 17.3591 14.1791L17.368 14.188L17.3679 14.1881L20.8268 17.7645C21.7593 18.7021 21.7577 20.218 20.822 21.1537C19.8848 22.0909 18.3652 22.0909 17.428 21.1537L17.4191 21.1448L17.4192 21.1447L13.9603 17.5684C13.4304 17.0355 13.2022 16.3159 13.2755 15.6222L12.5856 14.9162C11.5587 15.5303 10.3576 15.8831 9.07417 15.8831C5.28991 15.8831 2.22216 12.8154 2.22216 9.03113C2.22216 5.24687 5.28991 2.17912 9.07417 2.17912C12.8584 2.17912 15.9262 5.24687 15.9262 9.03114C15.9262 10.4458 15.4975 11.7603 14.7629 12.8519L15.3882 13.4918ZM15.0363 16.5233C15.3829 16.5101 15.7131 16.3963 15.9528 16.1619C16.2057 15.9149 16.3227 15.6029 16.3364 15.2792L19.7526 18.8114L19.7525 18.8114L19.7614 18.8203C20.1128 19.1717 20.1128 19.7416 19.7614 20.093C19.4112 20.4432 18.8442 20.4445 18.4924 20.0968L15.0363 16.5233ZM9.07417 3.92912C6.25641 3.92912 3.97216 6.21337 3.97216 9.03113C3.97216 11.8489 6.25641 14.1331 9.07417 14.1331C11.8919 14.1331 14.1762 11.8489 14.1762 9.03114C14.1762 6.21337 11.8919 3.92912 9.07417 3.92912ZM10.2779 11.7018C10.4564 12.1657 10.2251 12.6865 9.76119 12.865C9.47813 12.974 9.18509 12.9893 8.95246 12.979C8.70872 12.9683 8.46274 12.926 8.23733 12.8706C7.80084 12.7632 7.33708 12.5802 7.01053 12.3667C6.55802 12.0708 5.66109 11.4318 5.39409 10.3237C5.27767 9.84042 5.57502 9.35431 6.05825 9.23788C6.54148 9.12146 7.02759 9.41881 7.14402 9.90204C7.23429 10.2767 7.57343 10.5841 7.99558 10.8601C8.10636 10.9325 8.36702 11.0488 8.66713 11.1226C8.81002 11.1578 8.93539 11.1765 9.03167 11.1808C9.09302 11.1835 9.12443 11.1795 9.13263 11.1784L9.13269 11.1784C9.59197 11.0126 10.1017 11.2439 10.2779 11.7018ZM5.5481 6.89456C5.19301 7.32017 5.25018 7.95304 5.67578 8.30813C6.10138 8.66322 6.73425 8.60606 7.08934 8.18046C7.44443 7.75485 7.38727 7.12198 6.96167 6.76689C6.53607 6.4118 5.90319 6.46896 5.5481 6.89456Z" 17 + fill="currentColor" 18 + /> 19 + </svg> 20 + ); 21 + };
+20
components/Icons/SearchTiny.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const SearchTiny = (props: Props) => { 4 + return ( 5 + <svg 6 + width="16" 7 + height="16" 8 + viewBox="0 0 16 16" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + fillRule="evenodd" 14 + clipRule="evenodd" 15 + d="M2.82929 7.1256C2.42204 5.14225 3.64873 3.25471 5.51296 2.87192C7.37719 2.48913 9.24843 3.74055 9.65568 5.72391C10.0629 7.70726 8.83624 9.5948 6.97201 9.97759C5.10778 10.3604 3.23654 9.10896 2.82929 7.1256ZM5.24142 1.54951C2.6052 2.09082 0.958359 4.7258 1.50688 7.39714C2.0554 10.0685 4.60732 11.8413 7.24355 11.3C7.9444 11.1561 8.57532 10.8642 9.11365 10.463L13.0573 13.6769C13.4854 14.0258 14.1153 13.9616 14.4642 13.5334C14.8131 13.1053 14.7489 12.4754 14.3207 12.1265L10.4344 8.95927C11.012 7.93759 11.2341 6.69894 10.9781 5.45237C10.4296 2.78102 7.87765 1.0082 5.24142 1.54951ZM4.70667 6.0399C5.09948 5.95924 5.35253 5.57542 5.27187 5.18261C5.19121 4.7898 4.80739 4.53675 4.41458 4.6174C4.02177 4.69806 3.76872 5.08189 3.84938 5.4747C3.93004 5.86751 4.31386 6.12056 4.70667 6.0399ZM5.09658 7.14249C5.03594 6.80268 4.71131 6.57638 4.3715 6.63703C4.03169 6.69767 3.80538 7.02231 3.86603 7.36211C4.05123 8.39982 5.20679 9.33276 6.72726 9.11913C7.06908 9.07111 7.30725 8.75508 7.25923 8.41326C7.2112 8.07143 6.89517 7.83327 6.55335 7.88129C5.61779 8.01274 5.14981 7.44073 5.09658 7.14249Z" 16 + fill="currentColor" 17 + /> 18 + </svg> 19 + ); 20 + };
+18
components/Icons/SortSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const SortSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M6.67186 18.5V8.74805L4.70408 10.7002C4.31199 11.089 3.67892 11.0863 3.29002 10.6943C2.90107 10.3023 2.90381 9.66923 3.29588 9.28027L6.96776 5.6377L7.04295 5.56934C7.43471 5.25182 8.01037 5.27501 8.37596 5.6377L12.0478 9.28027C12.4399 9.66924 12.4427 10.3023 12.0537 10.6943C11.6648 11.0863 11.0317 11.089 10.6396 10.7002L8.67186 8.74805V18.5C8.67186 19.0523 8.22414 19.5 7.67186 19.5C7.1196 19.5 6.67186 19.0523 6.67186 18.5ZM15.3281 5.5C15.3281 4.94772 15.7758 4.5 16.3281 4.5C16.8804 4.50003 17.3281 4.94774 17.3281 5.5V15.1016L19.2959 13.1504C19.688 12.7616 20.321 12.7642 20.7099 13.1562C21.0988 13.5483 21.0961 14.1814 20.7041 14.5703L17.0322 18.2129C16.6423 18.5997 16.0139 18.5997 15.624 18.2129L11.9521 14.5703C11.5601 14.1814 11.5573 13.5483 11.9463 13.1562C12.3352 12.7642 12.9683 12.7615 13.3603 13.1504L15.3281 15.1016V5.5Z" 14 + fill="currentColor" 15 + /> 16 + </svg> 17 + ); 18 + };
+18
components/Icons/TabsSmall.tsx
···
··· 1 + import { Props } from "./Props"; 2 + 3 + export const TabsSmall = (props: Props) => { 4 + return ( 5 + <svg 6 + width="24" 7 + height="24" 8 + viewBox="0 0 24 24" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + > 12 + <path 13 + d="M4.5741 3.43762C4.71829 3.43388 4.83844 3.45707 4.91101 3.47278L7.50476 4.03333L7.50378 4.0343C7.93101 4.12295 8.3082 4.36081 8.56433 4.70715C8.91472 4.53042 9.32259 4.46201 9.72644 4.5509L11.4755 4.93567C11.8499 5.01812 12.1582 5.22518 12.3632 5.50598C12.7041 5.34426 13.096 5.28489 13.4843 5.37024L15.2343 5.755C15.961 5.91506 16.4399 6.54224 16.3944 7.27454L16.3358 8.20227C17.2858 8.41813 17.9768 9.25659 17.9823 10.2443L20.9491 10.8712L20.954 10.8732C22.0873 11.1209 22.7352 12.3188 22.3212 13.4025L19.8788 19.7941C19.4226 20.9881 18.1618 21.6693 16.913 21.3966L3.97937 18.5724C2.74764 18.3034 1.88959 17.1843 1.9491 15.9249C1.94954 15.9155 1.95025 15.906 1.95105 15.8966L2.85925 5.32043C2.89069 4.14023 3.78539 3.55411 4.42175 3.45129L4.5741 3.43762ZM4.62488 4.94543C4.58481 4.96035 4.52557 4.98845 4.47449 5.04114C4.41904 5.09848 4.35632 5.19676 4.35632 5.38489C4.35631 5.40603 4.35519 5.4273 4.35339 5.44836L3.44714 15.9962V16.0939C3.46785 16.5814 3.81504 17.0007 4.29968 17.1066L17.2333 19.9318C17.7572 20.0461 18.286 19.7598 18.4774 19.2589L20.9198 12.8673C21.0063 12.6408 20.8706 12.3898 20.6337 12.338V12.337L17.8378 11.7472C17.5141 13.7033 17.0716 16.26 16.8915 17.2648C16.8341 17.5855 16.9494 17.7292 17.0175 17.7823C17.1088 17.8537 17.1828 17.8354 17.2089 17.8195C17.562 17.6031 18.0237 17.7134 18.2401 18.0665C18.4564 18.4197 18.3451 18.8814 17.9921 19.0978C17.3655 19.4817 16.6134 19.3701 16.0936 18.964C15.5506 18.5395 15.2689 17.8204 15.4159 17.0001C15.6184 15.871 16.1561 12.765 16.4774 10.7823C16.4808 10.6491 16.4831 10.4619 16.4833 10.257C16.4836 9.97523 16.2841 9.72512 15.996 9.66321L15.9852 9.66028L7.88953 7.79407C7.52736 7.71059 7.28106 7.37451 7.31042 7.00403L7.40808 5.77161C7.40576 5.67206 7.37646 5.61707 7.35046 5.58606C7.3204 5.55031 7.27091 5.51692 7.19812 5.50208L7.18738 5.50012L4.62488 4.94543ZM13.1053 6.56555C12.8679 6.51346 12.6242 6.69552 12.6102 6.93469L12.5868 7.33606L15.0829 7.91223L15.1219 7.30188C15.1323 7.13296 15.022 6.9882 14.8544 6.95129L13.1053 6.56555ZM9.34656 5.74622C9.1658 5.70656 8.98124 5.80266 8.89734 5.95618L8.85632 6.47668L11.328 7.04602L11.3641 6.48254C11.3745 6.31363 11.2632 6.16886 11.0956 6.13196L9.34656 5.74622Z" 14 + fill="currentColor" 15 + /> 16 + </svg> 17 + ); 18 + };
+4
components/IdentityProvider.tsx
··· 2 import { getIdentityData } from "actions/getIdentityData"; 3 import { createContext, useContext } from "react"; 4 import useSWR, { KeyedMutator, mutate } from "swr"; 5 6 type Identity = Awaited<ReturnType<typeof getIdentityData>>; 7 let IdentityContext = createContext({ 8 identity: null as Identity,
··· 2 import { getIdentityData } from "actions/getIdentityData"; 3 import { createContext, useContext } from "react"; 4 import useSWR, { KeyedMutator, mutate } from "swr"; 5 + import { DashboardState } from "./PageLayouts/DashboardLayout"; 6 7 + export type InterfaceState = { 8 + dashboards: { [id: string]: DashboardState | undefined }; 9 + }; 10 type Identity = Awaited<ReturnType<typeof getIdentityData>>; 11 let IdentityContext = createContext({ 12 identity: null as Identity,
+2 -2
components/Input.tsx
··· 100 JSX.IntrinsicElements["textarea"], 101 ) => { 102 let { label, textarea, ...inputProps } = props; 103 - let style = `appearance-none w-full font-normal bg-transparent text-base text-primary focus:outline-0 ${props.className} outline-none resize-none`; 104 return ( 105 - <label className=" input-with-border flex flex-col gap-[1px] text-sm text-tertiary font-bold italic leading-tight !py-1 !px-[6px]"> 106 {props.label} 107 {textarea ? ( 108 <textarea {...inputProps} className={style} />
··· 100 JSX.IntrinsicElements["textarea"], 101 ) => { 102 let { label, textarea, ...inputProps } = props; 103 + let style = `appearance-none w-full font-normal bg-transparent text-base text-primary focus:outline-0 ${props.className} outline-hidden resize-none`; 104 return ( 105 + <label className=" input-with-border flex flex-col gap-px text-sm text-tertiary font-bold italic leading-tight py-1! px-[6px]!"> 106 {props.label} 107 {textarea ? ( 108 <textarea {...inputProps} className={style} />
+2 -2
components/Layout.tsx
··· 90 font-bold z-10 py-1 px-3 91 text-left text-secondary 92 flex gap-2 93 - data-[highlighted]:bg-border-light data-[highlighted]:text-secondary 94 hover:bg-border-light hover:text-secondary 95 - outline-none 96 cursor-pointer 97 ${props.className} 98 `}
··· 90 font-bold z-10 py-1 px-3 91 text-left text-secondary 92 flex gap-2 93 + data-highlighted:bg-border-light data-highlighted:text-secondary 94 hover:bg-border-light hover:text-secondary 95 + outline-hidden 96 cursor-pointer 97 ${props.className} 98 `}
+20
components/Media.tsx
··· 20 return <div className={props.className}>{props.children}</div>; 21 return null; 22 }
··· 20 return <div className={props.className}>{props.children}</div>; 21 return null; 22 } 23 + 24 + export function MediaContents(props: { 25 + mobile: boolean; 26 + children: React.ReactNode; 27 + className?: string; 28 + }) { 29 + let initialRender = useIsInitialRender(); 30 + let isMobile = useIsMobile(); 31 + if (initialRender) 32 + return ( 33 + <div 34 + className={`${props.mobile ? "sm:hidden contents" : "hidden sm:contents"} ${props.className}`} 35 + > 36 + {props.children} 37 + </div> 38 + ); 39 + if ((isMobile && props.mobile) || (!isMobile && !props.mobile)) 40 + return <div className={props.className}>{props.children}</div>; 41 + return null; 42 + }
+77
components/PageHeader.tsx
···
··· 1 + "use client"; 2 + import { useState, useEffect } from "react"; 3 + 4 + export const Header = (props: { 5 + children: React.ReactNode; 6 + hasBackgroundImage: boolean; 7 + }) => { 8 + let [scrollPos, setScrollPos] = useState(0); 9 + 10 + useEffect(() => { 11 + const homeContent = document.getElementById("home-content"); 12 + 13 + const handleScroll = () => { 14 + if (homeContent) { 15 + setScrollPos(homeContent.scrollTop); 16 + } 17 + }; 18 + 19 + if (homeContent) { 20 + homeContent.addEventListener("scroll", handleScroll); 21 + return () => homeContent.removeEventListener("scroll", handleScroll); 22 + } 23 + }, []); 24 + 25 + let headerBGColor = props.hasBackgroundImage 26 + ? "var(--bg-page)" 27 + : "var(--bg-leaflet)"; 28 + 29 + return ( 30 + <div 31 + className={` 32 + headerWrapper 33 + sticky top-0 z-10 34 + w-full bg-transparent 35 + `} 36 + > 37 + <div 38 + style={ 39 + scrollPos < 20 40 + ? { 41 + paddingLeft: `calc(${scrollPos / 20}*8px)`, 42 + paddingRight: `calc(${scrollPos / 20}*8px)`, 43 + } 44 + : { paddingLeft: `8px`, paddingRight: `8px` } 45 + } 46 + > 47 + <div 48 + className={` 49 + headerContent 50 + border rounded-lg 51 + ${scrollPos > 20 ? "border-border-light" : "border-transparent"} 52 + py-1 53 + w-full flex justify-between items-center gap-4`} 54 + style={ 55 + scrollPos < 20 56 + ? { 57 + backgroundColor: `rgba(${headerBGColor}, ${scrollPos / 60 + 0.75})`, 58 + paddingLeft: props.hasBackgroundImage 59 + ? "8px" 60 + : `calc(${scrollPos / 20}*8px)`, 61 + paddingRight: props.hasBackgroundImage 62 + ? "8px" 63 + : `calc(${scrollPos / 20}*8px)`, 64 + } 65 + : { 66 + backgroundColor: `rgb(${headerBGColor})`, 67 + paddingLeft: "8px", 68 + paddingRight: "8px", 69 + } 70 + } 71 + > 72 + {props.children} 73 + </div> 74 + </div> 75 + </div> 76 + ); 77 + };
+450
components/PageLayouts/DashboardLayout.tsx
···
··· 1 + "use client"; 2 + import { useState, createContext, useContext } from "react"; 3 + import { Header } from "../PageHeader"; 4 + import { Footer } from "components/ActionBar/Footer"; 5 + import { Sidebar } from "components/ActionBar/Sidebar"; 6 + import { 7 + DesktopNavigation, 8 + MobileNavigation, 9 + navPages, 10 + } from "components/ActionBar/Navigation"; 11 + import { create } from "zustand"; 12 + import { Popover } from "components/Popover"; 13 + import { Checkbox } from "components/Checkbox"; 14 + import { Separator } from "components/Layout"; 15 + import { CloseTiny } from "components/Icons/CloseTiny"; 16 + import { MediaContents } from "components/Media"; 17 + import { SortSmall } from "components/Icons/SortSmall"; 18 + import { TabsSmall } from "components/Icons/TabsSmall"; 19 + import { Input } from "components/Input"; 20 + import { SearchTiny } from "components/Icons/SearchTiny"; 21 + import { InterfaceState, useIdentityData } from "components/IdentityProvider"; 22 + import { updateIdentityInterfaceState } from "actions/updateIdentityInterfaceState"; 23 + 24 + export type DashboardState = { 25 + display?: "grid" | "list"; 26 + sort?: "created" | "alphabetical"; 27 + filter: { 28 + drafts: boolean; 29 + published: boolean; 30 + docs: boolean; 31 + templates: boolean; 32 + }; 33 + }; 34 + 35 + type DashboardStore = { 36 + dashboards: { [id: string]: DashboardState }; 37 + setDashboard: (id: string, partial: Partial<DashboardState>) => void; 38 + }; 39 + 40 + const defaultDashboardState: DashboardState = { 41 + display: undefined, 42 + sort: undefined, 43 + filter: { drafts: false, published: false, docs: false, templates: false }, 44 + }; 45 + 46 + export const useDashboardStore = create<DashboardStore>((set, get) => ({ 47 + dashboards: {}, 48 + setDashboard: (id: string, partial: Partial<DashboardState>) => { 49 + console.log(partial); 50 + set((state) => ({ 51 + dashboards: { 52 + ...state.dashboards, 53 + [id]: { 54 + ...(state.dashboards[id] || defaultDashboardState), 55 + ...partial, 56 + }, 57 + }, 58 + })); 59 + }, 60 + })); 61 + 62 + const DashboardIdContext = createContext<string | null>(null); 63 + 64 + export const useDashboardId = () => { 65 + const id = useContext(DashboardIdContext); 66 + if (!id) { 67 + throw new Error("useDashboardId must be used within a DashboardLayout"); 68 + } 69 + return id; 70 + }; 71 + 72 + export const useDashboardState = () => { 73 + const id = useDashboardId(); 74 + let { identity } = useIdentityData(); 75 + let localState = useDashboardStore( 76 + (state) => state.dashboards[id] || defaultDashboardState, 77 + ); 78 + if (!identity) return localState; 79 + let metadata = identity.interface_state as InterfaceState; 80 + return metadata?.dashboards?.[id] || defaultDashboardState; 81 + }; 82 + 83 + export const useSetDashboardState = () => { 84 + const id = useDashboardId(); 85 + let { identity, mutate } = useIdentityData(); 86 + const setDashboard = useDashboardStore((state) => state.setDashboard); 87 + return async (partial: Partial<DashboardState>) => { 88 + if (!identity) return setDashboard(id, partial); 89 + 90 + let interface_state = (identity.interface_state as InterfaceState) || {}; 91 + let newDashboardState = { 92 + ...defaultDashboardState, 93 + ...interface_state.dashboards?.[id], 94 + ...partial, 95 + }; 96 + mutate( 97 + { 98 + ...identity, 99 + interface_state: { 100 + ...interface_state, 101 + dashboards: { 102 + ...interface_state.dashboards, 103 + [id]: newDashboardState, 104 + }, 105 + }, 106 + }, 107 + { revalidate: false }, 108 + ); 109 + await updateIdentityInterfaceState({ 110 + ...interface_state, 111 + dashboards: { 112 + [id]: newDashboardState, 113 + }, 114 + }); 115 + }; 116 + }; 117 + 118 + export function DashboardLayout< 119 + T extends { 120 + [name: string]: { content: React.ReactNode; controls: React.ReactNode }; 121 + }, 122 + >(props: { 123 + id: string; 124 + hasBackgroundImage: boolean; 125 + tabs: T; 126 + defaultTab: keyof T; 127 + currentPage: navPages; 128 + publication?: string; 129 + actions: React.ReactNode; 130 + }) { 131 + let [tab, setTab] = useState(props.defaultTab); 132 + let { content, controls } = props.tabs[tab]; 133 + 134 + let [headerState, setHeaderState] = useState<"default" | "controls">( 135 + "default", 136 + ); 137 + return ( 138 + <DashboardIdContext.Provider value={props.id}> 139 + <div 140 + className={`dashboard pwa-padding relative max-w-(--breakpoint-lg) w-full h-full mx-auto flex sm:flex-row flex-col sm:items-stretch sm:px-6`} 141 + > 142 + <MediaContents mobile={false}> 143 + <div className="flex flex-col gap-4 my-6"> 144 + <DesktopNavigation 145 + currentPage={props.currentPage} 146 + publication={props.publication} 147 + /> 148 + {props.actions && <Sidebar alwaysOpen>{props.actions}</Sidebar>} 149 + </div> 150 + </MediaContents> 151 + <div 152 + className={`w-full h-full flex flex-col gap-2 relative overflow-y-scroll pt-3 pb-12 px-3 sm:pt-8 sm:pb-12 sm:pl-6 sm:pr-4 `} 153 + id="home-content" 154 + > 155 + {Object.keys(props.tabs).length <= 1 && !controls ? null : ( 156 + <> 157 + <Header hasBackgroundImage={props.hasBackgroundImage}> 158 + {headerState === "default" ? ( 159 + <> 160 + {Object.keys(props.tabs).length > 1 && ( 161 + <div className="pubDashTabs flex flex-row gap-1"> 162 + {Object.keys(props.tabs).map((t) => ( 163 + <Tab 164 + key={t} 165 + name={t} 166 + selected={t === tab} 167 + onSelect={() => setTab(t)} 168 + /> 169 + ))} 170 + </div> 171 + )} 172 + {props.publication && ( 173 + <button 174 + className={`sm:hidden block text-tertiary`} 175 + onClick={() => { 176 + setHeaderState("controls"); 177 + }} 178 + > 179 + <SortSmall /> 180 + </button> 181 + )} 182 + <div 183 + className={`sm:block ${props.publication && "hidden"} grow`} 184 + > 185 + {controls} 186 + </div> 187 + </> 188 + ) : ( 189 + <> 190 + {controls} 191 + <button 192 + className="text-tertiary" 193 + onClick={() => { 194 + setHeaderState("default"); 195 + }} 196 + > 197 + <TabsSmall /> 198 + </button> 199 + </> 200 + )} 201 + </Header> 202 + </> 203 + )} 204 + {content} 205 + </div> 206 + <Footer> 207 + <MobileNavigation 208 + currentPage={props.currentPage} 209 + publication={props.publication} 210 + /> 211 + {props.actions && ( 212 + <> 213 + <Separator /> 214 + {props.actions} 215 + </> 216 + )} 217 + </Footer> 218 + </div> 219 + </DashboardIdContext.Provider> 220 + ); 221 + } 222 + 223 + export const HomeDashboardControls = (props: { 224 + searchValue: string; 225 + setSearchValueAction: (searchValue: string) => void; 226 + hasBackgroundImage: boolean; 227 + defaultDisplay: Exclude<DashboardState["display"], undefined>; 228 + hasPubs: boolean; 229 + hasTemplates: boolean; 230 + }) => { 231 + let { display, sort } = useDashboardState(); 232 + console.log({ display, props }); 233 + display = display || props.defaultDisplay; 234 + let setState = useSetDashboardState(); 235 + 236 + let { identity } = useIdentityData(); 237 + console.log(props); 238 + 239 + return ( 240 + <div className="dashboardControls w-full flex gap-4"> 241 + {identity && ( 242 + <SearchInput 243 + searchValue={props.searchValue} 244 + setSearchValue={props.setSearchValueAction} 245 + hasBackgroundImage={props.hasBackgroundImage} 246 + /> 247 + )} 248 + <div className="flex gap-2 w-max shrink-0 items-center text-sm text-tertiary"> 249 + <DisplayToggle setState={setState} display={display} /> 250 + <Separator classname="h-4 min-h-4!" /> 251 + 252 + {props.hasPubs || props.hasTemplates ? ( 253 + <> 254 + {props.hasPubs} 255 + {props.hasTemplates} 256 + <FilterOptions 257 + hasPubs={props.hasPubs} 258 + hasTemplates={props.hasTemplates} 259 + /> 260 + <Separator classname="h-4 min-h-4!" />{" "} 261 + </> 262 + ) : null} 263 + <SortToggle setState={setState} sort={sort} /> 264 + </div> 265 + </div> 266 + ); 267 + }; 268 + 269 + export const PublicationDashboardControls = (props: { 270 + searchValue: string; 271 + setSearchValueAction: (searchValue: string) => void; 272 + hasBackgroundImage: boolean; 273 + defaultDisplay: Exclude<DashboardState["display"], undefined>; 274 + }) => { 275 + let { display, sort } = useDashboardState(); 276 + console.log({ display, props }); 277 + display = display || props.defaultDisplay; 278 + let setState = useSetDashboardState(); 279 + return ( 280 + <div className="dashboardControls w-full flex gap-4"> 281 + <SearchInput 282 + searchValue={props.searchValue} 283 + setSearchValue={props.setSearchValueAction} 284 + hasBackgroundImage={props.hasBackgroundImage} 285 + /> 286 + <div className="flex gap-2 w-max shrink-0 items-center text-sm text-tertiary"> 287 + <DisplayToggle setState={setState} display={display} /> 288 + <Separator classname="h-4 min-h-4!" /> 289 + <SortToggle setState={setState} sort={sort} /> 290 + </div> 291 + </div> 292 + ); 293 + }; 294 + 295 + const SortToggle = (props: { 296 + setState: (partial: Partial<DashboardState>) => Promise<void>; 297 + sort: string | undefined; 298 + }) => { 299 + return ( 300 + <button 301 + onClick={() => 302 + props.setState({ 303 + sort: props.sort === "created" ? "alphabetical" : "created", 304 + }) 305 + } 306 + > 307 + Sort: {props.sort === "created" ? "Created On" : "A to Z"} 308 + </button> 309 + ); 310 + }; 311 + 312 + const DisplayToggle = (props: { 313 + setState: (partial: Partial<DashboardState>) => Promise<void>; 314 + display: string | undefined; 315 + }) => { 316 + return ( 317 + <button 318 + onClick={() => { 319 + props.setState({ 320 + display: props.display === "list" ? "grid" : "list", 321 + }); 322 + }} 323 + > 324 + {props.display === "list" ? "List" : "Grid"} 325 + </button> 326 + ); 327 + }; 328 + 329 + function Tab(props: { name: string; selected: boolean; onSelect: () => void }) { 330 + return ( 331 + <div 332 + className={`pubTabs px-1 py-0 rounded-md hover:cursor-pointer ${props.selected ? "text-accent-2 bg-accent-1 font-bold -mb-px" : "text-tertiary"}`} 333 + onClick={() => props.onSelect()} 334 + > 335 + {props.name} 336 + </div> 337 + ); 338 + } 339 + 340 + const FilterOptions = (props: { hasPubs: boolean; hasTemplates: boolean }) => { 341 + let { filter } = useDashboardState(); 342 + let setState = useSetDashboardState(); 343 + let filterCount = Object.values(filter).filter(Boolean).length; 344 + 345 + return ( 346 + <Popover 347 + className="text-sm px-2! py-1!" 348 + trigger={<div>Filter {filterCount > 0 && `(${filterCount})`}</div>} 349 + > 350 + {props.hasPubs && ( 351 + <> 352 + <Checkbox 353 + small 354 + checked={filter.drafts} 355 + onChange={(e) => 356 + setState({ 357 + filter: { ...filter, drafts: !!e.target.checked }, 358 + }) 359 + } 360 + > 361 + Drafts 362 + </Checkbox> 363 + <Checkbox 364 + small 365 + checked={filter.published} 366 + onChange={(e) => 367 + setState({ 368 + filter: { ...filter, published: !!e.target.checked }, 369 + }) 370 + } 371 + > 372 + Published 373 + </Checkbox> 374 + </> 375 + )} 376 + 377 + {props.hasTemplates && ( 378 + <> 379 + <Checkbox 380 + small 381 + checked={filter.templates} 382 + onChange={(e) => 383 + setState({ 384 + filter: { ...filter, templates: !!e.target.checked }, 385 + }) 386 + } 387 + > 388 + Templates 389 + </Checkbox> 390 + </> 391 + )} 392 + <Checkbox 393 + small 394 + checked={filter.docs} 395 + onChange={(e) => 396 + setState({ 397 + filter: { ...filter, docs: !!e.target.checked }, 398 + }) 399 + } 400 + > 401 + Docs 402 + </Checkbox> 403 + <hr className="border-border-light mt-1 mb-0.5" /> 404 + <button 405 + className="flex gap-1 items-center -mx-[2px] text-tertiary" 406 + onClick={() => { 407 + setState({ 408 + filter: { 409 + docs: false, 410 + published: false, 411 + drafts: false, 412 + templates: false, 413 + }, 414 + }); 415 + }} 416 + > 417 + <CloseTiny className="scale-75" /> Clear 418 + </button> 419 + </Popover> 420 + ); 421 + }; 422 + 423 + const SearchInput = (props: { 424 + searchValue: string; 425 + setSearchValue: (searchValue: string) => void; 426 + hasBackgroundImage: boolean; 427 + }) => { 428 + return ( 429 + <div className="relative grow shrink-0"> 430 + <Input 431 + className={`dashboardSearchInput 432 + appearance-none! outline-hidden! 433 + w-full min-w-0 text-primary relative pl-7 pr-1 -my-px 434 + border rounded-md border-transparent focus-within:border-border 435 + bg-transparent ${props.hasBackgroundImage ? "focus-within:bg-bg-page" : "focus-within:bg-bg-leaflet"} `} 436 + type="text" 437 + id="pubName" 438 + size={1} 439 + placeholder="search..." 440 + value={props.searchValue} 441 + onChange={(e) => { 442 + props.setSearchValue(e.currentTarget.value); 443 + }} 444 + /> 445 + <div className="absolute left-[6px] top-[4px] text-tertiary"> 446 + <SearchTiny /> 447 + </div> 448 + </div> 449 + ); 450 + };
+9
components/PageLayouts/NotFoundLayout.tsx
···
··· 1 + export const NotFoundLayout = (props: { children: React.ReactNode }) => { 2 + return ( 3 + <div className="w-screen h-full flex place-items-center bg-bg-leaflet p-4"> 4 + <div className="bg-bg-page mx-auto p-4 border border-border rounded-md flex flex-col text-center justify-center gap-1 w-fit"> 5 + {props.children} 6 + </div> 7 + </div> 8 + ); 9 + };
+7 -24
components/Pages/PublicationMetadata.tsx
··· 18 } from "app/lish/createPub/getPublicationURL"; 19 import { useSubscribe } from "src/replicache/useSubscribe"; 20 import { useEntitySetContext } from "components/EntitySetProvider"; 21 export const PublicationMetadata = ({ 22 cardBorderHidden, 23 }: { ··· 57 </div> 58 <AsyncValueAutosizeTextarea 59 disabled={!permissions.write} 60 - className="text-xl font-bold outline-none bg-transparent" 61 value={title} 62 onChange={async (e) => { 63 await rep?.mutate.updatePublicationDraft({ ··· 70 <AsyncValueAutosizeTextarea 71 disabled={!permissions.write} 72 placeholder="add an optional description..." 73 - className="italic text-secondary outline-none bg-transparent" 74 value={description} 75 onChange={async (e) => { 76 await rep?.mutate.updatePublicationDraft({ ··· 82 {pub.doc ? ( 83 <div className="flex flex-row items-center gap-2 pt-3"> 84 <p className="text-sm text-tertiary"> 85 - Published{" "} 86 - {publishedAt && 87 - new Date(publishedAt).toLocaleString(undefined, { 88 - year: "numeric", 89 - month: "2-digit", 90 - day: "2-digit", 91 - hour: "2-digit", 92 - minute: "2-digit", 93 - hour12: true, 94 - })} 95 </p> 96 <Separator classname="h-4" /> 97 <Link ··· 123 </div> 124 125 <div 126 - className={`text-xl font-bold outline-none bg-transparent ${!pub.title && "text-tertiary italic"}`} 127 > 128 {pub.title ? pub.title : "Untitled"} 129 </div> 130 - <div className="italic text-secondary outline-none bg-transparent"> 131 {pub.description} 132 </div> 133 134 {pub.doc ? ( 135 <div className="flex flex-row items-center gap-2 pt-3"> 136 <p className="text-sm text-tertiary"> 137 - Published{" "} 138 - {publishedAt && 139 - new Date(publishedAt).toLocaleString(undefined, { 140 - year: "numeric", 141 - month: "2-digit", 142 - day: "2-digit", 143 - hour: "2-digit", 144 - minute: "2-digit", 145 - hour12: true, 146 - })} 147 </p> 148 </div> 149 ) : (
··· 18 } from "app/lish/createPub/getPublicationURL"; 19 import { useSubscribe } from "src/replicache/useSubscribe"; 20 import { useEntitySetContext } from "components/EntitySetProvider"; 21 + import { timeAgo } from "src/utils/timeAgo"; 22 export const PublicationMetadata = ({ 23 cardBorderHidden, 24 }: { ··· 58 </div> 59 <AsyncValueAutosizeTextarea 60 disabled={!permissions.write} 61 + className="text-xl font-bold outline-hidden bg-transparent" 62 value={title} 63 onChange={async (e) => { 64 await rep?.mutate.updatePublicationDraft({ ··· 71 <AsyncValueAutosizeTextarea 72 disabled={!permissions.write} 73 placeholder="add an optional description..." 74 + className="italic text-secondary outline-hidden bg-transparent" 75 value={description} 76 onChange={async (e) => { 77 await rep?.mutate.updatePublicationDraft({ ··· 83 {pub.doc ? ( 84 <div className="flex flex-row items-center gap-2 pt-3"> 85 <p className="text-sm text-tertiary"> 86 + Published {publishedAt && timeAgo(publishedAt)} 87 </p> 88 <Separator classname="h-4" /> 89 <Link ··· 115 </div> 116 117 <div 118 + className={`text-xl font-bold outline-hidden bg-transparent ${!pub.title && "text-tertiary italic"}`} 119 > 120 {pub.title ? pub.title : "Untitled"} 121 </div> 122 + <div className="italic text-secondary outline-hidden bg-transparent"> 123 {pub.description} 124 </div> 125 126 {pub.doc ? ( 127 <div className="flex flex-row items-center gap-2 pt-3"> 128 <p className="text-sm text-tertiary"> 129 + Published {publishedAt && timeAgo(publishedAt)} 130 </p> 131 </div> 132 ) : (
+4 -2
components/Popover.tsx
··· 17 className?: string; 18 open?: boolean; 19 onOpenChange?: (open: boolean) => void; 20 asChild?: boolean; 21 arrowFill?: string; 22 }) => { ··· 39 className={` 40 z-20 bg-bg-page 41 px-3 py-2 42 - max-w-[var(--radix-popover-content-available-width)] 43 - max-h-[var(--radix-popover-content-available-height)] 44 border border-border rounded-md shadow-md 45 overflow-y-scroll no-scrollbar 46 ${props.className} ··· 49 align={props.align ? props.align : "center"} 50 sideOffset={4} 51 collisionPadding={16} 52 > 53 {props.children} 54 <RadixPopover.Arrow
··· 17 className?: string; 18 open?: boolean; 19 onOpenChange?: (open: boolean) => void; 20 + onOpenAutoFocus?: (e: Event) => void; 21 asChild?: boolean; 22 arrowFill?: string; 23 }) => { ··· 40 className={` 41 z-20 bg-bg-page 42 px-3 py-2 43 + max-w-(--radix-popover-content-available-width) 44 + max-h-(--radix-popover-content-available-height) 45 border border-border rounded-md shadow-md 46 overflow-y-scroll no-scrollbar 47 ${props.className} ··· 50 align={props.align ? props.align : "center"} 51 sideOffset={4} 52 collisionPadding={16} 53 + onOpenAutoFocus={props.onOpenAutoFocus} 54 > 55 {props.children} 56 <RadixPopover.Arrow
+1 -1
components/ShareOptions/DomainOptions.tsx
··· 234 <Input 235 type="text" 236 autoFocus 237 - className="appearance-none focus:outline-none font-normal text-accent-2 w-full bg-transparent placeholder:text-accent-2 placeholder:opacity-50" 238 placeholder="add-optional-path" 239 onChange={(e) => props.setSelectedRoute(e.target.value)} 240 value={props.selectedRoute}
··· 234 <Input 235 type="text" 236 autoFocus 237 + className="appearance-none focus:outline-hidden font-normal text-accent-2 w-full bg-transparent placeholder:text-accent-2 placeholder:opacity-50" 238 placeholder="add-optional-path" 239 onChange={(e) => props.setSelectedRoute(e.target.value)} 240 value={props.selectedRoute}
+1 -1
components/ShareOptions/index.tsx
··· 6 import { Menu, MenuItem } from "components/Layout"; 7 import { ActionButton } from "components/ActionBar/ActionButton"; 8 import useSWR from "swr"; 9 - import { useTemplateState } from "app/home/CreateNewButton"; 10 import LoginForm from "app/login/LoginForm"; 11 import { CustomDomainMenu } from "./DomainOptions"; 12 import { useIdentityData } from "components/IdentityProvider";
··· 6 import { Menu, MenuItem } from "components/Layout"; 7 import { ActionButton } from "components/ActionBar/ActionButton"; 8 import useSWR from "swr"; 9 + import { useTemplateState } from "app/home/Actions/CreateNewButton"; 10 import LoginForm from "app/login/LoginForm"; 11 import { CustomDomainMenu } from "./DomainOptions"; 12 import { useIdentityData } from "components/IdentityProvider";
+3 -3
components/ThemeManager/Pickers/ColorPicker.tsx
··· 17 import { onMouseDown } from "src/utils/iosInputMouseDown"; 18 19 export let thumbStyle = 20 - "w-4 h-4 rounded-full border-2 border-white shadow-[0_0_0_1px_#8C8C8C,_inset_0_0_0_1px_#8C8C8C]"; 21 22 export const ColorPicker = (props: { 23 label?: string; ··· 80 onBlur={(e) => { 81 props.setValue(parseColor(e.currentTarget.value)); 82 }} 83 - className="w-[72px] bg-transparent outline-none disabled:text-tertiary" 84 /> 85 </ColorField> 86 )} ··· 105 e.currentTarget.blur(); 106 } else return; 107 }} 108 - className="w-[72px] bg-transparent outline-none " 109 /> 110 </ColorField> 111 </>
··· 17 import { onMouseDown } from "src/utils/iosInputMouseDown"; 18 19 export let thumbStyle = 20 + "w-4 h-4 rounded-full border-2 border-white shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]"; 21 22 export const ColorPicker = (props: { 23 label?: string; ··· 80 onBlur={(e) => { 81 props.setValue(parseColor(e.currentTarget.value)); 82 }} 83 + className="w-[72px] bg-transparent outline-hidden disabled:text-tertiary" 84 /> 85 </ColorField> 86 )} ··· 105 e.currentTarget.blur(); 106 } else return; 107 }} 108 + className="w-[72px] bg-transparent outline-hidden " 109 /> 110 </ColorField> 111 </>
+5 -5
components/ThemeManager/Pickers/ImagePicker.tsx
··· 32 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 33 <label htmlFor="cover" className="w-full"> 34 <Radio 35 - radioCheckedClassName="!text-[#595959]" 36 - radioEmptyClassName="!text-[#969696]" 37 type="radio" 38 id="cover" 39 name="bg-image-options" ··· 58 id="repeat" 59 name="bg-image-options" 60 value="repeat" 61 - radioCheckedClassName="!text-[#595959]" 62 - radioEmptyClassName="!text-[#969696]" 63 checked={!!repeat} 64 onChange={async (e) => { 65 if (!e.currentTarget.checked) return; ··· 128 className={` 129 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 130 ${repeat ? "bg-[#595959]" : " bg-[#C3C3C3] "} 131 - ${repeat && "shadow-[0_0_0_1px_#8C8C8C,_inset_0_0_0_1px_#8C8C8C]"} `} 132 aria-label="Volume" 133 /> 134 </Slider.Root>
··· 32 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 33 <label htmlFor="cover" className="w-full"> 34 <Radio 35 + radioCheckedClassName="text-[#595959]!" 36 + radioEmptyClassName="text-[#969696]!" 37 type="radio" 38 id="cover" 39 name="bg-image-options" ··· 58 id="repeat" 59 name="bg-image-options" 60 value="repeat" 61 + radioCheckedClassName="text-[#595959]!" 62 + radioEmptyClassName="text-[#969696]!" 63 checked={!!repeat} 64 onChange={async (e) => { 65 if (!e.currentTarget.checked) return; ··· 128 className={` 129 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 130 ${repeat ? "bg-[#595959]" : " bg-[#C3C3C3] "} 131 + ${repeat && "shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]"} `} 132 aria-label="Volume" 133 /> 134 </Slider.Root>
+5 -5
components/ThemeManager/Pickers/PageThemePickers.tsx
··· 213 }); 214 }} 215 > 216 - <Separator classname="!h-4 my-1 !border-[#C3C3C3]" /> 217 <ColorField className="w-fit pl-[6px]" channel="alpha"> 218 <Input 219 disabled={props.disabled} ··· 229 e.currentTarget.blur(); 230 } else return; 231 }} 232 - className={`w-[48px] bg-transparent outline-none disabled:text-[#969696]`} 233 /> 234 </ColorField> 235 </SpectrumColorPicker> ··· 305 return ( 306 <div className="flex gap-2 h-8 "> 307 <button 308 - className={`w-full rounded-md bg-bg-page border ${selectedPattern === "grid" ? "outline outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 309 onMouseDown={() => { 310 props.rep && 311 props.rep.mutate.assertFact({ ··· 318 <CanvasBackgroundPattern pattern="grid" scale={0.5} /> 319 </button> 320 <button 321 - className={`w-full rounded-md bg-bg-page border ${selectedPattern === "dot" ? "outline outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 322 onMouseDown={() => { 323 props.rep && 324 props.rep.mutate.assertFact({ ··· 331 <CanvasBackgroundPattern pattern="dot" scale={0.5} /> 332 </button> 333 <button 334 - className={`w-full rounded-md bg-bg-page border ${selectedPattern === "plain" ? "outline outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 335 onMouseDown={() => { 336 props.rep && 337 props.rep.mutate.assertFact({
··· 213 }); 214 }} 215 > 216 + <Separator classname="h-4! my-1 border-[#C3C3C3]!" /> 217 <ColorField className="w-fit pl-[6px]" channel="alpha"> 218 <Input 219 disabled={props.disabled} ··· 229 e.currentTarget.blur(); 230 } else return; 231 }} 232 + className={`w-[48px] bg-transparent outline-hidden disabled:text-[#969696]`} 233 /> 234 </ColorField> 235 </SpectrumColorPicker> ··· 305 return ( 306 <div className="flex gap-2 h-8 "> 307 <button 308 + className={`w-full rounded-md bg-bg-page border ${selectedPattern === "grid" ? "outline-solid outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 309 onMouseDown={() => { 310 props.rep && 311 props.rep.mutate.assertFact({ ··· 318 <CanvasBackgroundPattern pattern="grid" scale={0.5} /> 319 </button> 320 <button 321 + className={`w-full rounded-md bg-bg-page border ${selectedPattern === "dot" ? "outline-solid outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 322 onMouseDown={() => { 323 props.rep && 324 props.rep.mutate.assertFact({ ··· 331 <CanvasBackgroundPattern pattern="dot" scale={0.5} /> 332 </button> 333 <button 334 + className={`w-full rounded-md bg-bg-page border ${selectedPattern === "plain" ? "outline-solid outline-tertiary border-tertiary" : "transparent-outline hover:outline-border border-border "}`} 335 onMouseDown={() => { 336 props.rep && 337 props.rep.mutate.assertFact({
+5 -5
components/ThemeManager/PubPickers/PubBackgroundPickers.tsx
··· 216 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 217 <label htmlFor="cover" className="w-full"> 218 <Radio 219 - radioCheckedClassName="!text-[#595959]" 220 - radioEmptyClassName="!text-[#969696]" 221 type="radio" 222 id="cover" 223 name="bg-image-options" ··· 242 id="repeat" 243 name="bg-image-options" 244 value="repeat" 245 - radioCheckedClassName="!text-[#595959]" 246 - radioEmptyClassName="!text-[#969696]" 247 checked={!!props.bgImage?.repeat} 248 onChange={async (e) => { 249 if (!e.currentTarget.checked) return; ··· 296 className={` 297 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 298 ${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3] "} 299 - ${props.bgImage?.repeat && "shadow-[0_0_0_1px_#8C8C8C,_inset_0_0_0_1px_#8C8C8C]"} `} 300 aria-label="Volume" 301 /> 302 </Slider.Root>
··· 216 <div className="themeBGImageControls font-bold flex flex-col gap-1 items-center px-3"> 217 <label htmlFor="cover" className="w-full"> 218 <Radio 219 + radioCheckedClassName="text-[#595959]!" 220 + radioEmptyClassName="text-[#969696]!" 221 type="radio" 222 id="cover" 223 name="bg-image-options" ··· 242 id="repeat" 243 name="bg-image-options" 244 value="repeat" 245 + radioCheckedClassName="text-[#595959]!" 246 + radioEmptyClassName="text-[#969696]!" 247 checked={!!props.bgImage?.repeat} 248 onChange={async (e) => { 249 if (!e.currentTarget.checked) return; ··· 296 className={` 297 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 298 ${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3] "} 299 + ${props.bgImage?.repeat && "shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]"} `} 300 aria-label="Volume" 301 /> 302 </Slider.Root>
+10 -17
components/ThemeManager/PubThemeSetter.tsx
··· 27 let [loading, setLoading] = useState(false); 28 let [sample, setSample] = useState<"pub" | "post">("pub"); 29 let [openPicker, setOpenPicker] = useState<pickers>("null"); 30 - let { data: pub, mutate } = usePublicationData(); 31 let record = pub?.record as PubLeafletPublication.Record | undefined; 32 let [showPageBackground, setShowPageBackground] = useState( 33 !!record?.theme?.showPageBackground, ··· 87 }} 88 > 89 <h4 className="text-accent-2">Publication Theme</h4> 90 - <ButtonSecondary 91 - compact 92 - disabled={ 93 - !( 94 - showPageBackground !== !!record?.theme?.showPageBackground || 95 - changes || 96 - !!image?.file || 97 - record?.theme?.backgroundImage?.width !== image?.repeat 98 - ) 99 - } 100 - > 101 {loading ? <DotLoader /> : "Update"} 102 </ButtonSecondary> 103 </form> ··· 130 <SectionArrow 131 fill="white" 132 stroke="#CCCCCC" 133 - className="ml-2 -mt-[1px]" 134 /> 135 </div> 136 </div> ··· 177 <div className="flex flex-col mt-4 "> 178 <div className="flex gap-2 items-center text-sm text-[#8C8C8C]"> 179 <div className="text-sm">Preview</div> 180 - <Separator classname="!h-4" />{" "} 181 <button 182 className={`${sample === "pub" ? "font-bold text-[#595959]" : ""}`} 183 onClick={() => setSample("pub")} ··· 216 pubBGRepeat: number | null; 217 showPageBackground: boolean; 218 }) => { 219 - let { data: publication } = usePublicationData(); 220 let record = publication?.record as PubLeafletPublication.Record | null; 221 222 return ( ··· 260 <div className="text-[7px] font-normal text-tertiary"> 261 {record?.description} 262 </div> 263 - <div className=" flex gap-1 items-center mt-[6px] bg-accent-1 text-accent-2 py-[1px] px-[4px] text-[7px] w-fit font-bold rounded-[2px] mx-auto"> 264 <div className="h-[7px] w-[7px] rounded-full bg-accent-2" /> 265 Subscribe with Bluesky 266 </div> ··· 283 pubBGRepeat: number | null; 284 showPageBackground: boolean; 285 }) => { 286 - let { data: publication } = usePublicationData(); 287 let record = publication?.record as PubLeafletPublication.Record | null; 288 return ( 289 <div
··· 27 let [loading, setLoading] = useState(false); 28 let [sample, setSample] = useState<"pub" | "post">("pub"); 29 let [openPicker, setOpenPicker] = useState<pickers>("null"); 30 + let { data, mutate } = usePublicationData(); 31 + let { publication: pub } = data || {}; 32 let record = pub?.record as PubLeafletPublication.Record | undefined; 33 let [showPageBackground, setShowPageBackground] = useState( 34 !!record?.theme?.showPageBackground, ··· 88 }} 89 > 90 <h4 className="text-accent-2">Publication Theme</h4> 91 + <ButtonSecondary compact> 92 {loading ? <DotLoader /> : "Update"} 93 </ButtonSecondary> 94 </form> ··· 121 <SectionArrow 122 fill="white" 123 stroke="#CCCCCC" 124 + className="ml-2 -mt-px" 125 /> 126 </div> 127 </div> ··· 168 <div className="flex flex-col mt-4 "> 169 <div className="flex gap-2 items-center text-sm text-[#8C8C8C]"> 170 <div className="text-sm">Preview</div> 171 + <Separator classname="h-4!" />{" "} 172 <button 173 className={`${sample === "pub" ? "font-bold text-[#595959]" : ""}`} 174 onClick={() => setSample("pub")} ··· 207 pubBGRepeat: number | null; 208 showPageBackground: boolean; 209 }) => { 210 + let { data } = usePublicationData(); 211 + let { publication } = data || {}; 212 let record = publication?.record as PubLeafletPublication.Record | null; 213 214 return ( ··· 252 <div className="text-[7px] font-normal text-tertiary"> 253 {record?.description} 254 </div> 255 + <div className=" flex gap-1 items-center mt-[6px] bg-accent-1 text-accent-2 py-px px-[4px] text-[7px] w-fit font-bold rounded-[2px] mx-auto"> 256 <div className="h-[7px] w-[7px] rounded-full bg-accent-2" /> 257 Subscribe with Bluesky 258 </div> ··· 275 pubBGRepeat: number | null; 276 showPageBackground: boolean; 277 }) => { 278 + let { data } = usePublicationData(); 279 + let { publication } = data || {}; 280 let record = publication?.record as PubLeafletPublication.Record | null; 281 return ( 282 <div
+2 -1
components/ThemeManager/PublicationThemeProvider.tsx
··· 49 children: React.ReactNode; 50 record?: PubLeafletPublication.Record | null; 51 }) { 52 - let { data: pub } = usePublicationData(); 53 return ( 54 <PublicationThemeProvider 55 pub_creator={pub?.identity_did || ""}
··· 49 children: React.ReactNode; 50 record?: PubLeafletPublication.Record | null; 51 }) { 52 + let { data } = usePublicationData(); 53 + let { publication: pub } = data || {}; 54 return ( 55 <PublicationThemeProvider 56 pub_creator={pub?.identity_did || ""}
+1 -1
components/ThemeManager/ThemeSetter.tsx
··· 114 <SectionArrow 115 fill="white" 116 stroke="#CCCCCC" 117 - className="ml-2 -mt-[1px]" 118 /> 119 </div> 120 </div>
··· 114 <SectionArrow 115 fill="white" 116 stroke="#CCCCCC" 117 + className="ml-2 -mt-px" 118 /> 119 </div> 120 </div>
+1 -1
components/Toolbar/InlineLinkToolbar.tsx
··· 135 <Separator classname="h-6" /> 136 <Input 137 autoFocus 138 - className="w-full grow bg-transparent border-none outline-none " 139 placeholder="www.example.com" 140 value={linkValue} 141 onChange={(e) => setLinkValue(e.target.value)}
··· 135 <Separator classname="h-6" /> 136 <Input 137 autoFocus 138 + className="w-full grow bg-transparent border-none outline-hidden " 139 placeholder="www.example.com" 140 value={linkValue} 141 onChange={(e) => setLinkValue(e.target.value)}
+68
components/Tooltip.tsx
···
··· 1 + import * as RadixTooltip from "@radix-ui/react-tooltip"; 2 + import { PopoverArrow } from "./Icons/PopoverArrow"; 3 + import { theme } from "tailwind.config"; 4 + import { NestedCardThemeProvider } from "./ThemeManager/ThemeProvider"; 5 + 6 + export const Tooltip = (props: { 7 + trigger: React.ReactNode; 8 + disabled?: boolean; 9 + children: React.ReactNode; 10 + delayDuration?: number; 11 + skipDelayDuration?: number; 12 + align?: "start" | "end" | "center"; 13 + side?: "top" | "bottom" | "left" | "right"; 14 + background?: string; 15 + border?: string; 16 + className?: string; 17 + open?: boolean; 18 + onOpenChange?: (open: boolean) => void; 19 + asChild?: boolean; 20 + arrowFill?: string; 21 + }) => { 22 + return ( 23 + <RadixTooltip.Provider 24 + delayDuration={props.delayDuration ? props.delayDuration : 600} 25 + skipDelayDuration={ 26 + props.skipDelayDuration ? props.skipDelayDuration : 300 27 + } 28 + > 29 + <RadixTooltip.Root> 30 + <RadixTooltip.Trigger disabled={props.disabled} asChild={props.asChild}> 31 + {props.trigger} 32 + </RadixTooltip.Trigger> 33 + <RadixTooltip.Portal> 34 + <NestedCardThemeProvider> 35 + <RadixTooltip.Content 36 + className={` 37 + z-20 bg-bg-page 38 + px-3 py-2 39 + max-w-(--radix-popover-content-available-width) 40 + max-h-(--radix-popover-content-available-height) 41 + border border-border rounded-md shadow-md 42 + overflow-y-scroll no-scrollbar 43 + ${props.className} 44 + `} 45 + side={props.side} 46 + align={props.align ? props.align : "center"} 47 + sideOffset={4} 48 + collisionPadding={16} 49 + > 50 + {props.children} 51 + <RadixTooltip.Arrow 52 + asChild 53 + width={16} 54 + height={8} 55 + viewBox="0 0 16 8" 56 + > 57 + <PopoverArrow 58 + arrowFill={theme.colors["border"]} 59 + arrowStroke="transparent" 60 + /> 61 + </RadixTooltip.Arrow> 62 + </RadixTooltip.Content> 63 + </NestedCardThemeProvider> 64 + </RadixTooltip.Portal> 65 + </RadixTooltip.Root> 66 + </RadixTooltip.Provider> 67 + ); 68 + };
+1
drizzle/schema.ts
··· 101 home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 102 email: text("email"), 103 atp_did: text("atp_did"), 104 }, 105 (table) => { 106 return {
··· 101 home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 102 email: text("email"), 103 atp_did: text("atp_did"), 104 + interface_state: jsonb("interface_state"), 105 }, 106 (table) => { 107 return {
+374 -280
lexicons/api/index.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { XrpcClient, FetchHandler, FetchHandlerOptions } from '@atproto/xrpc' 5 import { schemas } from './lexicons' 6 import { CID } from 'multiformats/cid' 7 - import { OmitKey, Un$Typed } from './util' 8 - import * as PubLeafletComment from './types/pub/leaflet/comment' 9 - import * as PubLeafletDocument from './types/pub/leaflet/document' 10 - import * as PubLeafletPublication from './types/pub/leaflet/publication' 11 import * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote' 12 import * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost' 13 import * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code' ··· 20 import * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' 21 import * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 22 import * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 23 import * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 24 import * as PubLeafletPagesLinearDocument from './types/pub/leaflet/pages/linearDocument' 25 import * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 26 import * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 27 import * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 28 - import * as ComAtprotoLabelDefs from './types/com/atproto/label/defs' 29 - import * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites' 30 - import * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord' 31 - import * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs' 32 - import * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord' 33 - import * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo' 34 - import * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord' 35 - import * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo' 36 - import * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs' 37 - import * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords' 38 - import * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord' 39 - import * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef' 40 - import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob' 41 - import * as AppBskyActorProfile from './types/app/bsky/actor/profile' 42 43 - export * as PubLeafletComment from './types/pub/leaflet/comment' 44 - export * as PubLeafletDocument from './types/pub/leaflet/document' 45 - export * as PubLeafletPublication from './types/pub/leaflet/publication' 46 export * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote' 47 export * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost' 48 export * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code' ··· 55 export * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' 56 export * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 57 export * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 58 export * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 59 export * as PubLeafletPagesLinearDocument from './types/pub/leaflet/pages/linearDocument' 60 export * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 61 export * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 62 export * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 63 - export * as ComAtprotoLabelDefs from './types/com/atproto/label/defs' 64 - export * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites' 65 - export * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord' 66 - export * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs' 67 - export * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord' 68 - export * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo' 69 - export * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord' 70 - export * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo' 71 - export * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs' 72 - export * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords' 73 - export * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord' 74 - export * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef' 75 - export * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob' 76 - export * as AppBskyActorProfile from './types/app/bsky/actor/profile' 77 78 export const PUB_LEAFLET_PAGES = { 79 LinearDocumentTextAlignLeft: 'pub.leaflet.pages.linearDocument#textAlignLeft', ··· 84 } 85 86 export class AtpBaseClient extends XrpcClient { 87 - pub: PubNS 88 com: ComNS 89 - app: AppNS 90 91 constructor(options: FetchHandler | FetchHandlerOptions) { 92 super(options, schemas) 93 - this.pub = new PubNS(this) 94 - this.com = new ComNS(this) 95 this.app = new AppNS(this) 96 } 97 98 /** @deprecated use `this` instead */ ··· 101 } 102 } 103 104 export class PubNS { 105 _client: XrpcClient 106 leaflet: PubLeafletNS ··· 113 114 export class PubLeafletNS { 115 _client: XrpcClient 116 - comment: CommentRecord 117 - document: DocumentRecord 118 - publication: PublicationRecord 119 blocks: PubLeafletBlocksNS 120 graph: PubLeafletGraphNS 121 pages: PubLeafletPagesNS ··· 129 this.pages = new PubLeafletPagesNS(client) 130 this.richtext = new PubLeafletRichtextNS(client) 131 this.theme = new PubLeafletThemeNS(client) 132 - this.comment = new CommentRecord(client) 133 - this.document = new DocumentRecord(client) 134 - this.publication = new PublicationRecord(client) 135 } 136 } 137 ··· 145 146 export class PubLeafletGraphNS { 147 _client: XrpcClient 148 - subscription: SubscriptionRecord 149 150 constructor(client: XrpcClient) { 151 this._client = client 152 - this.subscription = new SubscriptionRecord(client) 153 } 154 } 155 156 - export class SubscriptionRecord { 157 _client: XrpcClient 158 159 constructor(client: XrpcClient) { ··· 205 return res.data 206 } 207 208 async delete( 209 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 210 headers?: Record<string, string>, ··· 242 } 243 } 244 245 - export class CommentRecord { 246 _client: XrpcClient 247 248 constructor(client: XrpcClient) { ··· 290 return res.data 291 } 292 293 async delete( 294 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 295 headers?: Record<string, string>, ··· 303 } 304 } 305 306 - export class DocumentRecord { 307 _client: XrpcClient 308 309 constructor(client: XrpcClient) { ··· 351 return res.data 352 } 353 354 async delete( 355 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 356 headers?: Record<string, string>, ··· 364 } 365 } 366 367 - export class PublicationRecord { 368 _client: XrpcClient 369 370 constructor(client: XrpcClient) { ··· 416 return res.data 417 } 418 419 - async delete( 420 - params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 421 - headers?: Record<string, string>, 422 - ): Promise<void> { 423 - await this._client.call( 424 - 'com.atproto.repo.deleteRecord', 425 - undefined, 426 - { collection: 'pub.leaflet.publication', ...params }, 427 - { headers }, 428 - ) 429 - } 430 - } 431 - 432 - export class ComNS { 433 - _client: XrpcClient 434 - atproto: ComAtprotoNS 435 - 436 - constructor(client: XrpcClient) { 437 - this._client = client 438 - this.atproto = new ComAtprotoNS(client) 439 - } 440 - } 441 - 442 - export class ComAtprotoNS { 443 - _client: XrpcClient 444 - repo: ComAtprotoRepoNS 445 - 446 - constructor(client: XrpcClient) { 447 - this._client = client 448 - this.repo = new ComAtprotoRepoNS(client) 449 - } 450 - } 451 - 452 - export class ComAtprotoRepoNS { 453 - _client: XrpcClient 454 - 455 - constructor(client: XrpcClient) { 456 - this._client = client 457 - } 458 - 459 - applyWrites( 460 - data?: ComAtprotoRepoApplyWrites.InputSchema, 461 - opts?: ComAtprotoRepoApplyWrites.CallOptions, 462 - ): Promise<ComAtprotoRepoApplyWrites.Response> { 463 - return this._client 464 - .call('com.atproto.repo.applyWrites', opts?.qp, data, opts) 465 - .catch((e) => { 466 - throw ComAtprotoRepoApplyWrites.toKnownErr(e) 467 - }) 468 - } 469 - 470 - createRecord( 471 - data?: ComAtprotoRepoCreateRecord.InputSchema, 472 - opts?: ComAtprotoRepoCreateRecord.CallOptions, 473 - ): Promise<ComAtprotoRepoCreateRecord.Response> { 474 - return this._client 475 - .call('com.atproto.repo.createRecord', opts?.qp, data, opts) 476 - .catch((e) => { 477 - throw ComAtprotoRepoCreateRecord.toKnownErr(e) 478 - }) 479 - } 480 - 481 - deleteRecord( 482 - data?: ComAtprotoRepoDeleteRecord.InputSchema, 483 - opts?: ComAtprotoRepoDeleteRecord.CallOptions, 484 - ): Promise<ComAtprotoRepoDeleteRecord.Response> { 485 - return this._client 486 - .call('com.atproto.repo.deleteRecord', opts?.qp, data, opts) 487 - .catch((e) => { 488 - throw ComAtprotoRepoDeleteRecord.toKnownErr(e) 489 - }) 490 - } 491 - 492 - describeRepo( 493 - params?: ComAtprotoRepoDescribeRepo.QueryParams, 494 - opts?: ComAtprotoRepoDescribeRepo.CallOptions, 495 - ): Promise<ComAtprotoRepoDescribeRepo.Response> { 496 - return this._client.call( 497 - 'com.atproto.repo.describeRepo', 498 - params, 499 - undefined, 500 - opts, 501 - ) 502 - } 503 - 504 - getRecord( 505 - params?: ComAtprotoRepoGetRecord.QueryParams, 506 - opts?: ComAtprotoRepoGetRecord.CallOptions, 507 - ): Promise<ComAtprotoRepoGetRecord.Response> { 508 - return this._client 509 - .call('com.atproto.repo.getRecord', params, undefined, opts) 510 - .catch((e) => { 511 - throw ComAtprotoRepoGetRecord.toKnownErr(e) 512 - }) 513 - } 514 - 515 - importRepo( 516 - data?: ComAtprotoRepoImportRepo.InputSchema, 517 - opts?: ComAtprotoRepoImportRepo.CallOptions, 518 - ): Promise<ComAtprotoRepoImportRepo.Response> { 519 - return this._client.call( 520 - 'com.atproto.repo.importRepo', 521 - opts?.qp, 522 - data, 523 - opts, 524 - ) 525 - } 526 - 527 - listMissingBlobs( 528 - params?: ComAtprotoRepoListMissingBlobs.QueryParams, 529 - opts?: ComAtprotoRepoListMissingBlobs.CallOptions, 530 - ): Promise<ComAtprotoRepoListMissingBlobs.Response> { 531 - return this._client.call( 532 - 'com.atproto.repo.listMissingBlobs', 533 - params, 534 - undefined, 535 - opts, 536 - ) 537 - } 538 - 539 - listRecords( 540 - params?: ComAtprotoRepoListRecords.QueryParams, 541 - opts?: ComAtprotoRepoListRecords.CallOptions, 542 - ): Promise<ComAtprotoRepoListRecords.Response> { 543 - return this._client.call( 544 - 'com.atproto.repo.listRecords', 545 - params, 546 - undefined, 547 - opts, 548 - ) 549 - } 550 - 551 - putRecord( 552 - data?: ComAtprotoRepoPutRecord.InputSchema, 553 - opts?: ComAtprotoRepoPutRecord.CallOptions, 554 - ): Promise<ComAtprotoRepoPutRecord.Response> { 555 - return this._client 556 - .call('com.atproto.repo.putRecord', opts?.qp, data, opts) 557 - .catch((e) => { 558 - throw ComAtprotoRepoPutRecord.toKnownErr(e) 559 - }) 560 - } 561 - 562 - uploadBlob( 563 - data?: ComAtprotoRepoUploadBlob.InputSchema, 564 - opts?: ComAtprotoRepoUploadBlob.CallOptions, 565 - ): Promise<ComAtprotoRepoUploadBlob.Response> { 566 - return this._client.call( 567 - 'com.atproto.repo.uploadBlob', 568 - opts?.qp, 569 - data, 570 - opts, 571 - ) 572 - } 573 - } 574 - 575 - export class AppNS { 576 - _client: XrpcClient 577 - bsky: AppBskyNS 578 - 579 - constructor(client: XrpcClient) { 580 - this._client = client 581 - this.bsky = new AppBskyNS(client) 582 - } 583 - } 584 - 585 - export class AppBskyNS { 586 - _client: XrpcClient 587 - actor: AppBskyActorNS 588 - 589 - constructor(client: XrpcClient) { 590 - this._client = client 591 - this.actor = new AppBskyActorNS(client) 592 - } 593 - } 594 - 595 - export class AppBskyActorNS { 596 - _client: XrpcClient 597 - profile: ProfileRecord 598 - 599 - constructor(client: XrpcClient) { 600 - this._client = client 601 - this.profile = new ProfileRecord(client) 602 - } 603 - } 604 - 605 - export class ProfileRecord { 606 - _client: XrpcClient 607 - 608 - constructor(client: XrpcClient) { 609 - this._client = client 610 - } 611 - 612 - async list( 613 - params: OmitKey<ComAtprotoRepoListRecords.QueryParams, 'collection'>, 614 - ): Promise<{ 615 - cursor?: string 616 - records: { uri: string; value: AppBskyActorProfile.Record }[] 617 - }> { 618 - const res = await this._client.call('com.atproto.repo.listRecords', { 619 - collection: 'app.bsky.actor.profile', 620 - ...params, 621 - }) 622 - return res.data 623 - } 624 - 625 - async get( 626 - params: OmitKey<ComAtprotoRepoGetRecord.QueryParams, 'collection'>, 627 - ): Promise<{ uri: string; cid: string; value: AppBskyActorProfile.Record }> { 628 - const res = await this._client.call('com.atproto.repo.getRecord', { 629 - collection: 'app.bsky.actor.profile', 630 - ...params, 631 - }) 632 - return res.data 633 - } 634 - 635 - async create( 636 params: OmitKey< 637 - ComAtprotoRepoCreateRecord.InputSchema, 638 'collection' | 'record' 639 >, 640 - record: Un$Typed<AppBskyActorProfile.Record>, 641 headers?: Record<string, string>, 642 ): Promise<{ uri: string; cid: string }> { 643 - const collection = 'app.bsky.actor.profile' 644 const res = await this._client.call( 645 - 'com.atproto.repo.createRecord', 646 undefined, 647 - { 648 - collection, 649 - rkey: 'self', 650 - ...params, 651 - record: { ...record, $type: collection }, 652 - }, 653 { encoding: 'application/json', headers }, 654 ) 655 return res.data ··· 662 await this._client.call( 663 'com.atproto.repo.deleteRecord', 664 undefined, 665 - { collection: 'app.bsky.actor.profile', ...params }, 666 { headers }, 667 ) 668 }
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { 5 + XrpcClient, 6 + type FetchHandler, 7 + type FetchHandlerOptions, 8 + } from '@atproto/xrpc' 9 import { schemas } from './lexicons' 10 import { CID } from 'multiformats/cid' 11 + import { type OmitKey, type Un$Typed } from './util' 12 + import * as AppBskyActorProfile from './types/app/bsky/actor/profile' 13 + import * as ComAtprotoLabelDefs from './types/com/atproto/label/defs' 14 + import * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites' 15 + import * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord' 16 + import * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs' 17 + import * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord' 18 + import * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo' 19 + import * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord' 20 + import * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo' 21 + import * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs' 22 + import * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords' 23 + import * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord' 24 + import * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef' 25 + import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob' 26 import * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote' 27 import * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost' 28 import * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code' ··· 35 import * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' 36 import * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 37 import * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 38 + import * as PubLeafletComment from './types/pub/leaflet/comment' 39 + import * as PubLeafletDocument from './types/pub/leaflet/document' 40 import * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 41 import * as PubLeafletPagesLinearDocument from './types/pub/leaflet/pages/linearDocument' 42 + import * as PubLeafletPublication from './types/pub/leaflet/publication' 43 import * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 44 import * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 45 import * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 46 47 + export * as AppBskyActorProfile from './types/app/bsky/actor/profile' 48 + export * as ComAtprotoLabelDefs from './types/com/atproto/label/defs' 49 + export * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites' 50 + export * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord' 51 + export * as ComAtprotoRepoDefs from './types/com/atproto/repo/defs' 52 + export * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord' 53 + export * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo' 54 + export * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord' 55 + export * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo' 56 + export * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs' 57 + export * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords' 58 + export * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord' 59 + export * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef' 60 + export * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob' 61 export * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote' 62 export * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost' 63 export * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code' ··· 70 export * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' 71 export * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 72 export * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 73 + export * as PubLeafletComment from './types/pub/leaflet/comment' 74 + export * as PubLeafletDocument from './types/pub/leaflet/document' 75 export * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 76 export * as PubLeafletPagesLinearDocument from './types/pub/leaflet/pages/linearDocument' 77 + export * as PubLeafletPublication from './types/pub/leaflet/publication' 78 export * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 79 export * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 80 export * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 81 82 export const PUB_LEAFLET_PAGES = { 83 LinearDocumentTextAlignLeft: 'pub.leaflet.pages.linearDocument#textAlignLeft', ··· 88 } 89 90 export class AtpBaseClient extends XrpcClient { 91 + app: AppNS 92 com: ComNS 93 + pub: PubNS 94 95 constructor(options: FetchHandler | FetchHandlerOptions) { 96 super(options, schemas) 97 this.app = new AppNS(this) 98 + this.com = new ComNS(this) 99 + this.pub = new PubNS(this) 100 } 101 102 /** @deprecated use `this` instead */ ··· 105 } 106 } 107 108 + export class AppNS { 109 + _client: XrpcClient 110 + bsky: AppBskyNS 111 + 112 + constructor(client: XrpcClient) { 113 + this._client = client 114 + this.bsky = new AppBskyNS(client) 115 + } 116 + } 117 + 118 + export class AppBskyNS { 119 + _client: XrpcClient 120 + actor: AppBskyActorNS 121 + 122 + constructor(client: XrpcClient) { 123 + this._client = client 124 + this.actor = new AppBskyActorNS(client) 125 + } 126 + } 127 + 128 + export class AppBskyActorNS { 129 + _client: XrpcClient 130 + profile: AppBskyActorProfileRecord 131 + 132 + constructor(client: XrpcClient) { 133 + this._client = client 134 + this.profile = new AppBskyActorProfileRecord(client) 135 + } 136 + } 137 + 138 + export class AppBskyActorProfileRecord { 139 + _client: XrpcClient 140 + 141 + constructor(client: XrpcClient) { 142 + this._client = client 143 + } 144 + 145 + async list( 146 + params: OmitKey<ComAtprotoRepoListRecords.QueryParams, 'collection'>, 147 + ): Promise<{ 148 + cursor?: string 149 + records: { uri: string; value: AppBskyActorProfile.Record }[] 150 + }> { 151 + const res = await this._client.call('com.atproto.repo.listRecords', { 152 + collection: 'app.bsky.actor.profile', 153 + ...params, 154 + }) 155 + return res.data 156 + } 157 + 158 + async get( 159 + params: OmitKey<ComAtprotoRepoGetRecord.QueryParams, 'collection'>, 160 + ): Promise<{ uri: string; cid: string; value: AppBskyActorProfile.Record }> { 161 + const res = await this._client.call('com.atproto.repo.getRecord', { 162 + collection: 'app.bsky.actor.profile', 163 + ...params, 164 + }) 165 + return res.data 166 + } 167 + 168 + async create( 169 + params: OmitKey< 170 + ComAtprotoRepoCreateRecord.InputSchema, 171 + 'collection' | 'record' 172 + >, 173 + record: Un$Typed<AppBskyActorProfile.Record>, 174 + headers?: Record<string, string>, 175 + ): Promise<{ uri: string; cid: string }> { 176 + const collection = 'app.bsky.actor.profile' 177 + const res = await this._client.call( 178 + 'com.atproto.repo.createRecord', 179 + undefined, 180 + { 181 + collection, 182 + rkey: 'self', 183 + ...params, 184 + record: { ...record, $type: collection }, 185 + }, 186 + { encoding: 'application/json', headers }, 187 + ) 188 + return res.data 189 + } 190 + 191 + async put( 192 + params: OmitKey< 193 + ComAtprotoRepoPutRecord.InputSchema, 194 + 'collection' | 'record' 195 + >, 196 + record: Un$Typed<AppBskyActorProfile.Record>, 197 + headers?: Record<string, string>, 198 + ): Promise<{ uri: string; cid: string }> { 199 + const collection = 'app.bsky.actor.profile' 200 + const res = await this._client.call( 201 + 'com.atproto.repo.putRecord', 202 + undefined, 203 + { collection, ...params, record: { ...record, $type: collection } }, 204 + { encoding: 'application/json', headers }, 205 + ) 206 + return res.data 207 + } 208 + 209 + async delete( 210 + params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 211 + headers?: Record<string, string>, 212 + ): Promise<void> { 213 + await this._client.call( 214 + 'com.atproto.repo.deleteRecord', 215 + undefined, 216 + { collection: 'app.bsky.actor.profile', ...params }, 217 + { headers }, 218 + ) 219 + } 220 + } 221 + 222 + export class ComNS { 223 + _client: XrpcClient 224 + atproto: ComAtprotoNS 225 + 226 + constructor(client: XrpcClient) { 227 + this._client = client 228 + this.atproto = new ComAtprotoNS(client) 229 + } 230 + } 231 + 232 + export class ComAtprotoNS { 233 + _client: XrpcClient 234 + repo: ComAtprotoRepoNS 235 + 236 + constructor(client: XrpcClient) { 237 + this._client = client 238 + this.repo = new ComAtprotoRepoNS(client) 239 + } 240 + } 241 + 242 + export class ComAtprotoRepoNS { 243 + _client: XrpcClient 244 + 245 + constructor(client: XrpcClient) { 246 + this._client = client 247 + } 248 + 249 + applyWrites( 250 + data?: ComAtprotoRepoApplyWrites.InputSchema, 251 + opts?: ComAtprotoRepoApplyWrites.CallOptions, 252 + ): Promise<ComAtprotoRepoApplyWrites.Response> { 253 + return this._client 254 + .call('com.atproto.repo.applyWrites', opts?.qp, data, opts) 255 + .catch((e) => { 256 + throw ComAtprotoRepoApplyWrites.toKnownErr(e) 257 + }) 258 + } 259 + 260 + createRecord( 261 + data?: ComAtprotoRepoCreateRecord.InputSchema, 262 + opts?: ComAtprotoRepoCreateRecord.CallOptions, 263 + ): Promise<ComAtprotoRepoCreateRecord.Response> { 264 + return this._client 265 + .call('com.atproto.repo.createRecord', opts?.qp, data, opts) 266 + .catch((e) => { 267 + throw ComAtprotoRepoCreateRecord.toKnownErr(e) 268 + }) 269 + } 270 + 271 + deleteRecord( 272 + data?: ComAtprotoRepoDeleteRecord.InputSchema, 273 + opts?: ComAtprotoRepoDeleteRecord.CallOptions, 274 + ): Promise<ComAtprotoRepoDeleteRecord.Response> { 275 + return this._client 276 + .call('com.atproto.repo.deleteRecord', opts?.qp, data, opts) 277 + .catch((e) => { 278 + throw ComAtprotoRepoDeleteRecord.toKnownErr(e) 279 + }) 280 + } 281 + 282 + describeRepo( 283 + params?: ComAtprotoRepoDescribeRepo.QueryParams, 284 + opts?: ComAtprotoRepoDescribeRepo.CallOptions, 285 + ): Promise<ComAtprotoRepoDescribeRepo.Response> { 286 + return this._client.call( 287 + 'com.atproto.repo.describeRepo', 288 + params, 289 + undefined, 290 + opts, 291 + ) 292 + } 293 + 294 + getRecord( 295 + params?: ComAtprotoRepoGetRecord.QueryParams, 296 + opts?: ComAtprotoRepoGetRecord.CallOptions, 297 + ): Promise<ComAtprotoRepoGetRecord.Response> { 298 + return this._client 299 + .call('com.atproto.repo.getRecord', params, undefined, opts) 300 + .catch((e) => { 301 + throw ComAtprotoRepoGetRecord.toKnownErr(e) 302 + }) 303 + } 304 + 305 + importRepo( 306 + data?: ComAtprotoRepoImportRepo.InputSchema, 307 + opts?: ComAtprotoRepoImportRepo.CallOptions, 308 + ): Promise<ComAtprotoRepoImportRepo.Response> { 309 + return this._client.call( 310 + 'com.atproto.repo.importRepo', 311 + opts?.qp, 312 + data, 313 + opts, 314 + ) 315 + } 316 + 317 + listMissingBlobs( 318 + params?: ComAtprotoRepoListMissingBlobs.QueryParams, 319 + opts?: ComAtprotoRepoListMissingBlobs.CallOptions, 320 + ): Promise<ComAtprotoRepoListMissingBlobs.Response> { 321 + return this._client.call( 322 + 'com.atproto.repo.listMissingBlobs', 323 + params, 324 + undefined, 325 + opts, 326 + ) 327 + } 328 + 329 + listRecords( 330 + params?: ComAtprotoRepoListRecords.QueryParams, 331 + opts?: ComAtprotoRepoListRecords.CallOptions, 332 + ): Promise<ComAtprotoRepoListRecords.Response> { 333 + return this._client.call( 334 + 'com.atproto.repo.listRecords', 335 + params, 336 + undefined, 337 + opts, 338 + ) 339 + } 340 + 341 + putRecord( 342 + data?: ComAtprotoRepoPutRecord.InputSchema, 343 + opts?: ComAtprotoRepoPutRecord.CallOptions, 344 + ): Promise<ComAtprotoRepoPutRecord.Response> { 345 + return this._client 346 + .call('com.atproto.repo.putRecord', opts?.qp, data, opts) 347 + .catch((e) => { 348 + throw ComAtprotoRepoPutRecord.toKnownErr(e) 349 + }) 350 + } 351 + 352 + uploadBlob( 353 + data?: ComAtprotoRepoUploadBlob.InputSchema, 354 + opts?: ComAtprotoRepoUploadBlob.CallOptions, 355 + ): Promise<ComAtprotoRepoUploadBlob.Response> { 356 + return this._client.call( 357 + 'com.atproto.repo.uploadBlob', 358 + opts?.qp, 359 + data, 360 + opts, 361 + ) 362 + } 363 + } 364 + 365 export class PubNS { 366 _client: XrpcClient 367 leaflet: PubLeafletNS ··· 374 375 export class PubLeafletNS { 376 _client: XrpcClient 377 + comment: PubLeafletCommentRecord 378 + document: PubLeafletDocumentRecord 379 + publication: PubLeafletPublicationRecord 380 blocks: PubLeafletBlocksNS 381 graph: PubLeafletGraphNS 382 pages: PubLeafletPagesNS ··· 390 this.pages = new PubLeafletPagesNS(client) 391 this.richtext = new PubLeafletRichtextNS(client) 392 this.theme = new PubLeafletThemeNS(client) 393 + this.comment = new PubLeafletCommentRecord(client) 394 + this.document = new PubLeafletDocumentRecord(client) 395 + this.publication = new PubLeafletPublicationRecord(client) 396 } 397 } 398 ··· 406 407 export class PubLeafletGraphNS { 408 _client: XrpcClient 409 + subscription: PubLeafletGraphSubscriptionRecord 410 411 constructor(client: XrpcClient) { 412 this._client = client 413 + this.subscription = new PubLeafletGraphSubscriptionRecord(client) 414 } 415 } 416 417 + export class PubLeafletGraphSubscriptionRecord { 418 _client: XrpcClient 419 420 constructor(client: XrpcClient) { ··· 466 return res.data 467 } 468 469 + async put( 470 + params: OmitKey< 471 + ComAtprotoRepoPutRecord.InputSchema, 472 + 'collection' | 'record' 473 + >, 474 + record: Un$Typed<PubLeafletGraphSubscription.Record>, 475 + headers?: Record<string, string>, 476 + ): Promise<{ uri: string; cid: string }> { 477 + const collection = 'pub.leaflet.graph.subscription' 478 + const res = await this._client.call( 479 + 'com.atproto.repo.putRecord', 480 + undefined, 481 + { collection, ...params, record: { ...record, $type: collection } }, 482 + { encoding: 'application/json', headers }, 483 + ) 484 + return res.data 485 + } 486 + 487 async delete( 488 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 489 headers?: Record<string, string>, ··· 521 } 522 } 523 524 + export class PubLeafletCommentRecord { 525 _client: XrpcClient 526 527 constructor(client: XrpcClient) { ··· 569 return res.data 570 } 571 572 + async put( 573 + params: OmitKey< 574 + ComAtprotoRepoPutRecord.InputSchema, 575 + 'collection' | 'record' 576 + >, 577 + record: Un$Typed<PubLeafletComment.Record>, 578 + headers?: Record<string, string>, 579 + ): Promise<{ uri: string; cid: string }> { 580 + const collection = 'pub.leaflet.comment' 581 + const res = await this._client.call( 582 + 'com.atproto.repo.putRecord', 583 + undefined, 584 + { collection, ...params, record: { ...record, $type: collection } }, 585 + { encoding: 'application/json', headers }, 586 + ) 587 + return res.data 588 + } 589 + 590 async delete( 591 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 592 headers?: Record<string, string>, ··· 600 } 601 } 602 603 + export class PubLeafletDocumentRecord { 604 _client: XrpcClient 605 606 constructor(client: XrpcClient) { ··· 648 return res.data 649 } 650 651 + async put( 652 + params: OmitKey< 653 + ComAtprotoRepoPutRecord.InputSchema, 654 + 'collection' | 'record' 655 + >, 656 + record: Un$Typed<PubLeafletDocument.Record>, 657 + headers?: Record<string, string>, 658 + ): Promise<{ uri: string; cid: string }> { 659 + const collection = 'pub.leaflet.document' 660 + const res = await this._client.call( 661 + 'com.atproto.repo.putRecord', 662 + undefined, 663 + { collection, ...params, record: { ...record, $type: collection } }, 664 + { encoding: 'application/json', headers }, 665 + ) 666 + return res.data 667 + } 668 + 669 async delete( 670 params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 671 headers?: Record<string, string>, ··· 679 } 680 } 681 682 + export class PubLeafletPublicationRecord { 683 _client: XrpcClient 684 685 constructor(client: XrpcClient) { ··· 731 return res.data 732 } 733 734 + async put( 735 params: OmitKey< 736 + ComAtprotoRepoPutRecord.InputSchema, 737 'collection' | 'record' 738 >, 739 + record: Un$Typed<PubLeafletPublication.Record>, 740 headers?: Record<string, string>, 741 ): Promise<{ uri: string; cid: string }> { 742 + const collection = 'pub.leaflet.publication' 743 const res = await this._client.call( 744 + 'com.atproto.repo.putRecord', 745 undefined, 746 + { collection, ...params, record: { ...record, $type: collection } }, 747 { encoding: 'application/json', headers }, 748 ) 749 return res.data ··· 756 await this._client.call( 757 'com.atproto.repo.deleteRecord', 758 undefined, 759 + { collection: 'pub.leaflet.publication', ...params }, 760 { headers }, 761 ) 762 }
+819 -821
lexicons/api/lexicons.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { 5 - LexiconDoc, 6 Lexicons, 7 ValidationError, 8 - ValidationResult, 9 } from '@atproto/lexicon' 10 - import { $Typed, is$typed, maybe$typed } from './util' 11 12 export const schemaDict = { 13 - PubLeafletComment: { 14 lexicon: 1, 15 - id: 'pub.leaflet.comment', 16 - revision: 1, 17 - description: 'A lexicon for comments on documents', 18 defs: { 19 main: { 20 type: 'record', 21 - key: 'tid', 22 - description: 'Record containing a comment', 23 record: { 24 type: 'object', 25 - required: ['subject', 'plaintext', 'createdAt'], 26 properties: { 27 - subject: { 28 type: 'string', 29 - format: 'at-uri', 30 - }, 31 - createdAt: { 32 - type: 'string', 33 - format: 'datetime', 34 - }, 35 - reply: { 36 - type: 'ref', 37 - ref: 'lex:pub.leaflet.comment#replyRef', 38 - }, 39 - plaintext: { 40 - type: 'string', 41 - }, 42 - facets: { 43 - type: 'array', 44 - items: { 45 - type: 'ref', 46 - ref: 'lex:pub.leaflet.richtext.facet', 47 - }, 48 - }, 49 - attachment: { 50 - type: 'union', 51 - refs: ['lex:pub.leaflet.comment#linearDocumentQuote'], 52 - }, 53 - }, 54 - }, 55 - }, 56 - linearDocumentQuote: { 57 - type: 'object', 58 - required: ['document', 'quote'], 59 - properties: { 60 - document: { 61 - type: 'string', 62 - format: 'at-uri', 63 - }, 64 - quote: { 65 - type: 'ref', 66 - ref: 'lex:pub.leaflet.pages.linearDocument#quote', 67 - }, 68 - }, 69 - }, 70 - replyRef: { 71 - type: 'object', 72 - required: ['parent'], 73 - properties: { 74 - parent: { 75 - type: 'string', 76 - format: 'at-uri', 77 - }, 78 - }, 79 - }, 80 - }, 81 - }, 82 - PubLeafletDocument: { 83 - lexicon: 1, 84 - id: 'pub.leaflet.document', 85 - revision: 1, 86 - description: 'A lexicon for long form rich media documents', 87 - defs: { 88 - main: { 89 - type: 'record', 90 - key: 'tid', 91 - description: 'Record containing a document', 92 - record: { 93 - type: 'object', 94 - required: ['pages', 'author', 'title', 'publication'], 95 - properties: { 96 - title: { 97 - type: 'string', 98 - maxLength: 1280, 99 - maxGraphemes: 128, 100 - }, 101 - postRef: { 102 - type: 'ref', 103 - ref: 'lex:com.atproto.repo.strongRef', 104 }, 105 description: { 106 type: 'string', 107 - maxLength: 3000, 108 - maxGraphemes: 300, 109 - }, 110 - publishedAt: { 111 - type: 'string', 112 - format: 'datetime', 113 }, 114 - publication: { 115 - type: 'string', 116 - format: 'at-uri', 117 - }, 118 - author: { 119 - type: 'string', 120 - format: 'at-identifier', 121 - }, 122 - pages: { 123 - type: 'array', 124 - items: { 125 - type: 'union', 126 - refs: ['lex:pub.leaflet.pages.linearDocument'], 127 - }, 128 }, 129 - }, 130 - }, 131 - }, 132 - }, 133 - }, 134 - PubLeafletPublication: { 135 - lexicon: 1, 136 - id: 'pub.leaflet.publication', 137 - defs: { 138 - main: { 139 - type: 'record', 140 - key: 'tid', 141 - description: 'Record declaring a publication', 142 - record: { 143 - type: 'object', 144 - required: ['name'], 145 - properties: { 146 - name: { 147 - type: 'string', 148 - maxLength: 2000, 149 - }, 150 - base_path: { 151 - type: 'string', 152 - format: 'uri', 153 - }, 154 - description: { 155 - type: 'string', 156 - maxLength: 2000, 157 - }, 158 - icon: { 159 type: 'blob', 160 - accept: ['image/*'], 161 maxSize: 1000000, 162 }, 163 - theme: { 164 - type: 'ref', 165 - ref: 'lex:pub.leaflet.publication#theme', 166 }, 167 - preferences: { 168 type: 'ref', 169 - ref: 'lex:pub.leaflet.publication#preferences', 170 }, 171 - }, 172 - }, 173 - }, 174 - preferences: { 175 - type: 'object', 176 - properties: { 177 - showInDiscover: { 178 - type: 'boolean', 179 - default: true, 180 - }, 181 - showComments: { 182 - type: 'boolean', 183 - default: true, 184 - }, 185 - }, 186 - }, 187 - theme: { 188 - type: 'object', 189 - properties: { 190 - backgroundColor: { 191 - type: 'union', 192 - refs: [ 193 - 'lex:pub.leaflet.theme.color#rgba', 194 - 'lex:pub.leaflet.theme.color#rgb', 195 - ], 196 - }, 197 - backgroundImage: { 198 - type: 'ref', 199 - ref: 'lex:pub.leaflet.theme.backgroundImage', 200 - }, 201 - primary: { 202 - type: 'union', 203 - refs: [ 204 - 'lex:pub.leaflet.theme.color#rgba', 205 - 'lex:pub.leaflet.theme.color#rgb', 206 - ], 207 - }, 208 - pageBackground: { 209 - type: 'union', 210 - refs: [ 211 - 'lex:pub.leaflet.theme.color#rgba', 212 - 'lex:pub.leaflet.theme.color#rgb', 213 - ], 214 - }, 215 - showPageBackground: { 216 - type: 'boolean', 217 - default: false, 218 - }, 219 - accentBackground: { 220 - type: 'union', 221 - refs: [ 222 - 'lex:pub.leaflet.theme.color#rgba', 223 - 'lex:pub.leaflet.theme.color#rgb', 224 - ], 225 - }, 226 - accentText: { 227 - type: 'union', 228 - refs: [ 229 - 'lex:pub.leaflet.theme.color#rgba', 230 - 'lex:pub.leaflet.theme.color#rgb', 231 - ], 232 - }, 233 - }, 234 - }, 235 - }, 236 - }, 237 - PubLeafletBlocksBlockquote: { 238 - lexicon: 1, 239 - id: 'pub.leaflet.blocks.blockquote', 240 - defs: { 241 - main: { 242 - type: 'object', 243 - required: ['plaintext'], 244 - properties: { 245 - plaintext: { 246 - type: 'string', 247 - }, 248 - facets: { 249 - type: 'array', 250 - items: { 251 type: 'ref', 252 - ref: 'lex:pub.leaflet.richtext.facet', 253 }, 254 - }, 255 - }, 256 - }, 257 - }, 258 - }, 259 - PubLeafletBlocksBskyPost: { 260 - lexicon: 1, 261 - id: 'pub.leaflet.blocks.bskyPost', 262 - defs: { 263 - main: { 264 - type: 'object', 265 - required: ['postRef'], 266 - properties: { 267 - postRef: { 268 - type: 'ref', 269 - ref: 'lex:com.atproto.repo.strongRef', 270 - }, 271 - }, 272 - }, 273 - }, 274 - }, 275 - PubLeafletBlocksCode: { 276 - lexicon: 1, 277 - id: 'pub.leaflet.blocks.code', 278 - defs: { 279 - main: { 280 - type: 'object', 281 - required: ['plaintext'], 282 - properties: { 283 - plaintext: { 284 - type: 'string', 285 - }, 286 - language: { 287 - type: 'string', 288 - }, 289 - syntaxHighlightingTheme: { 290 - type: 'string', 291 - }, 292 - }, 293 - }, 294 - }, 295 - }, 296 - PubLeafletBlocksHeader: { 297 - lexicon: 1, 298 - id: 'pub.leaflet.blocks.header', 299 - defs: { 300 - main: { 301 - type: 'object', 302 - required: ['plaintext'], 303 - properties: { 304 - level: { 305 - type: 'integer', 306 - minimum: 1, 307 - maximum: 6, 308 - }, 309 - plaintext: { 310 - type: 'string', 311 - }, 312 - facets: { 313 - type: 'array', 314 - items: { 315 - type: 'ref', 316 - ref: 'lex:pub.leaflet.richtext.facet', 317 - }, 318 - }, 319 - }, 320 - }, 321 - }, 322 - }, 323 - PubLeafletBlocksHorizontalRule: { 324 - lexicon: 1, 325 - id: 'pub.leaflet.blocks.horizontalRule', 326 - defs: { 327 - main: { 328 - type: 'object', 329 - required: [], 330 - properties: {}, 331 - }, 332 - }, 333 - }, 334 - PubLeafletBlocksIframe: { 335 - lexicon: 1, 336 - id: 'pub.leaflet.blocks.iframe', 337 - defs: { 338 - main: { 339 - type: 'object', 340 - required: ['url'], 341 - properties: { 342 - url: { 343 - type: 'string', 344 - format: 'uri', 345 - }, 346 - height: { 347 - type: 'integer', 348 - minimum: 16, 349 - maximum: 1600, 350 - }, 351 - }, 352 - }, 353 - }, 354 - }, 355 - PubLeafletBlocksImage: { 356 - lexicon: 1, 357 - id: 'pub.leaflet.blocks.image', 358 - defs: { 359 - main: { 360 - type: 'object', 361 - required: ['image', 'aspectRatio'], 362 - properties: { 363 - image: { 364 - type: 'blob', 365 - accept: ['image/*'], 366 - maxSize: 1000000, 367 - }, 368 - alt: { 369 - type: 'string', 370 - description: 371 - 'Alt text description of the image, for accessibility.', 372 - }, 373 - aspectRatio: { 374 - type: 'ref', 375 - ref: 'lex:pub.leaflet.blocks.image#aspectRatio', 376 - }, 377 - }, 378 - }, 379 - aspectRatio: { 380 - type: 'object', 381 - required: ['width', 'height'], 382 - properties: { 383 - width: { 384 - type: 'integer', 385 - }, 386 - height: { 387 - type: 'integer', 388 - }, 389 - }, 390 - }, 391 - }, 392 - }, 393 - PubLeafletBlocksMath: { 394 - lexicon: 1, 395 - id: 'pub.leaflet.blocks.math', 396 - defs: { 397 - main: { 398 - type: 'object', 399 - required: ['tex'], 400 - properties: { 401 - tex: { 402 - type: 'string', 403 - }, 404 - }, 405 - }, 406 - }, 407 - }, 408 - PubLeafletBlocksPage: { 409 - lexicon: 1, 410 - id: 'pub.leaflet.blocks.page', 411 - defs: { 412 - main: { 413 - type: 'object', 414 - required: ['id'], 415 - properties: { 416 - id: { 417 - type: 'string', 418 - }, 419 - }, 420 - }, 421 - }, 422 - }, 423 - PubLeafletBlocksText: { 424 - lexicon: 1, 425 - id: 'pub.leaflet.blocks.text', 426 - defs: { 427 - main: { 428 - type: 'object', 429 - required: ['plaintext'], 430 - properties: { 431 - plaintext: { 432 - type: 'string', 433 - }, 434 - facets: { 435 - type: 'array', 436 - items: { 437 - type: 'ref', 438 - ref: 'lex:pub.leaflet.richtext.facet', 439 - }, 440 - }, 441 - }, 442 - }, 443 - }, 444 - }, 445 - PubLeafletBlocksUnorderedList: { 446 - lexicon: 1, 447 - id: 'pub.leaflet.blocks.unorderedList', 448 - defs: { 449 - main: { 450 - type: 'object', 451 - required: ['children'], 452 - properties: { 453 - children: { 454 - type: 'array', 455 - items: { 456 - type: 'ref', 457 - ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 458 - }, 459 - }, 460 - }, 461 - }, 462 - listItem: { 463 - type: 'object', 464 - required: ['content'], 465 - properties: { 466 - content: { 467 - type: 'union', 468 - refs: [ 469 - 'lex:pub.leaflet.blocks.text', 470 - 'lex:pub.leaflet.blocks.header', 471 - 'lex:pub.leaflet.blocks.image', 472 - ], 473 - }, 474 - children: { 475 - type: 'array', 476 - items: { 477 - type: 'ref', 478 - ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 479 - }, 480 - }, 481 - }, 482 - }, 483 - }, 484 - }, 485 - PubLeafletBlocksWebsite: { 486 - lexicon: 1, 487 - id: 'pub.leaflet.blocks.website', 488 - defs: { 489 - main: { 490 - type: 'object', 491 - required: ['src'], 492 - properties: { 493 - previewImage: { 494 - type: 'blob', 495 - accept: ['image/*'], 496 - maxSize: 1000000, 497 - }, 498 - title: { 499 - type: 'string', 500 - }, 501 - description: { 502 - type: 'string', 503 - }, 504 - src: { 505 - type: 'string', 506 - format: 'uri', 507 - }, 508 - }, 509 - }, 510 - }, 511 - }, 512 - PubLeafletGraphSubscription: { 513 - lexicon: 1, 514 - id: 'pub.leaflet.graph.subscription', 515 - defs: { 516 - main: { 517 - type: 'record', 518 - key: 'tid', 519 - description: 'Record declaring a subscription to a publication', 520 - record: { 521 - type: 'object', 522 - required: ['publication'], 523 - properties: { 524 - publication: { 525 type: 'string', 526 - format: 'at-uri', 527 - }, 528 - }, 529 - }, 530 - }, 531 - }, 532 - }, 533 - PubLeafletPagesLinearDocument: { 534 - lexicon: 1, 535 - id: 'pub.leaflet.pages.linearDocument', 536 - defs: { 537 - main: { 538 - type: 'object', 539 - required: ['blocks'], 540 - properties: { 541 - id: { 542 - type: 'string', 543 - }, 544 - blocks: { 545 - type: 'array', 546 - items: { 547 - type: 'ref', 548 - ref: 'lex:pub.leaflet.pages.linearDocument#block', 549 - }, 550 - }, 551 - }, 552 - }, 553 - block: { 554 - type: 'object', 555 - required: ['block'], 556 - properties: { 557 - block: { 558 - type: 'union', 559 - refs: [ 560 - 'lex:pub.leaflet.blocks.iframe', 561 - 'lex:pub.leaflet.blocks.text', 562 - 'lex:pub.leaflet.blocks.blockquote', 563 - 'lex:pub.leaflet.blocks.header', 564 - 'lex:pub.leaflet.blocks.image', 565 - 'lex:pub.leaflet.blocks.unorderedList', 566 - 'lex:pub.leaflet.blocks.website', 567 - 'lex:pub.leaflet.blocks.math', 568 - 'lex:pub.leaflet.blocks.code', 569 - 'lex:pub.leaflet.blocks.horizontalRule', 570 - 'lex:pub.leaflet.blocks.bskyPost', 571 - 'lex:pub.leaflet.blocks.page', 572 - ], 573 - }, 574 - alignment: { 575 - type: 'string', 576 - knownValues: [ 577 - 'lex:pub.leaflet.pages.linearDocument#textAlignLeft', 578 - 'lex:pub.leaflet.pages.linearDocument#textAlignCenter', 579 - 'lex:pub.leaflet.pages.linearDocument#textAlignRight', 580 - 'lex:pub.leaflet.pages.linearDocument#textAlignJustify', 581 - ], 582 - }, 583 - }, 584 - }, 585 - textAlignLeft: { 586 - type: 'token', 587 - }, 588 - textAlignCenter: { 589 - type: 'token', 590 - }, 591 - textAlignRight: { 592 - type: 'token', 593 - }, 594 - quote: { 595 - type: 'object', 596 - required: ['start', 'end'], 597 - properties: { 598 - start: { 599 - type: 'ref', 600 - ref: 'lex:pub.leaflet.pages.linearDocument#position', 601 - }, 602 - end: { 603 - type: 'ref', 604 - ref: 'lex:pub.leaflet.pages.linearDocument#position', 605 - }, 606 - }, 607 - }, 608 - position: { 609 - type: 'object', 610 - required: ['block', 'offset'], 611 - properties: { 612 - block: { 613 - type: 'array', 614 - items: { 615 - type: 'integer', 616 }, 617 - }, 618 - offset: { 619 - type: 'integer', 620 - }, 621 - }, 622 - }, 623 - }, 624 - }, 625 - PubLeafletRichtextFacet: { 626 - lexicon: 1, 627 - id: 'pub.leaflet.richtext.facet', 628 - defs: { 629 - main: { 630 - type: 'object', 631 - description: 'Annotation of a sub-string within rich text.', 632 - required: ['index', 'features'], 633 - properties: { 634 - index: { 635 - type: 'ref', 636 - ref: 'lex:pub.leaflet.richtext.facet#byteSlice', 637 - }, 638 - features: { 639 - type: 'array', 640 - items: { 641 - type: 'union', 642 - refs: [ 643 - 'lex:pub.leaflet.richtext.facet#link', 644 - 'lex:pub.leaflet.richtext.facet#code', 645 - 'lex:pub.leaflet.richtext.facet#highlight', 646 - 'lex:pub.leaflet.richtext.facet#underline', 647 - 'lex:pub.leaflet.richtext.facet#strikethrough', 648 - 'lex:pub.leaflet.richtext.facet#id', 649 - 'lex:pub.leaflet.richtext.facet#bold', 650 - 'lex:pub.leaflet.richtext.facet#italic', 651 - ], 652 - }, 653 - }, 654 - }, 655 - }, 656 - byteSlice: { 657 - type: 'object', 658 - description: 659 - 'Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.', 660 - required: ['byteStart', 'byteEnd'], 661 - properties: { 662 - byteStart: { 663 - type: 'integer', 664 - minimum: 0, 665 - }, 666 - byteEnd: { 667 - type: 'integer', 668 - minimum: 0, 669 - }, 670 - }, 671 - }, 672 - link: { 673 - type: 'object', 674 - description: 675 - 'Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.', 676 - required: ['uri'], 677 - properties: { 678 - uri: { 679 - type: 'string', 680 - format: 'uri', 681 - }, 682 - }, 683 - }, 684 - code: { 685 - type: 'object', 686 - description: 'Facet feature for inline code.', 687 - required: [], 688 - properties: {}, 689 - }, 690 - highlight: { 691 - type: 'object', 692 - description: 'Facet feature for highlighted text.', 693 - required: [], 694 - properties: {}, 695 - }, 696 - underline: { 697 - type: 'object', 698 - description: 'Facet feature for underline markup', 699 - required: [], 700 - properties: {}, 701 - }, 702 - strikethrough: { 703 - type: 'object', 704 - description: 'Facet feature for strikethrough markup', 705 - required: [], 706 - properties: {}, 707 - }, 708 - id: { 709 - type: 'object', 710 - description: 711 - 'Facet feature for an identifier. Used for linking to a segment', 712 - required: [], 713 - properties: { 714 - id: { 715 - type: 'string', 716 - }, 717 - }, 718 - }, 719 - bold: { 720 - type: 'object', 721 - description: 'Facet feature for bold text', 722 - required: [], 723 - properties: {}, 724 - }, 725 - italic: { 726 - type: 'object', 727 - description: 'Facet feature for italic text', 728 - required: [], 729 - properties: {}, 730 - }, 731 - }, 732 - }, 733 - PubLeafletThemeBackgroundImage: { 734 - lexicon: 1, 735 - id: 'pub.leaflet.theme.backgroundImage', 736 - defs: { 737 - main: { 738 - type: 'object', 739 - required: ['image'], 740 - properties: { 741 - image: { 742 - type: 'blob', 743 - accept: ['image/*'], 744 - maxSize: 1000000, 745 - }, 746 - width: { 747 - type: 'integer', 748 - }, 749 - repeat: { 750 - type: 'boolean', 751 - }, 752 - }, 753 - }, 754 - }, 755 - }, 756 - PubLeafletThemeColor: { 757 - lexicon: 1, 758 - id: 'pub.leaflet.theme.color', 759 - defs: { 760 - rgba: { 761 - type: 'object', 762 - required: ['r', 'g', 'b', 'a'], 763 - properties: { 764 - r: { 765 - type: 'integer', 766 - maximum: 255, 767 - minimum: 0, 768 - }, 769 - g: { 770 - type: 'integer', 771 - maximum: 255, 772 - minimum: 0, 773 - }, 774 - b: { 775 - type: 'integer', 776 - maximum: 255, 777 - minimum: 0, 778 - }, 779 - a: { 780 - type: 'integer', 781 - maximum: 100, 782 - minimum: 0, 783 - }, 784 - }, 785 - }, 786 - rgb: { 787 - type: 'object', 788 - required: ['r', 'g', 'b'], 789 - properties: { 790 - r: { 791 - type: 'integer', 792 - maximum: 255, 793 - minimum: 0, 794 - }, 795 - g: { 796 - type: 'integer', 797 - maximum: 255, 798 - minimum: 0, 799 - }, 800 - b: { 801 - type: 'integer', 802 - maximum: 255, 803 - minimum: 0, 804 }, 805 }, 806 }, ··· 1751 }, 1752 }, 1753 }, 1754 - AppBskyActorProfile: { 1755 lexicon: 1, 1756 - id: 'app.bsky.actor.profile', 1757 defs: { 1758 main: { 1759 type: 'record', 1760 - description: 'A declaration of a Bluesky account profile.', 1761 - key: 'literal:self', 1762 record: { 1763 type: 'object', 1764 properties: { 1765 - displayName: { 1766 type: 'string', 1767 - maxGraphemes: 64, 1768 - maxLength: 640, 1769 }, 1770 - description: { 1771 type: 'string', 1772 - description: 'Free-form profile description text.', 1773 - maxGraphemes: 256, 1774 - maxLength: 2560, 1775 }, 1776 - avatar: { 1777 - type: 'blob', 1778 - description: 1779 - "Small image to be displayed next to posts from account. AKA, 'profile picture'", 1780 - accept: ['image/png', 'image/jpeg'], 1781 - maxSize: 1000000, 1782 }, 1783 - banner: { 1784 - type: 'blob', 1785 - description: 1786 - 'Larger horizontal image to display behind profile view.', 1787 - accept: ['image/png', 'image/jpeg'], 1788 - maxSize: 1000000, 1789 }, 1790 - labels: { 1791 type: 'union', 1792 - description: 1793 - 'Self-label values, specific to the Bluesky application, on the overall account.', 1794 - refs: ['lex:com.atproto.label.defs#selfLabels'], 1795 }, 1796 - joinedViaStarterPack: { 1797 type: 'ref', 1798 ref: 'lex:com.atproto.repo.strongRef', 1799 }, 1800 - pinnedPost: { 1801 type: 'ref', 1802 - ref: 'lex:com.atproto.repo.strongRef', 1803 }, 1804 - createdAt: { 1805 type: 'string', 1806 - format: 'datetime', 1807 }, 1808 }, 1809 }, 1810 }, 1811 }, 1812 }, 1813 } as const satisfies Record<string, LexiconDoc> 1814 - 1815 export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 1816 export const lexicons: Lexicons = new Lexicons(schemas) 1817 ··· 1844 } 1845 1846 export const ids = { 1847 - PubLeafletComment: 'pub.leaflet.comment', 1848 - PubLeafletDocument: 'pub.leaflet.document', 1849 - PubLeafletPublication: 'pub.leaflet.publication', 1850 PubLeafletBlocksBlockquote: 'pub.leaflet.blocks.blockquote', 1851 PubLeafletBlocksBskyPost: 'pub.leaflet.blocks.bskyPost', 1852 PubLeafletBlocksCode: 'pub.leaflet.blocks.code', ··· 1859 PubLeafletBlocksText: 'pub.leaflet.blocks.text', 1860 PubLeafletBlocksUnorderedList: 'pub.leaflet.blocks.unorderedList', 1861 PubLeafletBlocksWebsite: 'pub.leaflet.blocks.website', 1862 PubLeafletGraphSubscription: 'pub.leaflet.graph.subscription', 1863 PubLeafletPagesLinearDocument: 'pub.leaflet.pages.linearDocument', 1864 PubLeafletRichtextFacet: 'pub.leaflet.richtext.facet', 1865 PubLeafletThemeBackgroundImage: 'pub.leaflet.theme.backgroundImage', 1866 PubLeafletThemeColor: 'pub.leaflet.theme.color', 1867 - ComAtprotoLabelDefs: 'com.atproto.label.defs', 1868 - ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites', 1869 - ComAtprotoRepoCreateRecord: 'com.atproto.repo.createRecord', 1870 - ComAtprotoRepoDefs: 'com.atproto.repo.defs', 1871 - ComAtprotoRepoDeleteRecord: 'com.atproto.repo.deleteRecord', 1872 - ComAtprotoRepoDescribeRepo: 'com.atproto.repo.describeRepo', 1873 - ComAtprotoRepoGetRecord: 'com.atproto.repo.getRecord', 1874 - ComAtprotoRepoImportRepo: 'com.atproto.repo.importRepo', 1875 - ComAtprotoRepoListMissingBlobs: 'com.atproto.repo.listMissingBlobs', 1876 - ComAtprotoRepoListRecords: 'com.atproto.repo.listRecords', 1877 - ComAtprotoRepoPutRecord: 'com.atproto.repo.putRecord', 1878 - ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', 1879 - ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob', 1880 - AppBskyActorProfile: 'app.bsky.actor.profile', 1881 } as const
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { 5 + type LexiconDoc, 6 Lexicons, 7 ValidationError, 8 + type ValidationResult, 9 } from '@atproto/lexicon' 10 + import { type $Typed, is$typed, maybe$typed } from './util' 11 12 export const schemaDict = { 13 + AppBskyActorProfile: { 14 lexicon: 1, 15 + id: 'app.bsky.actor.profile', 16 defs: { 17 main: { 18 type: 'record', 19 + description: 'A declaration of a Bluesky account profile.', 20 + key: 'literal:self', 21 record: { 22 type: 'object', 23 properties: { 24 + displayName: { 25 type: 'string', 26 + maxGraphemes: 64, 27 + maxLength: 640, 28 }, 29 description: { 30 type: 'string', 31 + description: 'Free-form profile description text.', 32 + maxGraphemes: 256, 33 + maxLength: 2560, 34 }, 35 + avatar: { 36 + type: 'blob', 37 + description: 38 + "Small image to be displayed next to posts from account. AKA, 'profile picture'", 39 + accept: ['image/png', 'image/jpeg'], 40 + maxSize: 1000000, 41 }, 42 + banner: { 43 type: 'blob', 44 + description: 45 + 'Larger horizontal image to display behind profile view.', 46 + accept: ['image/png', 'image/jpeg'], 47 maxSize: 1000000, 48 }, 49 + labels: { 50 + type: 'union', 51 + description: 52 + 'Self-label values, specific to the Bluesky application, on the overall account.', 53 + refs: ['lex:com.atproto.label.defs#selfLabels'], 54 }, 55 + joinedViaStarterPack: { 56 type: 'ref', 57 + ref: 'lex:com.atproto.repo.strongRef', 58 }, 59 + pinnedPost: { 60 type: 'ref', 61 + ref: 'lex:com.atproto.repo.strongRef', 62 }, 63 + createdAt: { 64 type: 'string', 65 + format: 'datetime', 66 }, 67 }, 68 }, 69 }, ··· 1014 }, 1015 }, 1016 }, 1017 + PubLeafletBlocksBlockquote: { 1018 + lexicon: 1, 1019 + id: 'pub.leaflet.blocks.blockquote', 1020 + defs: { 1021 + main: { 1022 + type: 'object', 1023 + required: ['plaintext'], 1024 + properties: { 1025 + plaintext: { 1026 + type: 'string', 1027 + }, 1028 + facets: { 1029 + type: 'array', 1030 + items: { 1031 + type: 'ref', 1032 + ref: 'lex:pub.leaflet.richtext.facet', 1033 + }, 1034 + }, 1035 + }, 1036 + }, 1037 + }, 1038 + }, 1039 + PubLeafletBlocksBskyPost: { 1040 + lexicon: 1, 1041 + id: 'pub.leaflet.blocks.bskyPost', 1042 + defs: { 1043 + main: { 1044 + type: 'object', 1045 + required: ['postRef'], 1046 + properties: { 1047 + postRef: { 1048 + type: 'ref', 1049 + ref: 'lex:com.atproto.repo.strongRef', 1050 + }, 1051 + }, 1052 + }, 1053 + }, 1054 + }, 1055 + PubLeafletBlocksCode: { 1056 + lexicon: 1, 1057 + id: 'pub.leaflet.blocks.code', 1058 + defs: { 1059 + main: { 1060 + type: 'object', 1061 + required: ['plaintext'], 1062 + properties: { 1063 + plaintext: { 1064 + type: 'string', 1065 + }, 1066 + language: { 1067 + type: 'string', 1068 + }, 1069 + syntaxHighlightingTheme: { 1070 + type: 'string', 1071 + }, 1072 + }, 1073 + }, 1074 + }, 1075 + }, 1076 + PubLeafletBlocksHeader: { 1077 + lexicon: 1, 1078 + id: 'pub.leaflet.blocks.header', 1079 + defs: { 1080 + main: { 1081 + type: 'object', 1082 + required: ['plaintext'], 1083 + properties: { 1084 + level: { 1085 + type: 'integer', 1086 + minimum: 1, 1087 + maximum: 6, 1088 + }, 1089 + plaintext: { 1090 + type: 'string', 1091 + }, 1092 + facets: { 1093 + type: 'array', 1094 + items: { 1095 + type: 'ref', 1096 + ref: 'lex:pub.leaflet.richtext.facet', 1097 + }, 1098 + }, 1099 + }, 1100 + }, 1101 + }, 1102 + }, 1103 + PubLeafletBlocksHorizontalRule: { 1104 + lexicon: 1, 1105 + id: 'pub.leaflet.blocks.horizontalRule', 1106 + defs: { 1107 + main: { 1108 + type: 'object', 1109 + required: [], 1110 + properties: {}, 1111 + }, 1112 + }, 1113 + }, 1114 + PubLeafletBlocksIframe: { 1115 + lexicon: 1, 1116 + id: 'pub.leaflet.blocks.iframe', 1117 + defs: { 1118 + main: { 1119 + type: 'object', 1120 + required: ['url'], 1121 + properties: { 1122 + url: { 1123 + type: 'string', 1124 + format: 'uri', 1125 + }, 1126 + height: { 1127 + type: 'integer', 1128 + minimum: 16, 1129 + maximum: 1600, 1130 + }, 1131 + }, 1132 + }, 1133 + }, 1134 + }, 1135 + PubLeafletBlocksImage: { 1136 lexicon: 1, 1137 + id: 'pub.leaflet.blocks.image', 1138 + defs: { 1139 + main: { 1140 + type: 'object', 1141 + required: ['image', 'aspectRatio'], 1142 + properties: { 1143 + image: { 1144 + type: 'blob', 1145 + accept: ['image/*'], 1146 + maxSize: 1000000, 1147 + }, 1148 + alt: { 1149 + type: 'string', 1150 + description: 1151 + 'Alt text description of the image, for accessibility.', 1152 + }, 1153 + aspectRatio: { 1154 + type: 'ref', 1155 + ref: 'lex:pub.leaflet.blocks.image#aspectRatio', 1156 + }, 1157 + }, 1158 + }, 1159 + aspectRatio: { 1160 + type: 'object', 1161 + required: ['width', 'height'], 1162 + properties: { 1163 + width: { 1164 + type: 'integer', 1165 + }, 1166 + height: { 1167 + type: 'integer', 1168 + }, 1169 + }, 1170 + }, 1171 + }, 1172 + }, 1173 + PubLeafletBlocksMath: { 1174 + lexicon: 1, 1175 + id: 'pub.leaflet.blocks.math', 1176 + defs: { 1177 + main: { 1178 + type: 'object', 1179 + required: ['tex'], 1180 + properties: { 1181 + tex: { 1182 + type: 'string', 1183 + }, 1184 + }, 1185 + }, 1186 + }, 1187 + }, 1188 + PubLeafletBlocksPage: { 1189 + lexicon: 1, 1190 + id: 'pub.leaflet.blocks.page', 1191 + defs: { 1192 + main: { 1193 + type: 'object', 1194 + required: ['id'], 1195 + properties: { 1196 + id: { 1197 + type: 'string', 1198 + }, 1199 + }, 1200 + }, 1201 + }, 1202 + }, 1203 + PubLeafletBlocksText: { 1204 + lexicon: 1, 1205 + id: 'pub.leaflet.blocks.text', 1206 + defs: { 1207 + main: { 1208 + type: 'object', 1209 + required: ['plaintext'], 1210 + properties: { 1211 + plaintext: { 1212 + type: 'string', 1213 + }, 1214 + facets: { 1215 + type: 'array', 1216 + items: { 1217 + type: 'ref', 1218 + ref: 'lex:pub.leaflet.richtext.facet', 1219 + }, 1220 + }, 1221 + }, 1222 + }, 1223 + }, 1224 + }, 1225 + PubLeafletBlocksUnorderedList: { 1226 + lexicon: 1, 1227 + id: 'pub.leaflet.blocks.unorderedList', 1228 + defs: { 1229 + main: { 1230 + type: 'object', 1231 + required: ['children'], 1232 + properties: { 1233 + children: { 1234 + type: 'array', 1235 + items: { 1236 + type: 'ref', 1237 + ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 1238 + }, 1239 + }, 1240 + }, 1241 + }, 1242 + listItem: { 1243 + type: 'object', 1244 + required: ['content'], 1245 + properties: { 1246 + content: { 1247 + type: 'union', 1248 + refs: [ 1249 + 'lex:pub.leaflet.blocks.text', 1250 + 'lex:pub.leaflet.blocks.header', 1251 + 'lex:pub.leaflet.blocks.image', 1252 + ], 1253 + }, 1254 + children: { 1255 + type: 'array', 1256 + items: { 1257 + type: 'ref', 1258 + ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 1259 + }, 1260 + }, 1261 + }, 1262 + }, 1263 + }, 1264 + }, 1265 + PubLeafletBlocksWebsite: { 1266 + lexicon: 1, 1267 + id: 'pub.leaflet.blocks.website', 1268 + defs: { 1269 + main: { 1270 + type: 'object', 1271 + required: ['src'], 1272 + properties: { 1273 + previewImage: { 1274 + type: 'blob', 1275 + accept: ['image/*'], 1276 + maxSize: 1000000, 1277 + }, 1278 + title: { 1279 + type: 'string', 1280 + }, 1281 + description: { 1282 + type: 'string', 1283 + }, 1284 + src: { 1285 + type: 'string', 1286 + format: 'uri', 1287 + }, 1288 + }, 1289 + }, 1290 + }, 1291 + }, 1292 + PubLeafletComment: { 1293 + lexicon: 1, 1294 + id: 'pub.leaflet.comment', 1295 + revision: 1, 1296 + description: 'A lexicon for comments on documents', 1297 defs: { 1298 main: { 1299 type: 'record', 1300 + key: 'tid', 1301 + description: 'Record containing a comment', 1302 record: { 1303 type: 'object', 1304 + required: ['subject', 'plaintext', 'createdAt'], 1305 properties: { 1306 + subject: { 1307 type: 'string', 1308 + format: 'at-uri', 1309 }, 1310 + createdAt: { 1311 type: 'string', 1312 + format: 'datetime', 1313 }, 1314 + reply: { 1315 + type: 'ref', 1316 + ref: 'lex:pub.leaflet.comment#replyRef', 1317 + }, 1318 + plaintext: { 1319 + type: 'string', 1320 }, 1321 + facets: { 1322 + type: 'array', 1323 + items: { 1324 + type: 'ref', 1325 + ref: 'lex:pub.leaflet.richtext.facet', 1326 + }, 1327 }, 1328 + attachment: { 1329 type: 'union', 1330 + refs: ['lex:pub.leaflet.comment#linearDocumentQuote'], 1331 }, 1332 + }, 1333 + }, 1334 + }, 1335 + linearDocumentQuote: { 1336 + type: 'object', 1337 + required: ['document', 'quote'], 1338 + properties: { 1339 + document: { 1340 + type: 'string', 1341 + format: 'at-uri', 1342 + }, 1343 + quote: { 1344 + type: 'ref', 1345 + ref: 'lex:pub.leaflet.pages.linearDocument#quote', 1346 + }, 1347 + }, 1348 + }, 1349 + replyRef: { 1350 + type: 'object', 1351 + required: ['parent'], 1352 + properties: { 1353 + parent: { 1354 + type: 'string', 1355 + format: 'at-uri', 1356 + }, 1357 + }, 1358 + }, 1359 + }, 1360 + }, 1361 + PubLeafletDocument: { 1362 + lexicon: 1, 1363 + id: 'pub.leaflet.document', 1364 + revision: 1, 1365 + description: 'A lexicon for long form rich media documents', 1366 + defs: { 1367 + main: { 1368 + type: 'record', 1369 + key: 'tid', 1370 + description: 'Record containing a document', 1371 + record: { 1372 + type: 'object', 1373 + required: ['pages', 'author', 'title', 'publication'], 1374 + properties: { 1375 + title: { 1376 + type: 'string', 1377 + maxLength: 1280, 1378 + maxGraphemes: 128, 1379 + }, 1380 + postRef: { 1381 type: 'ref', 1382 ref: 'lex:com.atproto.repo.strongRef', 1383 }, 1384 + description: { 1385 + type: 'string', 1386 + maxLength: 3000, 1387 + maxGraphemes: 300, 1388 + }, 1389 + publishedAt: { 1390 + type: 'string', 1391 + format: 'datetime', 1392 + }, 1393 + publication: { 1394 + type: 'string', 1395 + format: 'at-uri', 1396 + }, 1397 + author: { 1398 + type: 'string', 1399 + format: 'at-identifier', 1400 + }, 1401 + pages: { 1402 + type: 'array', 1403 + items: { 1404 + type: 'union', 1405 + refs: ['lex:pub.leaflet.pages.linearDocument'], 1406 + }, 1407 + }, 1408 + }, 1409 + }, 1410 + }, 1411 + }, 1412 + }, 1413 + PubLeafletGraphSubscription: { 1414 + lexicon: 1, 1415 + id: 'pub.leaflet.graph.subscription', 1416 + defs: { 1417 + main: { 1418 + type: 'record', 1419 + key: 'tid', 1420 + description: 'Record declaring a subscription to a publication', 1421 + record: { 1422 + type: 'object', 1423 + required: ['publication'], 1424 + properties: { 1425 + publication: { 1426 + type: 'string', 1427 + format: 'at-uri', 1428 + }, 1429 + }, 1430 + }, 1431 + }, 1432 + }, 1433 + }, 1434 + PubLeafletPagesLinearDocument: { 1435 + lexicon: 1, 1436 + id: 'pub.leaflet.pages.linearDocument', 1437 + defs: { 1438 + main: { 1439 + type: 'object', 1440 + required: ['blocks'], 1441 + properties: { 1442 + id: { 1443 + type: 'string', 1444 + }, 1445 + blocks: { 1446 + type: 'array', 1447 + items: { 1448 type: 'ref', 1449 + ref: 'lex:pub.leaflet.pages.linearDocument#block', 1450 + }, 1451 + }, 1452 + }, 1453 + }, 1454 + block: { 1455 + type: 'object', 1456 + required: ['block'], 1457 + properties: { 1458 + block: { 1459 + type: 'union', 1460 + refs: [ 1461 + 'lex:pub.leaflet.blocks.iframe', 1462 + 'lex:pub.leaflet.blocks.text', 1463 + 'lex:pub.leaflet.blocks.blockquote', 1464 + 'lex:pub.leaflet.blocks.header', 1465 + 'lex:pub.leaflet.blocks.image', 1466 + 'lex:pub.leaflet.blocks.unorderedList', 1467 + 'lex:pub.leaflet.blocks.website', 1468 + 'lex:pub.leaflet.blocks.math', 1469 + 'lex:pub.leaflet.blocks.code', 1470 + 'lex:pub.leaflet.blocks.horizontalRule', 1471 + 'lex:pub.leaflet.blocks.bskyPost', 1472 + 'lex:pub.leaflet.blocks.page', 1473 + ], 1474 + }, 1475 + alignment: { 1476 + type: 'string', 1477 + knownValues: [ 1478 + 'lex:pub.leaflet.pages.linearDocument#textAlignLeft', 1479 + 'lex:pub.leaflet.pages.linearDocument#textAlignCenter', 1480 + 'lex:pub.leaflet.pages.linearDocument#textAlignRight', 1481 + 'lex:pub.leaflet.pages.linearDocument#textAlignJustify', 1482 + ], 1483 + }, 1484 + }, 1485 + }, 1486 + textAlignLeft: { 1487 + type: 'token', 1488 + }, 1489 + textAlignCenter: { 1490 + type: 'token', 1491 + }, 1492 + textAlignRight: { 1493 + type: 'token', 1494 + }, 1495 + quote: { 1496 + type: 'object', 1497 + required: ['start', 'end'], 1498 + properties: { 1499 + start: { 1500 + type: 'ref', 1501 + ref: 'lex:pub.leaflet.pages.linearDocument#position', 1502 + }, 1503 + end: { 1504 + type: 'ref', 1505 + ref: 'lex:pub.leaflet.pages.linearDocument#position', 1506 + }, 1507 + }, 1508 + }, 1509 + position: { 1510 + type: 'object', 1511 + required: ['block', 'offset'], 1512 + properties: { 1513 + block: { 1514 + type: 'array', 1515 + items: { 1516 + type: 'integer', 1517 + }, 1518 + }, 1519 + offset: { 1520 + type: 'integer', 1521 + }, 1522 + }, 1523 + }, 1524 + }, 1525 + }, 1526 + PubLeafletPublication: { 1527 + lexicon: 1, 1528 + id: 'pub.leaflet.publication', 1529 + defs: { 1530 + main: { 1531 + type: 'record', 1532 + key: 'tid', 1533 + description: 'Record declaring a publication', 1534 + record: { 1535 + type: 'object', 1536 + required: ['name'], 1537 + properties: { 1538 + name: { 1539 + type: 'string', 1540 + maxLength: 2000, 1541 }, 1542 + base_path: { 1543 type: 'string', 1544 + format: 'uri', 1545 + }, 1546 + description: { 1547 + type: 'string', 1548 + maxLength: 2000, 1549 + }, 1550 + icon: { 1551 + type: 'blob', 1552 + accept: ['image/*'], 1553 + maxSize: 1000000, 1554 + }, 1555 + theme: { 1556 + type: 'ref', 1557 + ref: 'lex:pub.leaflet.publication#theme', 1558 + }, 1559 + preferences: { 1560 + type: 'ref', 1561 + ref: 'lex:pub.leaflet.publication#preferences', 1562 + }, 1563 + }, 1564 + }, 1565 + }, 1566 + preferences: { 1567 + type: 'object', 1568 + properties: { 1569 + showInDiscover: { 1570 + type: 'boolean', 1571 + default: true, 1572 + }, 1573 + showComments: { 1574 + type: 'boolean', 1575 + default: true, 1576 + }, 1577 + }, 1578 + }, 1579 + theme: { 1580 + type: 'object', 1581 + properties: { 1582 + backgroundColor: { 1583 + type: 'union', 1584 + refs: [ 1585 + 'lex:pub.leaflet.theme.color#rgba', 1586 + 'lex:pub.leaflet.theme.color#rgb', 1587 + ], 1588 + }, 1589 + backgroundImage: { 1590 + type: 'ref', 1591 + ref: 'lex:pub.leaflet.theme.backgroundImage', 1592 + }, 1593 + primary: { 1594 + type: 'union', 1595 + refs: [ 1596 + 'lex:pub.leaflet.theme.color#rgba', 1597 + 'lex:pub.leaflet.theme.color#rgb', 1598 + ], 1599 + }, 1600 + pageBackground: { 1601 + type: 'union', 1602 + refs: [ 1603 + 'lex:pub.leaflet.theme.color#rgba', 1604 + 'lex:pub.leaflet.theme.color#rgb', 1605 + ], 1606 + }, 1607 + showPageBackground: { 1608 + type: 'boolean', 1609 + default: false, 1610 + }, 1611 + accentBackground: { 1612 + type: 'union', 1613 + refs: [ 1614 + 'lex:pub.leaflet.theme.color#rgba', 1615 + 'lex:pub.leaflet.theme.color#rgb', 1616 + ], 1617 + }, 1618 + accentText: { 1619 + type: 'union', 1620 + refs: [ 1621 + 'lex:pub.leaflet.theme.color#rgba', 1622 + 'lex:pub.leaflet.theme.color#rgb', 1623 + ], 1624 + }, 1625 + }, 1626 + }, 1627 + }, 1628 + }, 1629 + PubLeafletRichtextFacet: { 1630 + lexicon: 1, 1631 + id: 'pub.leaflet.richtext.facet', 1632 + defs: { 1633 + main: { 1634 + type: 'object', 1635 + description: 'Annotation of a sub-string within rich text.', 1636 + required: ['index', 'features'], 1637 + properties: { 1638 + index: { 1639 + type: 'ref', 1640 + ref: 'lex:pub.leaflet.richtext.facet#byteSlice', 1641 + }, 1642 + features: { 1643 + type: 'array', 1644 + items: { 1645 + type: 'union', 1646 + refs: [ 1647 + 'lex:pub.leaflet.richtext.facet#link', 1648 + 'lex:pub.leaflet.richtext.facet#code', 1649 + 'lex:pub.leaflet.richtext.facet#highlight', 1650 + 'lex:pub.leaflet.richtext.facet#underline', 1651 + 'lex:pub.leaflet.richtext.facet#strikethrough', 1652 + 'lex:pub.leaflet.richtext.facet#id', 1653 + 'lex:pub.leaflet.richtext.facet#bold', 1654 + 'lex:pub.leaflet.richtext.facet#italic', 1655 + ], 1656 }, 1657 }, 1658 }, 1659 }, 1660 + byteSlice: { 1661 + type: 'object', 1662 + description: 1663 + 'Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.', 1664 + required: ['byteStart', 'byteEnd'], 1665 + properties: { 1666 + byteStart: { 1667 + type: 'integer', 1668 + minimum: 0, 1669 + }, 1670 + byteEnd: { 1671 + type: 'integer', 1672 + minimum: 0, 1673 + }, 1674 + }, 1675 + }, 1676 + link: { 1677 + type: 'object', 1678 + description: 1679 + 'Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.', 1680 + required: ['uri'], 1681 + properties: { 1682 + uri: { 1683 + type: 'string', 1684 + }, 1685 + }, 1686 + }, 1687 + code: { 1688 + type: 'object', 1689 + description: 'Facet feature for inline code.', 1690 + required: [], 1691 + properties: {}, 1692 + }, 1693 + highlight: { 1694 + type: 'object', 1695 + description: 'Facet feature for highlighted text.', 1696 + required: [], 1697 + properties: {}, 1698 + }, 1699 + underline: { 1700 + type: 'object', 1701 + description: 'Facet feature for underline markup', 1702 + required: [], 1703 + properties: {}, 1704 + }, 1705 + strikethrough: { 1706 + type: 'object', 1707 + description: 'Facet feature for strikethrough markup', 1708 + required: [], 1709 + properties: {}, 1710 + }, 1711 + id: { 1712 + type: 'object', 1713 + description: 1714 + 'Facet feature for an identifier. Used for linking to a segment', 1715 + required: [], 1716 + properties: { 1717 + id: { 1718 + type: 'string', 1719 + }, 1720 + }, 1721 + }, 1722 + bold: { 1723 + type: 'object', 1724 + description: 'Facet feature for bold text', 1725 + required: [], 1726 + properties: {}, 1727 + }, 1728 + italic: { 1729 + type: 'object', 1730 + description: 'Facet feature for italic text', 1731 + required: [], 1732 + properties: {}, 1733 + }, 1734 + }, 1735 + }, 1736 + PubLeafletThemeBackgroundImage: { 1737 + lexicon: 1, 1738 + id: 'pub.leaflet.theme.backgroundImage', 1739 + defs: { 1740 + main: { 1741 + type: 'object', 1742 + required: ['image'], 1743 + properties: { 1744 + image: { 1745 + type: 'blob', 1746 + accept: ['image/*'], 1747 + maxSize: 1000000, 1748 + }, 1749 + width: { 1750 + type: 'integer', 1751 + }, 1752 + repeat: { 1753 + type: 'boolean', 1754 + }, 1755 + }, 1756 + }, 1757 + }, 1758 + }, 1759 + PubLeafletThemeColor: { 1760 + lexicon: 1, 1761 + id: 'pub.leaflet.theme.color', 1762 + defs: { 1763 + rgba: { 1764 + type: 'object', 1765 + required: ['r', 'g', 'b', 'a'], 1766 + properties: { 1767 + r: { 1768 + type: 'integer', 1769 + maximum: 255, 1770 + minimum: 0, 1771 + }, 1772 + g: { 1773 + type: 'integer', 1774 + maximum: 255, 1775 + minimum: 0, 1776 + }, 1777 + b: { 1778 + type: 'integer', 1779 + maximum: 255, 1780 + minimum: 0, 1781 + }, 1782 + a: { 1783 + type: 'integer', 1784 + maximum: 100, 1785 + minimum: 0, 1786 + }, 1787 + }, 1788 + }, 1789 + rgb: { 1790 + type: 'object', 1791 + required: ['r', 'g', 'b'], 1792 + properties: { 1793 + r: { 1794 + type: 'integer', 1795 + maximum: 255, 1796 + minimum: 0, 1797 + }, 1798 + g: { 1799 + type: 'integer', 1800 + maximum: 255, 1801 + minimum: 0, 1802 + }, 1803 + b: { 1804 + type: 'integer', 1805 + maximum: 255, 1806 + minimum: 0, 1807 + }, 1808 + }, 1809 + }, 1810 }, 1811 }, 1812 } as const satisfies Record<string, LexiconDoc> 1813 export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 1814 export const lexicons: Lexicons = new Lexicons(schemas) 1815 ··· 1842 } 1843 1844 export const ids = { 1845 + AppBskyActorProfile: 'app.bsky.actor.profile', 1846 + ComAtprotoLabelDefs: 'com.atproto.label.defs', 1847 + ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites', 1848 + ComAtprotoRepoCreateRecord: 'com.atproto.repo.createRecord', 1849 + ComAtprotoRepoDefs: 'com.atproto.repo.defs', 1850 + ComAtprotoRepoDeleteRecord: 'com.atproto.repo.deleteRecord', 1851 + ComAtprotoRepoDescribeRepo: 'com.atproto.repo.describeRepo', 1852 + ComAtprotoRepoGetRecord: 'com.atproto.repo.getRecord', 1853 + ComAtprotoRepoImportRepo: 'com.atproto.repo.importRepo', 1854 + ComAtprotoRepoListMissingBlobs: 'com.atproto.repo.listMissingBlobs', 1855 + ComAtprotoRepoListRecords: 'com.atproto.repo.listRecords', 1856 + ComAtprotoRepoPutRecord: 'com.atproto.repo.putRecord', 1857 + ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', 1858 + ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob', 1859 PubLeafletBlocksBlockquote: 'pub.leaflet.blocks.blockquote', 1860 PubLeafletBlocksBskyPost: 'pub.leaflet.blocks.bskyPost', 1861 PubLeafletBlocksCode: 'pub.leaflet.blocks.code', ··· 1868 PubLeafletBlocksText: 'pub.leaflet.blocks.text', 1869 PubLeafletBlocksUnorderedList: 'pub.leaflet.blocks.unorderedList', 1870 PubLeafletBlocksWebsite: 'pub.leaflet.blocks.website', 1871 + PubLeafletComment: 'pub.leaflet.comment', 1872 + PubLeafletDocument: 'pub.leaflet.document', 1873 PubLeafletGraphSubscription: 'pub.leaflet.graph.subscription', 1874 PubLeafletPagesLinearDocument: 'pub.leaflet.pages.linearDocument', 1875 + PubLeafletPublication: 'pub.leaflet.publication', 1876 PubLeafletRichtextFacet: 'pub.leaflet.richtext.facet', 1877 PubLeafletThemeBackgroundImage: 'pub.leaflet.theme.backgroundImage', 1878 PubLeafletThemeColor: 'pub.leaflet.theme.color', 1879 } as const
+6 -2
lexicons/api/types/app/bsky/actor/profile.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' 9 import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' 10
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs' 13 import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' 14
+6 -2
lexicons/api/types/com/atproto/label/defs.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+7 -3
lexicons/api/types/com/atproto/repo/applyWrites.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 import type * as ComAtprotoRepoDefs from './defs' 10 11 const is$typed = _is$typed, 12 validate = _validate 13 const id = 'com.atproto.repo.applyWrites' 14 15 - export interface QueryParams {} 16 17 export interface InputSchema { 18 /** The handle or DID of the repo (aka, current account). */
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 import type * as ComAtprotoRepoDefs from './defs' 14 15 const is$typed = _is$typed, 16 validate = _validate 17 const id = 'com.atproto.repo.applyWrites' 18 19 + export type QueryParams = {} 20 21 export interface InputSchema { 22 /** The handle or DID of the repo (aka, current account). */
+7 -3
lexicons/api/types/com/atproto/repo/createRecord.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 import type * as ComAtprotoRepoDefs from './defs' 10 11 const is$typed = _is$typed, 12 validate = _validate 13 const id = 'com.atproto.repo.createRecord' 14 15 - export interface QueryParams {} 16 17 export interface InputSchema { 18 /** The handle or DID of the repo (aka, current account). */
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 import type * as ComAtprotoRepoDefs from './defs' 14 15 const is$typed = _is$typed, 16 validate = _validate 17 const id = 'com.atproto.repo.createRecord' 18 19 + export type QueryParams = {} 20 21 export interface InputSchema { 22 /** The handle or DID of the repo (aka, current account). */
+6 -2
lexicons/api/types/com/atproto/repo/defs.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+7 -3
lexicons/api/types/com/atproto/repo/deleteRecord.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 import type * as ComAtprotoRepoDefs from './defs' 10 11 const is$typed = _is$typed, 12 validate = _validate 13 const id = 'com.atproto.repo.deleteRecord' 14 15 - export interface QueryParams {} 16 17 export interface InputSchema { 18 /** The handle or DID of the repo (aka, current account). */
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 import type * as ComAtprotoRepoDefs from './defs' 14 15 const is$typed = _is$typed, 16 validate = _validate 17 const id = 'com.atproto.repo.deleteRecord' 18 19 + export type QueryParams = {} 20 21 export interface InputSchema { 22 /** The handle or DID of the repo (aka, current account). */
+7 -4
lexicons/api/types/com/atproto/repo/describeRepo.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.describeRepo' 13 14 - export interface QueryParams { 15 /** The handle or DID of the repo. */ 16 repo: string 17 } 18 - 19 export type InputSchema = undefined 20 21 export interface OutputSchema {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.describeRepo' 17 18 + export type QueryParams = { 19 /** The handle or DID of the repo. */ 20 repo: string 21 } 22 export type InputSchema = undefined 23 24 export interface OutputSchema {
+7 -4
lexicons/api/types/com/atproto/repo/getRecord.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.getRecord' 13 14 - export interface QueryParams { 15 /** The handle or DID of the repo. */ 16 repo: string 17 /** The NSID of the record collection. */ ··· 21 /** The CID of the version of the record. If not specified, then return the most recent version. */ 22 cid?: string 23 } 24 - 25 export type InputSchema = undefined 26 27 export interface OutputSchema {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.getRecord' 17 18 + export type QueryParams = { 19 /** The handle or DID of the repo. */ 20 repo: string 21 /** The NSID of the record collection. */ ··· 25 /** The CID of the version of the record. If not specified, then return the most recent version. */ 26 cid?: string 27 } 28 export type InputSchema = undefined 29 30 export interface OutputSchema {
+7 -4
lexicons/api/types/com/atproto/repo/importRepo.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.importRepo' 13 14 - export interface QueryParams {} 15 - 16 export type InputSchema = string | Uint8Array | Blob 17 18 export interface CallOptions {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.importRepo' 17 18 + export type QueryParams = {} 19 export type InputSchema = string | Uint8Array | Blob 20 21 export interface CallOptions {
+7 -4
lexicons/api/types/com/atproto/repo/listMissingBlobs.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.listMissingBlobs' 13 14 - export interface QueryParams { 15 limit?: number 16 cursor?: string 17 } 18 - 19 export type InputSchema = undefined 20 21 export interface OutputSchema {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.listMissingBlobs' 17 18 + export type QueryParams = { 19 limit?: number 20 cursor?: string 21 } 22 export type InputSchema = undefined 23 24 export interface OutputSchema {
+7 -4
lexicons/api/types/com/atproto/repo/listRecords.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.listRecords' 13 14 - export interface QueryParams { 15 /** The handle or DID of the repo. */ 16 repo: string 17 /** The NSID of the record type. */ ··· 26 /** Flag to reverse the order of the returned records. */ 27 reverse?: boolean 28 } 29 - 30 export type InputSchema = undefined 31 32 export interface OutputSchema {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.listRecords' 17 18 + export type QueryParams = { 19 /** The handle or DID of the repo. */ 20 repo: string 21 /** The NSID of the record type. */ ··· 30 /** Flag to reverse the order of the returned records. */ 31 reverse?: boolean 32 } 33 export type InputSchema = undefined 34 35 export interface OutputSchema {
+7 -3
lexicons/api/types/com/atproto/repo/putRecord.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 import type * as ComAtprotoRepoDefs from './defs' 10 11 const is$typed = _is$typed, 12 validate = _validate 13 const id = 'com.atproto.repo.putRecord' 14 15 - export interface QueryParams {} 16 17 export interface InputSchema { 18 /** The handle or DID of the repo (aka, current account). */
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 import type * as ComAtprotoRepoDefs from './defs' 14 15 const is$typed = _is$typed, 16 validate = _validate 17 const id = 'com.atproto.repo.putRecord' 18 19 + export type QueryParams = {} 20 21 export interface InputSchema { 22 /** The handle or DID of the repo (aka, current account). */
+6 -2
lexicons/api/types/com/atproto/repo/strongRef.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+7 -4
lexicons/api/types/com/atproto/repo/uploadBlob.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 9 10 const is$typed = _is$typed, 11 validate = _validate 12 const id = 'com.atproto.repo.uploadBlob' 13 14 - export interface QueryParams {} 15 - 16 export type InputSchema = string | Uint8Array | Blob 17 18 export interface OutputSchema {
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 import { HeadersMap, XRPCError } from '@atproto/xrpc' 5 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 6 import { CID } from 'multiformats/cid' 7 import { validate as _validate } from '../../../../lexicons' 8 + import { 9 + type $Typed, 10 + is$typed as _is$typed, 11 + type OmitKey, 12 + } from '../../../../util' 13 14 const is$typed = _is$typed, 15 validate = _validate 16 const id = 'com.atproto.repo.uploadBlob' 17 18 + export type QueryParams = {} 19 export type InputSchema = string | Uint8Array | Blob 20 21 export interface OutputSchema {
+6 -2
lexicons/api/types/pub/leaflet/blocks/blockquote.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as PubLeafletRichtextFacet from '../richtext/facet' 9 10 const is$typed = _is$typed,
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as PubLeafletRichtextFacet from '../richtext/facet' 13 14 const is$typed = _is$typed,
+6 -2
lexicons/api/types/pub/leaflet/blocks/bskyPost.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' 9 10 const is$typed = _is$typed,
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef' 13 14 const is$typed = _is$typed,
+6 -2
lexicons/api/types/pub/leaflet/blocks/code.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/header.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as PubLeafletRichtextFacet from '../richtext/facet' 9 10 const is$typed = _is$typed,
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as PubLeafletRichtextFacet from '../richtext/facet' 13 14 const is$typed = _is$typed,
+6 -2
lexicons/api/types/pub/leaflet/blocks/horizontalRule.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/iframe.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/image.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/math.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/page.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/blocks/text.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as PubLeafletRichtextFacet from '../richtext/facet' 9 10 const is$typed = _is$typed,
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as PubLeafletRichtextFacet from '../richtext/facet' 13 14 const is$typed = _is$typed,
+6 -2
lexicons/api/types/pub/leaflet/blocks/unorderedList.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as PubLeafletBlocksText from './text' 9 import type * as PubLeafletBlocksHeader from './header' 10 import type * as PubLeafletBlocksImage from './image'
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as PubLeafletBlocksText from './text' 13 import type * as PubLeafletBlocksHeader from './header' 14 import type * as PubLeafletBlocksImage from './image'
+6 -2
lexicons/api/types/pub/leaflet/blocks/website.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+2 -2
lexicons/api/types/pub/leaflet/comment.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../util' 8 import type * as PubLeafletRichtextFacet from './richtext/facet' 9 import type * as PubLeafletPagesLinearDocument from './pages/linearDocument' 10
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 import type * as PubLeafletRichtextFacet from './richtext/facet' 9 import type * as PubLeafletPagesLinearDocument from './pages/linearDocument' 10
+2 -2
lexicons/api/types/pub/leaflet/document.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../util' 8 import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef' 9 import type * as PubLeafletPagesLinearDocument from './pages/linearDocument' 10
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef' 9 import type * as PubLeafletPagesLinearDocument from './pages/linearDocument' 10
+6 -2
lexicons/api/types/pub/leaflet/graph/subscription.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/pages/linearDocument.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 import type * as PubLeafletBlocksIframe from '../blocks/iframe' 9 import type * as PubLeafletBlocksText from '../blocks/text' 10 import type * as PubLeafletBlocksBlockquote from '../blocks/blockquote'
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 import type * as PubLeafletBlocksIframe from '../blocks/iframe' 13 import type * as PubLeafletBlocksText from '../blocks/text' 14 import type * as PubLeafletBlocksBlockquote from '../blocks/blockquote'
+2 -2
lexicons/api/types/pub/leaflet/publication.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../util' 8 import type * as PubLeafletThemeColor from './theme/color' 9 import type * as PubLeafletThemeBackgroundImage from './theme/backgroundImage' 10
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 import type * as PubLeafletThemeColor from './theme/color' 9 import type * as PubLeafletThemeBackgroundImage from './theme/backgroundImage' 10
+6 -2
lexicons/api/types/pub/leaflet/richtext/facet.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/theme/backgroundImage.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+6 -2
lexicons/api/types/pub/leaflet/theme/color.ts
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 - import { ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 - import { $Typed, is$typed as _is$typed, OmitKey } from '../../../../util' 8 9 const is$typed = _is$typed, 10 validate = _validate
··· 1 /** 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 import { CID } from 'multiformats/cid' 6 import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 13 const is$typed = _is$typed, 14 validate = _validate
+1 -1
lexicons/api/util.ts
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 5 - import { ValidationResult } from '@atproto/lexicon' 6 7 export type OmitKey<T, K extends keyof T> = { 8 [K2 in keyof T as K2 extends K ? never : K2]: T[K2]
··· 2 * GENERATED CODE - DO NOT MODIFY 3 */ 4 5 + import { type ValidationResult } from '@atproto/lexicon' 6 7 export type OmitKey<T, K extends keyof T> = { 8 [K2 in keyof T as K2 extends K ? never : K2]: T[K2]
+1 -2
lexicons/pub/leaflet/richtext/facet.json
··· 58 ], 59 "properties": { 60 "uri": { 61 - "type": "string", 62 - "format": "uri" 63 } 64 } 65 },
··· 58 ], 59 "properties": { 60 "uri": { 61 + "type": "string" 62 } 63 } 64 },
+1 -1
lexicons/src/facet.ts
··· 6 "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 7 required: ["uri"], 8 properties: { 9 - uri: { type: "string", format: "uri" }, 10 }, 11 }, 12 code: {
··· 6 "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 7 required: ["uri"], 8 properties: { 9 + uri: { type: "string" }, 10 }, 11 }, 12 code: {
+892 -1169
package-lock.json
··· 9 "version": "1.0.0", 10 "license": "ISC", 11 "dependencies": { 12 - "@atproto/api": "^0.14.2", 13 "@atproto/common": "^0.4.8", 14 "@atproto/identity": "^0.4.6", 15 - "@atproto/oauth-client-node": "^0.2.17", 16 - "@atproto/sync": "^0.1.32", 17 "@atproto/syntax": "^0.3.3", 18 - "@atproto/xrpc": "^0.6.9", 19 - "@atproto/xrpc-server": "^0.7.19", 20 "@hono/node-server": "^1.14.3", 21 "@mdx-js/loader": "^3.1.0", 22 "@mdx-js/react": "^3.1.0", ··· 48 "katex": "^0.16.22", 49 "linkifyjs": "^4.2.0", 50 "multiformats": "^13.3.2", 51 - "next": "^15.5.0", 52 "pg": "^8.16.3", 53 "prosemirror-commands": "^1.5.2", 54 "prosemirror-inputrules": "^1.4.0", ··· 83 "zustand": "^5.0.4" 84 }, 85 "devDependencies": { 86 - "@atproto/lex-cli": "^0.6.1", 87 - "@atproto/lexicon": "^0.4.7", 88 "@cloudflare/workers-types": "^4.20240512.0", 89 "@types/katex": "^0.16.7", 90 "@types/node": "^22.15.17", 91 "@types/react": "19.1.3", 92 "@types/react-dom": "19.1.3", 93 "@types/uuid": "^10.0.0", 94 - "autoprefixer": "^10.4.19", 95 "drizzle-kit": "^0.21.2", 96 "esbuild": "^0.25.4", 97 "eslint": "8.57.0", 98 - "eslint-config-next": "^15.4.4", 99 "postcss": "^8.4.38", 100 "prettier": "3.2.5", 101 "supabase": "^1.187.3", 102 - "tailwindcss": "^3.4.3", 103 "tsx": "^4.19.3", 104 "typescript": "^5.8.3", 105 "wrangler": "^3.56.0" ··· 110 "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", 111 "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", 112 "dev": true, 113 "engines": { 114 "node": ">=10" 115 }, ··· 118 } 119 }, 120 "node_modules/@atproto-labs/did-resolver": { 121 - "version": "0.1.12", 122 - "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.1.12.tgz", 123 - "integrity": "sha512-criWN7o21C5TFsauB+bGTlkqqerOU6gT2TbxdQVgZUWqNcfazUmUjT4gJAY02i+O4d3QmZa27fv9CcaRKWkSug==", 124 "license": "MIT", 125 "dependencies": { 126 - "@atproto-labs/fetch": "0.2.2", 127 - "@atproto-labs/pipe": "0.1.0", 128 - "@atproto-labs/simple-store": "0.2.0", 129 - "@atproto-labs/simple-store-memory": "0.1.3", 130 - "@atproto/did": "0.1.5", 131 "zod": "^3.23.8" 132 } 133 }, 134 "node_modules/@atproto-labs/fetch": { 135 - "version": "0.2.2", 136 - "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.2.tgz", 137 - "integrity": "sha512-QyafkedbFeVaN20DYUpnY2hcArYxjdThPXbYMqOSoZhcvkrUqaw4xDND4wZB5TBD9cq2yqe9V6mcw9P4XQKQuQ==", 138 "license": "MIT", 139 "dependencies": { 140 - "@atproto-labs/pipe": "0.1.0" 141 } 142 }, 143 "node_modules/@atproto-labs/fetch-node": { 144 - "version": "0.1.8", 145 - "resolved": "https://registry.npmjs.org/@atproto-labs/fetch-node/-/fetch-node-0.1.8.tgz", 146 - "integrity": "sha512-OOTIhZNPEDDm7kaYU8iYRgzM+D5n3mP2iiBSyKuLakKTaZBL5WwYlUsJVsqX26SnUXtGEroOJEVJ6f66OcG80w==", 147 "license": "MIT", 148 "dependencies": { 149 - "@atproto-labs/fetch": "0.2.2", 150 - "@atproto-labs/pipe": "0.1.0", 151 "ipaddr.js": "^2.1.0", 152 - "psl": "^1.9.0", 153 "undici": "^6.14.1" 154 }, 155 "engines": { ··· 157 } 158 }, 159 "node_modules/@atproto-labs/handle-resolver": { 160 - "version": "0.1.8", 161 - "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.1.8.tgz", 162 - "integrity": "sha512-Y0ckccoCGDo/3g4thPkgp9QcORmc+qqEaCBCYCZYtfLIQp4775u22wd+4fyEyJP4DqoReKacninkICgRGfs3dQ==", 163 "license": "MIT", 164 "dependencies": { 165 - "@atproto-labs/simple-store": "0.2.0", 166 - "@atproto-labs/simple-store-memory": "0.1.3", 167 - "@atproto/did": "0.1.5", 168 "zod": "^3.23.8" 169 } 170 }, 171 "node_modules/@atproto-labs/handle-resolver-node": { 172 - "version": "0.1.15", 173 - "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver-node/-/handle-resolver-node-0.1.15.tgz", 174 - "integrity": "sha512-krl9KqfCCrGID35VAAHKBIiXOxe3gYxAtOJLYpZc5cOPFwnvPlAdhTYZLIc1dJRKDayi8gh6Q4XZRDv7i8dryg==", 175 "license": "MIT", 176 "dependencies": { 177 - "@atproto-labs/fetch-node": "0.1.8", 178 - "@atproto-labs/handle-resolver": "0.1.8", 179 - "@atproto/did": "0.1.5" 180 }, 181 "engines": { 182 "node": ">=18.7.0" 183 } 184 }, 185 "node_modules/@atproto-labs/identity-resolver": { 186 - "version": "0.1.16", 187 - "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.1.16.tgz", 188 - "integrity": "sha512-pFrtKT49cYBhCDd2U1t/CcUBiMmQzaNQxh8oSkDUlGs/K3P8rJFTAGAMm8UjokfGEKwF4hX9oo7O8Kn+GkyExw==", 189 "license": "MIT", 190 "dependencies": { 191 - "@atproto-labs/did-resolver": "0.1.12", 192 - "@atproto-labs/handle-resolver": "0.1.8", 193 - "@atproto/syntax": "0.4.0" 194 } 195 }, 196 - "node_modules/@atproto-labs/identity-resolver/node_modules/@atproto/syntax": { 197 - "version": "0.4.0", 198 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.0.tgz", 199 - "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==", 200 - "license": "MIT" 201 - }, 202 "node_modules/@atproto-labs/pipe": { 203 - "version": "0.1.0", 204 - "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.0.tgz", 205 - "integrity": "sha512-ghOqHFyJlQVFPESzlVHjKroP0tPzbmG5Jms0dNI9yLDEfL8xp4OFPWLX4f6T8mRq69wWs4nIDM3sSsFbFqLa1w==", 206 "license": "MIT" 207 }, 208 "node_modules/@atproto-labs/simple-store": { 209 - "version": "0.2.0", 210 - "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.2.0.tgz", 211 - "integrity": "sha512-0bRbAlI8Ayh03wRwncAMEAyUKtZ+AuTS1jgPrfym1WVOAOiottI/ZmgccqLl6w5MbxVcClNQF7WYGKvGwGoIhA==", 212 "license": "MIT" 213 }, 214 "node_modules/@atproto-labs/simple-store-memory": { 215 - "version": "0.1.3", 216 - "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.3.tgz", 217 - "integrity": "sha512-jkitT9+AtU+0b28DoN92iURLaCt/q/q4yX8q6V+9LSwYlUTqKoj/5NFKvF7x6EBuG+gpUdlcycbH7e60gjOhRQ==", 218 "license": "MIT", 219 "dependencies": { 220 - "@atproto-labs/simple-store": "0.2.0", 221 "lru-cache": "^10.2.0" 222 } 223 }, 224 "node_modules/@atproto/api": { 225 - "version": "0.14.9", 226 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.14.9.tgz", 227 - "integrity": "sha512-9S7Vl6gK8hmy0+Gw5AWriUsQfQYZxwW7yTK5UdPPGlmjvQP49YeSLrsxB1ZkTyrlLSG2tpYrGsN4vco6KOORAQ==", 228 "license": "MIT", 229 "dependencies": { 230 - "@atproto/common-web": "^0.4.0", 231 - "@atproto/lexicon": "^0.4.8", 232 - "@atproto/syntax": "^0.3.4", 233 - "@atproto/xrpc": "^0.6.10", 234 "await-lock": "^2.2.2", 235 "multiformats": "^9.9.0", 236 "tlds": "^1.234.0", 237 "zod": "^3.23.8" 238 } 239 }, 240 "node_modules/@atproto/api/node_modules/multiformats": { 241 "version": "9.9.0", 242 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 244 "license": "(Apache-2.0 AND MIT)" 245 }, 246 "node_modules/@atproto/common": { 247 - "version": "0.4.11", 248 - "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.4.11.tgz", 249 - "integrity": "sha512-Knv0viYXNMfCdIE7jLUiWJKnnMfEwg+vz2epJQi8WOjqtqCFb3W/3Jn72ZiuovIfpdm13MaOiny6w2NErUQC6g==", 250 "license": "MIT", 251 "dependencies": { 252 - "@atproto/common-web": "^0.4.2", 253 "@ipld/dag-cbor": "^7.0.3", 254 "cbor-x": "^1.5.1", 255 "iso-datestring-validator": "^2.2.2", ··· 261 } 262 }, 263 "node_modules/@atproto/common-web": { 264 - "version": "0.4.2", 265 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.2.tgz", 266 - "integrity": "sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw==", 267 "license": "MIT", 268 "dependencies": { 269 "graphemer": "^1.4.0", ··· 299 } 300 }, 301 "node_modules/@atproto/did": { 302 - "version": "0.1.5", 303 - "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.1.5.tgz", 304 - "integrity": "sha512-8+1D08QdGE5TF0bB0vV8HLVrVZJeLNITpRTUVEoABNMRaUS7CoYSVb0+JNQDeJIVmqMjOL8dOjvCUDkp3gEaGQ==", 305 "license": "MIT", 306 "dependencies": { 307 "zod": "^3.23.8" 308 } 309 }, 310 "node_modules/@atproto/identity": { 311 - "version": "0.4.8", 312 - "resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.8.tgz", 313 - "integrity": "sha512-Z0sLnJ87SeNdAifT+rqpgE1Rc3layMMW25gfWNo4u40RGuRODbdfAZlTwBSU2r+Vk45hU+iE+xeQspfednCEnA==", 314 "license": "MIT", 315 "dependencies": { 316 - "@atproto/common-web": "^0.4.2", 317 "@atproto/crypto": "^0.4.4" 318 }, 319 "engines": { ··· 321 } 322 }, 323 "node_modules/@atproto/jwk": { 324 - "version": "0.1.5", 325 - "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.1.5.tgz", 326 - "integrity": "sha512-OzZFLhX41TOcMeanP3aZlL5bLeaUIZT15MI4aU5cwflNq/rwpGOpz3uwDjZc8ytgUjuTQ8LabSz5jMmwoTSWFg==", 327 "license": "MIT", 328 "dependencies": { 329 "multiformats": "^9.9.0", ··· 331 } 332 }, 333 "node_modules/@atproto/jwk-jose": { 334 - "version": "0.1.6", 335 - "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.6.tgz", 336 - "integrity": "sha512-r4DGMvvmazy6CxqAcnplpUxvp6Vd8UwKxQBZRpmm1aNsVonf5qj1yeDkECTiwoe/FPbvtdamlzClB3UZc7Yb5w==", 337 "license": "MIT", 338 "dependencies": { 339 - "@atproto/jwk": "0.1.5", 340 "jose": "^5.2.0" 341 } 342 }, 343 "node_modules/@atproto/jwk-webcrypto": { 344 - "version": "0.1.6", 345 - "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.1.6.tgz", 346 - "integrity": "sha512-mxWHOvlg+HGohldfiaon1fNsr7iDvKrTrkV0/ZvymWRzxsDFPCon1hu8OtKLXUVgLh+IzDJT1D79I4fBSo4pog==", 347 "license": "MIT", 348 "dependencies": { 349 - "@atproto/jwk": "0.1.5", 350 - "@atproto/jwk-jose": "0.1.6", 351 "zod": "^3.23.8" 352 } 353 }, ··· 358 "license": "(Apache-2.0 AND MIT)" 359 }, 360 "node_modules/@atproto/lex-cli": { 361 - "version": "0.6.2", 362 - "resolved": "https://registry.npmjs.org/@atproto/lex-cli/-/lex-cli-0.6.2.tgz", 363 - "integrity": "sha512-YbypfXBOYdZQATgQRRMpTwcG42/JmRs3iYnN6hQH4ljjn+t5nbDtdZjUF5okiWI7LK+yvOrgagxosIAjjjSe8Q==", 364 "dev": true, 365 "license": "MIT", 366 "dependencies": { 367 - "@atproto/lexicon": "^0.4.8", 368 - "@atproto/syntax": "^0.3.4", 369 "chalk": "^4.1.2", 370 "commander": "^9.4.0", 371 "prettier": "^3.2.5", 372 - "ts-morph": "^16.0.0", 373 "yesno": "^0.4.0", 374 "zod": "^3.23.8" 375 }, ··· 379 "engines": { 380 "node": ">=18.7.0" 381 } 382 }, 383 "node_modules/@atproto/lexicon": { 384 - "version": "0.4.14", 385 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.14.tgz", 386 - "integrity": "sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ==", 387 "license": "MIT", 388 "dependencies": { 389 - "@atproto/common-web": "^0.4.2", 390 - "@atproto/syntax": "^0.4.0", 391 "iso-datestring-validator": "^2.2.2", 392 "multiformats": "^9.9.0", 393 "zod": "^3.23.8" 394 } 395 }, 396 "node_modules/@atproto/lexicon/node_modules/@atproto/syntax": { 397 - "version": "0.4.0", 398 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.0.tgz", 399 - "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==", 400 "license": "MIT" 401 }, 402 "node_modules/@atproto/lexicon/node_modules/multiformats": { ··· 406 "license": "(Apache-2.0 AND MIT)" 407 }, 408 "node_modules/@atproto/oauth-client": { 409 - "version": "0.3.16", 410 - "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.3.16.tgz", 411 - "integrity": "sha512-AEtGLOXRJzBcBa8LyUXwFf/M7cZc+CcOBjLsiqmVQriSwccfyTkALgiyM0UcRHJqlwtLPuf9RYtgKPc8rW5F/w==", 412 "license": "MIT", 413 "dependencies": { 414 - "@atproto-labs/did-resolver": "0.1.12", 415 - "@atproto-labs/fetch": "0.2.2", 416 - "@atproto-labs/handle-resolver": "0.1.8", 417 - "@atproto-labs/identity-resolver": "0.1.16", 418 - "@atproto-labs/simple-store": "0.2.0", 419 - "@atproto-labs/simple-store-memory": "0.1.3", 420 - "@atproto/did": "0.1.5", 421 - "@atproto/jwk": "0.1.5", 422 - "@atproto/oauth-types": "0.2.7", 423 - "@atproto/xrpc": "0.7.0", 424 "multiformats": "^9.9.0", 425 "zod": "^3.23.8" 426 } 427 }, 428 "node_modules/@atproto/oauth-client-node": { 429 - "version": "0.2.17", 430 - "resolved": "https://registry.npmjs.org/@atproto/oauth-client-node/-/oauth-client-node-0.2.17.tgz", 431 - "integrity": "sha512-mPaBEZlsEAhUvhr958tPrQA4T+wDv4I6MLPTF7QNzTG1n/kP2LEIStq1Y63UVDwEqTS16LnpsZodu3QZnrNxXg==", 432 "license": "MIT", 433 "dependencies": { 434 - "@atproto-labs/did-resolver": "0.1.12", 435 - "@atproto-labs/handle-resolver-node": "0.1.15", 436 - "@atproto-labs/simple-store": "0.2.0", 437 - "@atproto/did": "0.1.5", 438 - "@atproto/jwk": "0.1.5", 439 - "@atproto/jwk-jose": "0.1.6", 440 - "@atproto/jwk-webcrypto": "0.1.6", 441 - "@atproto/oauth-client": "0.3.16", 442 - "@atproto/oauth-types": "0.2.7" 443 }, 444 "engines": { 445 "node": ">=18.7.0" 446 } 447 }, 448 - "node_modules/@atproto/oauth-client/node_modules/@atproto/xrpc": { 449 - "version": "0.7.0", 450 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.0.tgz", 451 - "integrity": "sha512-SfhP9dGx2qclaScFDb58Jnrmim5nk4geZXCqg6sB0I/KZhZEkr9iIx1hLCp+sxkIfEsmEJjeWO4B0rjUIJW5cw==", 452 - "license": "MIT", 453 - "dependencies": { 454 - "@atproto/lexicon": "^0.4.11", 455 - "zod": "^3.23.8" 456 - } 457 - }, 458 "node_modules/@atproto/oauth-client/node_modules/multiformats": { 459 "version": "9.9.0", 460 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 462 "license": "(Apache-2.0 AND MIT)" 463 }, 464 "node_modules/@atproto/oauth-types": { 465 - "version": "0.2.7", 466 - "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.2.7.tgz", 467 - "integrity": "sha512-2SlDveiSI0oowC+sfuNd/npV8jw/FhokSS26qyUyldTg1g9ZlhxXUfMP4IZOPeZcVn9EszzQRHs1H9ZJqVQIew==", 468 "license": "MIT", 469 "dependencies": { 470 - "@atproto/jwk": "0.1.5", 471 "zod": "^3.23.8" 472 } 473 }, 474 "node_modules/@atproto/repo": { 475 - "version": "0.8.7", 476 - "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.7.tgz", 477 - "integrity": "sha512-KFn2bDj1XfIX2BoXg4CvgUtwWKpm7gXAFd0upuDDuDtbdIDvemyq/ZzDfY4P9nBLtE6KUNZeobtKcMtveWbhkg==", 478 "license": "MIT", 479 "dependencies": { 480 - "@atproto/common": "^0.4.11", 481 - "@atproto/common-web": "^0.4.2", 482 "@atproto/crypto": "^0.4.4", 483 - "@atproto/lexicon": "^0.4.14", 484 "@ipld/dag-cbor": "^7.0.0", 485 "multiformats": "^9.9.0", 486 "uint8arrays": "3.0.0", ··· 498 "license": "(Apache-2.0 AND MIT)" 499 }, 500 "node_modules/@atproto/sync": { 501 - "version": "0.1.32", 502 - "resolved": "https://registry.npmjs.org/@atproto/sync/-/sync-0.1.32.tgz", 503 - "integrity": "sha512-8aXr8xyJASclXZ5WWp6p8xic1vIrNMhP1ZWYBSFl3QkyPUEmzzTJs4e1cjCVen1sPsxyLOXaVWHRMyqu621+GA==", 504 "license": "MIT", 505 "dependencies": { 506 - "@atproto/common": "^0.4.11", 507 - "@atproto/identity": "^0.4.8", 508 - "@atproto/lexicon": "^0.4.14", 509 - "@atproto/repo": "^0.8.7", 510 - "@atproto/syntax": "^0.4.0", 511 - "@atproto/xrpc-server": "^0.9.3", 512 "multiformats": "^9.9.0", 513 "p-queue": "^6.6.2", 514 "ws": "^8.12.0" ··· 518 } 519 }, 520 "node_modules/@atproto/sync/node_modules/@atproto/syntax": { 521 - "version": "0.4.0", 522 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.0.tgz", 523 - "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==", 524 "license": "MIT" 525 }, 526 - "node_modules/@atproto/sync/node_modules/@atproto/xrpc": { 527 - "version": "0.7.3", 528 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.3.tgz", 529 - "integrity": "sha512-JaJbZ4ymIJzOakR3B/B+6NyppW3oQWn06OtQq03LqVsu93Afpc8VkNtPN3QnhQcD/yYSYCu73lLsDM/ErJEk7Q==", 530 - "license": "MIT", 531 - "dependencies": { 532 - "@atproto/lexicon": "^0.4.14", 533 - "zod": "^3.23.8" 534 - } 535 - }, 536 - "node_modules/@atproto/sync/node_modules/@atproto/xrpc-server": { 537 - "version": "0.9.3", 538 - "resolved": "https://registry.npmjs.org/@atproto/xrpc-server/-/xrpc-server-0.9.3.tgz", 539 - "integrity": "sha512-nKQagbjNPzdapJ9HEbqeCajWC/iSatIvqs9s5OiEm3eJeSLyQUfOIwVuS5TdhcmZ96S0ALZPE8juVexTxz1pZg==", 540 - "license": "MIT", 541 - "dependencies": { 542 - "@atproto/common": "^0.4.11", 543 - "@atproto/crypto": "^0.4.4", 544 - "@atproto/lexicon": "^0.4.14", 545 - "@atproto/xrpc": "^0.7.3", 546 - "cbor-x": "^1.5.1", 547 - "express": "^4.17.2", 548 - "http-errors": "^2.0.0", 549 - "mime-types": "^2.1.35", 550 - "rate-limiter-flexible": "^2.4.1", 551 - "uint8arrays": "3.0.0", 552 - "ws": "^8.12.0", 553 - "zod": "^3.23.8" 554 - }, 555 - "engines": { 556 - "node": ">=18.7.0" 557 - } 558 - }, 559 "node_modules/@atproto/sync/node_modules/multiformats": { 560 "version": "9.9.0", 561 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 569 "license": "MIT" 570 }, 571 "node_modules/@atproto/xrpc": { 572 - "version": "0.6.10", 573 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.10.tgz", 574 - "integrity": "sha512-ClMiO+oAl3KrFe7sdo8Wzw81yV7EpEradZLJnYilPq4s7uF0by1jHGI/LarHBKHnE5RpaFpBC/5XD/ZzgmvAeg==", 575 "license": "MIT", 576 "dependencies": { 577 - "@atproto/lexicon": "^0.4.8", 578 "zod": "^3.23.8" 579 } 580 }, 581 "node_modules/@atproto/xrpc-server": { 582 - "version": "0.7.19", 583 - "resolved": "https://registry.npmjs.org/@atproto/xrpc-server/-/xrpc-server-0.7.19.tgz", 584 - "integrity": "sha512-YSCl/tU2NDykgDYslFSOYCr96esUgDwncFiADKL59/fyIFPLoT0qY8Uq/budpxUh0qPzjow4HHgVWESOaOpUmA==", 585 "license": "MIT", 586 "dependencies": { 587 - "@atproto/common": "^0.4.11", 588 "@atproto/crypto": "^0.4.4", 589 - "@atproto/lexicon": "^0.4.11", 590 - "@atproto/xrpc": "^0.7.0", 591 "cbor-x": "^1.5.1", 592 "express": "^4.17.2", 593 "http-errors": "^2.0.0", ··· 599 }, 600 "engines": { 601 "node": ">=18.7.0" 602 - } 603 - }, 604 - "node_modules/@atproto/xrpc-server/node_modules/@atproto/xrpc": { 605 - "version": "0.7.0", 606 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.0.tgz", 607 - "integrity": "sha512-SfhP9dGx2qclaScFDb58Jnrmim5nk4geZXCqg6sB0I/KZhZEkr9iIx1hLCp+sxkIfEsmEJjeWO4B0rjUIJW5cw==", 608 - "license": "MIT", 609 - "dependencies": { 610 - "@atproto/lexicon": "^0.4.11", 611 - "zod": "^3.23.8" 612 } 613 }, 614 "node_modules/@babel/helper-string-parser": { ··· 1192 "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 1193 "license": "(Apache-2.0 AND MIT)" 1194 }, 1195 - "node_modules/@isaacs/cliui": { 1196 - "version": "8.0.2", 1197 - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", 1198 - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", 1199 - "dev": true, 1200 - "dependencies": { 1201 - "string-width": "^5.1.2", 1202 - "string-width-cjs": "npm:string-width@^4.2.0", 1203 - "strip-ansi": "^7.0.1", 1204 - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", 1205 - "wrap-ansi": "^8.1.0", 1206 - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" 1207 - }, 1208 - "engines": { 1209 - "node": ">=12" 1210 - } 1211 - }, 1212 - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { 1213 - "version": "6.0.1", 1214 - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", 1215 - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", 1216 - "dev": true, 1217 - "engines": { 1218 - "node": ">=12" 1219 - }, 1220 - "funding": { 1221 - "url": "https://github.com/chalk/ansi-regex?sponsor=1" 1222 - } 1223 - }, 1224 - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { 1225 - "version": "6.2.1", 1226 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 1227 - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 1228 - "dev": true, 1229 - "engines": { 1230 - "node": ">=12" 1231 - }, 1232 - "funding": { 1233 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1234 - } 1235 - }, 1236 - "node_modules/@isaacs/cliui/node_modules/string-width": { 1237 - "version": "5.1.2", 1238 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", 1239 - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", 1240 - "dev": true, 1241 - "dependencies": { 1242 - "eastasianwidth": "^0.2.0", 1243 - "emoji-regex": "^9.2.2", 1244 - "strip-ansi": "^7.0.1" 1245 - }, 1246 - "engines": { 1247 - "node": ">=12" 1248 - }, 1249 - "funding": { 1250 - "url": "https://github.com/sponsors/sindresorhus" 1251 - } 1252 - }, 1253 - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { 1254 - "version": "7.1.0", 1255 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 1256 - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 1257 - "dev": true, 1258 - "dependencies": { 1259 - "ansi-regex": "^6.0.1" 1260 - }, 1261 - "engines": { 1262 - "node": ">=12" 1263 - }, 1264 - "funding": { 1265 - "url": "https://github.com/chalk/strip-ansi?sponsor=1" 1266 - } 1267 - }, 1268 - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { 1269 - "version": "8.1.0", 1270 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", 1271 - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", 1272 - "dev": true, 1273 - "dependencies": { 1274 - "ansi-styles": "^6.1.0", 1275 - "string-width": "^5.0.1", 1276 - "strip-ansi": "^7.0.1" 1277 - }, 1278 - "engines": { 1279 - "node": ">=12" 1280 - }, 1281 - "funding": { 1282 - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1283 - } 1284 - }, 1285 "node_modules/@isaacs/fs-minipass": { 1286 "version": "4.0.1", 1287 "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", ··· 1301 "license": "MIT" 1302 }, 1303 "node_modules/@jridgewell/gen-mapping": { 1304 - "version": "0.3.5", 1305 - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", 1306 - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", 1307 "dev": true, 1308 "dependencies": { 1309 - "@jridgewell/set-array": "^1.2.1", 1310 - "@jridgewell/sourcemap-codec": "^1.4.10", 1311 "@jridgewell/trace-mapping": "^0.3.24" 1312 - }, 1313 - "engines": { 1314 - "node": ">=6.0.0" 1315 } 1316 }, 1317 "node_modules/@jridgewell/resolve-uri": { ··· 1323 "node": ">=6.0.0" 1324 } 1325 }, 1326 - "node_modules/@jridgewell/set-array": { 1327 - "version": "1.2.1", 1328 - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 1329 - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 1330 "dev": true, 1331 - "engines": { 1332 - "node": ">=6.0.0" 1333 - } 1334 - }, 1335 - "node_modules/@jridgewell/sourcemap-codec": { 1336 - "version": "1.4.15", 1337 - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 1338 - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 1339 - "dev": true 1340 }, 1341 "node_modules/@jridgewell/trace-mapping": { 1342 - "version": "0.3.25", 1343 - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 1344 - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 1345 "dev": true, 1346 "dependencies": { 1347 "@jridgewell/resolve-uri": "^3.1.0", 1348 "@jridgewell/sourcemap-codec": "^1.4.14" ··· 1464 } 1465 }, 1466 "node_modules/@next/env": { 1467 - "version": "15.5.0", 1468 - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.0.tgz", 1469 - "integrity": "sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==", 1470 "license": "MIT" 1471 }, 1472 "node_modules/@next/eslint-plugin-next": { 1473 - "version": "15.4.4", 1474 - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.4.tgz", 1475 - "integrity": "sha512-1FDsyN//ai3Jd97SEd7scw5h1yLdzDACGOPRofr2GD3sEFsBylEEoL0MHSerd4n2dq9Zm/mFMqi4+NRMOreOKA==", 1476 "dev": true, 1477 "license": "MIT", 1478 "dependencies": { ··· 1539 } 1540 }, 1541 "node_modules/@next/swc-darwin-arm64": { 1542 - "version": "15.5.0", 1543 - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.0.tgz", 1544 - "integrity": "sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==", 1545 "cpu": [ 1546 "arm64" 1547 ], ··· 1555 } 1556 }, 1557 "node_modules/@next/swc-darwin-x64": { 1558 - "version": "15.5.0", 1559 - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.0.tgz", 1560 - "integrity": "sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==", 1561 "cpu": [ 1562 "x64" 1563 ], ··· 1571 } 1572 }, 1573 "node_modules/@next/swc-linux-arm64-gnu": { 1574 - "version": "15.5.0", 1575 - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.0.tgz", 1576 - "integrity": "sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==", 1577 "cpu": [ 1578 "arm64" 1579 ], ··· 1587 } 1588 }, 1589 "node_modules/@next/swc-linux-arm64-musl": { 1590 - "version": "15.5.0", 1591 - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.0.tgz", 1592 - "integrity": "sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==", 1593 "cpu": [ 1594 "arm64" 1595 ], ··· 1603 } 1604 }, 1605 "node_modules/@next/swc-linux-x64-gnu": { 1606 - "version": "15.5.0", 1607 - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.0.tgz", 1608 - "integrity": "sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==", 1609 "cpu": [ 1610 "x64" 1611 ], ··· 1619 } 1620 }, 1621 "node_modules/@next/swc-linux-x64-musl": { 1622 - "version": "15.5.0", 1623 - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.0.tgz", 1624 - "integrity": "sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==", 1625 "cpu": [ 1626 "x64" 1627 ], ··· 1635 } 1636 }, 1637 "node_modules/@next/swc-win32-arm64-msvc": { 1638 - "version": "15.5.0", 1639 - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.0.tgz", 1640 - "integrity": "sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==", 1641 "cpu": [ 1642 "arm64" 1643 ], ··· 1651 } 1652 }, 1653 "node_modules/@next/swc-win32-x64-msvc": { 1654 - "version": "15.5.0", 1655 - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.0.tgz", 1656 - "integrity": "sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==", 1657 "cpu": [ 1658 "x64" 1659 ], ··· 3053 }, 3054 "peerDependencies": { 3055 "@opentelemetry/api": "^1.1.0" 3056 - } 3057 - }, 3058 - "node_modules/@pkgjs/parseargs": { 3059 - "version": "0.11.0", 3060 - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", 3061 - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", 3062 - "dev": true, 3063 - "optional": true, 3064 - "engines": { 3065 - "node": ">=14" 3066 } 3067 }, 3068 "node_modules/@polka/url": { ··· 5919 "tslib": "^2.8.0" 5920 } 5921 }, 5922 "node_modules/@tiptap/core": { 5923 "version": "2.11.5", 5924 "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.5.tgz", ··· 5964 } 5965 }, 5966 "node_modules/@ts-morph/common": { 5967 - "version": "0.17.0", 5968 - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.17.0.tgz", 5969 - "integrity": "sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==", 5970 "dev": true, 5971 "license": "MIT", 5972 "dependencies": { 5973 - "fast-glob": "^3.2.11", 5974 - "minimatch": "^5.1.0", 5975 - "mkdirp": "^1.0.4", 5976 - "path-browserify": "^1.0.1" 5977 } 5978 }, 5979 "node_modules/@ts-morph/common/node_modules/brace-expansion": { 5980 - "version": "2.0.1", 5981 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 5982 - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 5983 "dev": true, 5984 "license": "MIT", 5985 "dependencies": { ··· 5987 } 5988 }, 5989 "node_modules/@ts-morph/common/node_modules/minimatch": { 5990 - "version": "5.1.6", 5991 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 5992 - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 5993 "dev": true, 5994 "license": "ISC", 5995 "dependencies": { 5996 "brace-expansion": "^2.0.1" 5997 }, 5998 "engines": { 5999 - "node": ">=10" 6000 - } 6001 - }, 6002 - "node_modules/@ts-morph/common/node_modules/mkdirp": { 6003 - "version": "1.0.4", 6004 - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 6005 - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 6006 - "dev": true, 6007 - "license": "MIT", 6008 - "bin": { 6009 - "mkdirp": "bin/cmd.js" 6010 }, 6011 - "engines": { 6012 - "node": ">=10" 6013 } 6014 }, 6015 "node_modules/@types/acorn": { ··· 6659 "url": "https://github.com/chalk/ansi-styles?sponsor=1" 6660 } 6661 }, 6662 - "node_modules/any-promise": { 6663 - "version": "1.3.0", 6664 - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 6665 - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", 6666 - "dev": true 6667 - }, 6668 "node_modules/anymatch": { 6669 "version": "3.1.3", 6670 "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", ··· 6677 "engines": { 6678 "node": ">= 8" 6679 } 6680 - }, 6681 - "node_modules/arg": { 6682 - "version": "5.0.2", 6683 - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 6684 - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 6685 - "dev": true 6686 }, 6687 "node_modules/argparse": { 6688 "version": "2.0.1", ··· 6918 "node": ">=8.0.0" 6919 } 6920 }, 6921 - "node_modules/autoprefixer": { 6922 - "version": "10.4.19", 6923 - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", 6924 - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", 6925 - "dev": true, 6926 - "funding": [ 6927 - { 6928 - "type": "opencollective", 6929 - "url": "https://opencollective.com/postcss/" 6930 - }, 6931 - { 6932 - "type": "tidelift", 6933 - "url": "https://tidelift.com/funding/github/npm/autoprefixer" 6934 - }, 6935 - { 6936 - "type": "github", 6937 - "url": "https://github.com/sponsors/ai" 6938 - } 6939 - ], 6940 - "dependencies": { 6941 - "browserslist": "^4.23.0", 6942 - "caniuse-lite": "^1.0.30001599", 6943 - "fraction.js": "^4.3.7", 6944 - "normalize-range": "^0.1.2", 6945 - "picocolors": "^1.0.0", 6946 - "postcss-value-parser": "^4.2.0" 6947 - }, 6948 - "bin": { 6949 - "autoprefixer": "bin/autoprefixer" 6950 - }, 6951 - "engines": { 6952 - "node": "^10 || ^12 || >=14" 6953 - }, 6954 - "peerDependencies": { 6955 - "postcss": "^8.1.0" 6956 - } 6957 - }, 6958 "node_modules/available-typed-arrays": { 6959 "version": "1.0.7", 6960 "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", ··· 7169 "node": ">=8" 7170 } 7171 }, 7172 - "node_modules/browserslist": { 7173 - "version": "4.23.0", 7174 - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", 7175 - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", 7176 - "dev": true, 7177 - "funding": [ 7178 - { 7179 - "type": "opencollective", 7180 - "url": "https://opencollective.com/browserslist" 7181 - }, 7182 - { 7183 - "type": "tidelift", 7184 - "url": "https://tidelift.com/funding/github/npm/browserslist" 7185 - }, 7186 - { 7187 - "type": "github", 7188 - "url": "https://github.com/sponsors/ai" 7189 - } 7190 - ], 7191 - "dependencies": { 7192 - "caniuse-lite": "^1.0.30001587", 7193 - "electron-to-chromium": "^1.4.668", 7194 - "node-releases": "^2.0.14", 7195 - "update-browserslist-db": "^1.0.13" 7196 - }, 7197 - "bin": { 7198 - "browserslist": "cli.js" 7199 - }, 7200 - "engines": { 7201 - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 7202 - } 7203 - }, 7204 "node_modules/buffer": { 7205 "version": "6.0.3", 7206 "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", ··· 7303 "node": ">=6" 7304 } 7305 }, 7306 - "node_modules/camelcase-css": { 7307 - "version": "2.0.1", 7308 - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 7309 - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 7310 - "dev": true, 7311 - "engines": { 7312 - "node": ">= 6" 7313 - } 7314 - }, 7315 "node_modules/caniuse-lite": { 7316 "version": "1.0.30001717", 7317 "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", ··· 7561 } 7562 }, 7563 "node_modules/code-block-writer": { 7564 - "version": "11.0.3", 7565 - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", 7566 - "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", 7567 "dev": true, 7568 "license": "MIT" 7569 }, ··· 7740 "node": ">= 8" 7741 } 7742 }, 7743 - "node_modules/cssesc": { 7744 - "version": "3.0.0", 7745 - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 7746 - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 7747 - "dev": true, 7748 - "bin": { 7749 - "cssesc": "bin/cssesc" 7750 - }, 7751 - "engines": { 7752 - "node": ">=4" 7753 - } 7754 - }, 7755 "node_modules/csstype": { 7756 "version": "3.1.3", 7757 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", ··· 8006 "url": "https://github.com/sponsors/wooorm" 8007 } 8008 }, 8009 - "node_modules/didyoumean": { 8010 - "version": "1.2.2", 8011 - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 8012 - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 8013 - "dev": true 8014 - }, 8015 "node_modules/difflib": { 8016 "version": "0.2.4", 8017 "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", ··· 8024 "node": "*" 8025 } 8026 }, 8027 - "node_modules/dlv": { 8028 - "version": "1.1.3", 8029 - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 8030 - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 8031 - "dev": true 8032 - }, 8033 "node_modules/doctrine": { 8034 "version": "3.0.0", 8035 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", ··· 8259 "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", 8260 "license": "MIT" 8261 }, 8262 - "node_modules/eastasianwidth": { 8263 - "version": "0.2.0", 8264 - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 8265 - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 8266 - "dev": true 8267 - }, 8268 "node_modules/ecdsa-sig-formatter": { 8269 "version": "1.0.11", 8270 "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", ··· 8280 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 8281 "license": "MIT" 8282 }, 8283 - "node_modules/electron-to-chromium": { 8284 - "version": "1.4.783", 8285 - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz", 8286 - "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==", 8287 - "dev": true 8288 - }, 8289 "node_modules/emoji-regex": { 8290 "version": "9.2.2", 8291 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", ··· 8302 } 8303 }, 8304 "node_modules/enhanced-resolve": { 8305 - "version": "5.16.1", 8306 - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", 8307 - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", 8308 "dev": true, 8309 "dependencies": { 8310 "graceful-fs": "^4.2.4", 8311 "tapable": "^2.2.0" ··· 8719 } 8720 }, 8721 "node_modules/eslint-config-next": { 8722 - "version": "15.4.4", 8723 - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.4.tgz", 8724 - "integrity": "sha512-sK/lWLUVF5om18O5w76Jt3F8uzu/LP5mVa6TprCMWkjWHUmByq80iHGHcdH7k1dLiJlj+DRIWf98d5piwRsSuA==", 8725 "dev": true, 8726 "license": "MIT", 8727 "dependencies": { 8728 - "@next/eslint-plugin-next": "15.4.4", 8729 "@rushstack/eslint-patch": "^1.10.3", 8730 "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", 8731 "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", ··· 9598 "url": "https://github.com/sponsors/ljharb" 9599 } 9600 }, 9601 - "node_modules/foreground-child": { 9602 - "version": "3.1.1", 9603 - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", 9604 - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", 9605 - "dev": true, 9606 - "dependencies": { 9607 - "cross-spawn": "^7.0.0", 9608 - "signal-exit": "^4.0.1" 9609 - }, 9610 - "engines": { 9611 - "node": ">=14" 9612 - }, 9613 - "funding": { 9614 - "url": "https://github.com/sponsors/isaacs" 9615 - } 9616 - }, 9617 "node_modules/form-data": { 9618 "version": "4.0.0", 9619 "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", ··· 9653 "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", 9654 "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", 9655 "license": "MIT" 9656 - }, 9657 - "node_modules/fraction.js": { 9658 - "version": "4.3.7", 9659 - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", 9660 - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", 9661 - "dev": true, 9662 - "engines": { 9663 - "node": "*" 9664 - }, 9665 - "funding": { 9666 - "type": "patreon", 9667 - "url": "https://github.com/sponsors/rawify" 9668 - } 9669 }, 9670 "node_modules/fractional-indexing": { 9671 "version": "3.2.0", ··· 11283 } 11284 }, 11285 "node_modules/jiti": { 11286 - "version": "1.21.0", 11287 - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", 11288 - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", 11289 "dev": true, 11290 "bin": { 11291 - "jiti": "bin/jiti.js" 11292 } 11293 }, 11294 "node_modules/jose": { ··· 11528 "url": "https://github.com/sponsors/dmonad" 11529 } 11530 }, 11531 - "node_modules/lilconfig": { 11532 - "version": "2.1.0", 11533 - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", 11534 - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", 11535 "dev": true, 11536 "engines": { 11537 - "node": ">=10" 11538 } 11539 }, 11540 - "node_modules/lines-and-columns": { 11541 - "version": "1.2.4", 11542 - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", 11543 - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", 11544 - "dev": true 11545 }, 11546 "node_modules/linkify-it": { 11547 "version": "5.0.0", ··· 12893 } 12894 }, 12895 "node_modules/minizlib": { 12896 - "version": "3.0.1", 12897 - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", 12898 - "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", 12899 "dev": true, 12900 "dependencies": { 12901 - "minipass": "^7.0.4", 12902 - "rimraf": "^5.0.5" 12903 }, 12904 "engines": { 12905 "node": ">= 18" 12906 } 12907 }, 12908 - "node_modules/minizlib/node_modules/brace-expansion": { 12909 - "version": "2.0.1", 12910 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 12911 - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 12912 - "dev": true, 12913 - "dependencies": { 12914 - "balanced-match": "^1.0.0" 12915 - } 12916 - }, 12917 - "node_modules/minizlib/node_modules/glob": { 12918 - "version": "10.4.5", 12919 - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", 12920 - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", 12921 - "dev": true, 12922 - "dependencies": { 12923 - "foreground-child": "^3.1.0", 12924 - "jackspeak": "^3.1.2", 12925 - "minimatch": "^9.0.4", 12926 - "minipass": "^7.1.2", 12927 - "package-json-from-dist": "^1.0.0", 12928 - "path-scurry": "^1.11.1" 12929 - }, 12930 - "bin": { 12931 - "glob": "dist/esm/bin.mjs" 12932 - }, 12933 - "funding": { 12934 - "url": "https://github.com/sponsors/isaacs" 12935 - } 12936 - }, 12937 - "node_modules/minizlib/node_modules/jackspeak": { 12938 - "version": "3.4.3", 12939 - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", 12940 - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", 12941 - "dev": true, 12942 - "dependencies": { 12943 - "@isaacs/cliui": "^8.0.2" 12944 - }, 12945 - "funding": { 12946 - "url": "https://github.com/sponsors/isaacs" 12947 - }, 12948 - "optionalDependencies": { 12949 - "@pkgjs/parseargs": "^0.11.0" 12950 - } 12951 - }, 12952 - "node_modules/minizlib/node_modules/minimatch": { 12953 - "version": "9.0.5", 12954 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 12955 - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 12956 - "dev": true, 12957 - "dependencies": { 12958 - "brace-expansion": "^2.0.1" 12959 - }, 12960 - "engines": { 12961 - "node": ">=16 || 14 >=14.17" 12962 - }, 12963 - "funding": { 12964 - "url": "https://github.com/sponsors/isaacs" 12965 - } 12966 - }, 12967 - "node_modules/minizlib/node_modules/rimraf": { 12968 - "version": "5.0.9", 12969 - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.9.tgz", 12970 - "integrity": "sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==", 12971 - "dev": true, 12972 - "dependencies": { 12973 - "glob": "^10.3.7" 12974 - }, 12975 - "bin": { 12976 - "rimraf": "dist/esm/bin.mjs" 12977 - }, 12978 - "engines": { 12979 - "node": "14 >=14.20 || 16 >=16.20 || >=18" 12980 - }, 12981 - "funding": { 12982 - "url": "https://github.com/sponsors/isaacs" 12983 - } 12984 - }, 12985 "node_modules/mkdirp": { 12986 "version": "3.0.1", 12987 "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", ··· 13033 "mustache": "bin/mustache" 13034 } 13035 }, 13036 - "node_modules/mz": { 13037 - "version": "2.7.0", 13038 - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", 13039 - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", 13040 - "dev": true, 13041 - "dependencies": { 13042 - "any-promise": "^1.0.0", 13043 - "object-assign": "^4.0.1", 13044 - "thenify-all": "^1.0.0" 13045 - } 13046 - }, 13047 "node_modules/nanoid": { 13048 - "version": "3.3.7", 13049 - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", 13050 - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", 13051 "funding": [ 13052 { 13053 "type": "github", 13054 "url": "https://github.com/sponsors/ai" 13055 } 13056 ], 13057 "bin": { 13058 "nanoid": "bin/nanoid.cjs" 13059 }, ··· 13077 } 13078 }, 13079 "node_modules/next": { 13080 - "version": "15.5.0", 13081 - "resolved": "https://registry.npmjs.org/next/-/next-15.5.0.tgz", 13082 - "integrity": "sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==", 13083 "license": "MIT", 13084 "dependencies": { 13085 - "@next/env": "15.5.0", 13086 "@swc/helpers": "0.5.15", 13087 "caniuse-lite": "^1.0.30001579", 13088 "postcss": "8.4.31", ··· 13095 "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" 13096 }, 13097 "optionalDependencies": { 13098 - "@next/swc-darwin-arm64": "15.5.0", 13099 - "@next/swc-darwin-x64": "15.5.0", 13100 - "@next/swc-linux-arm64-gnu": "15.5.0", 13101 - "@next/swc-linux-arm64-musl": "15.5.0", 13102 - "@next/swc-linux-x64-gnu": "15.5.0", 13103 - "@next/swc-linux-x64-musl": "15.5.0", 13104 - "@next/swc-win32-arm64-msvc": "15.5.0", 13105 - "@next/swc-win32-x64-msvc": "15.5.0", 13106 "sharp": "^0.34.3" 13107 }, 13108 "peerDependencies": { ··· 13228 "node-gyp-build-optional-packages-test": "build-test.js" 13229 } 13230 }, 13231 - "node_modules/node-releases": { 13232 - "version": "2.0.14", 13233 - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", 13234 - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", 13235 - "dev": true 13236 - }, 13237 "node_modules/normalize-path": { 13238 "version": "3.0.0", 13239 "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", ··· 13243 "node": ">=0.10.0" 13244 } 13245 }, 13246 - "node_modules/normalize-range": { 13247 - "version": "0.1.2", 13248 - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", 13249 - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", 13250 - "dev": true, 13251 - "engines": { 13252 - "node": ">=0.10.0" 13253 - } 13254 - }, 13255 "node_modules/npm-normalize-package-bin": { 13256 "version": "3.0.1", 13257 "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", ··· 13268 "dev": true, 13269 "engines": { 13270 "node": ">=0.10.0" 13271 - } 13272 - }, 13273 - "node_modules/object-hash": { 13274 - "version": "3.0.0", 13275 - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 13276 - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 13277 - "dev": true, 13278 - "engines": { 13279 - "node": ">= 6" 13280 } 13281 }, 13282 "node_modules/object-inspect": { ··· 13549 "node": ">=8" 13550 } 13551 }, 13552 - "node_modules/package-json-from-dist": { 13553 - "version": "1.0.0", 13554 - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", 13555 - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", 13556 - "dev": true 13557 - }, 13558 "node_modules/parent-module": { 13559 "version": "1.0.1", 13560 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", ··· 13650 "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 13651 "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 13652 }, 13653 - "node_modules/path-scurry": { 13654 - "version": "1.11.1", 13655 - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", 13656 - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", 13657 - "dev": true, 13658 - "dependencies": { 13659 - "lru-cache": "^10.2.0", 13660 - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" 13661 - }, 13662 - "engines": { 13663 - "node": ">=16 || 14 >=14.18" 13664 - }, 13665 - "funding": { 13666 - "url": "https://github.com/sponsors/isaacs" 13667 - } 13668 - }, 13669 "node_modules/path-to-regexp": { 13670 "version": "6.2.2", 13671 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", ··· 13762 } 13763 }, 13764 "node_modules/picocolors": { 13765 - "version": "1.0.1", 13766 - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", 13767 - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" 13768 }, 13769 "node_modules/picomatch": { 13770 "version": "2.3.1", ··· 13778 "url": "https://github.com/sponsors/jonschlinkert" 13779 } 13780 }, 13781 - "node_modules/pify": { 13782 - "version": "2.3.0", 13783 - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 13784 - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 13785 - "dev": true, 13786 - "engines": { 13787 - "node": ">=0.10.0" 13788 - } 13789 - }, 13790 "node_modules/pino": { 13791 "version": "8.21.0", 13792 "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", ··· 13825 "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 13826 "license": "MIT" 13827 }, 13828 - "node_modules/pirates": { 13829 - "version": "4.0.6", 13830 - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", 13831 - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", 13832 - "dev": true, 13833 - "engines": { 13834 - "node": ">= 6" 13835 - } 13836 - }, 13837 "node_modules/possible-typed-array-names": { 13838 "version": "1.1.0", 13839 "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", ··· 13845 } 13846 }, 13847 "node_modules/postcss": { 13848 - "version": "8.4.38", 13849 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", 13850 - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", 13851 "dev": true, 13852 "funding": [ 13853 { ··· 13863 "url": "https://github.com/sponsors/ai" 13864 } 13865 ], 13866 "dependencies": { 13867 - "nanoid": "^3.3.7", 13868 - "picocolors": "^1.0.0", 13869 - "source-map-js": "^1.2.0" 13870 }, 13871 "engines": { 13872 "node": "^10 || ^12 || >=14" 13873 } 13874 }, 13875 - "node_modules/postcss-import": { 13876 - "version": "15.1.0", 13877 - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", 13878 - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", 13879 - "dev": true, 13880 - "dependencies": { 13881 - "postcss-value-parser": "^4.0.0", 13882 - "read-cache": "^1.0.0", 13883 - "resolve": "^1.1.7" 13884 - }, 13885 - "engines": { 13886 - "node": ">=14.0.0" 13887 - }, 13888 - "peerDependencies": { 13889 - "postcss": "^8.0.0" 13890 - } 13891 - }, 13892 - "node_modules/postcss-js": { 13893 - "version": "4.0.1", 13894 - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", 13895 - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", 13896 - "dev": true, 13897 - "dependencies": { 13898 - "camelcase-css": "^2.0.1" 13899 - }, 13900 - "engines": { 13901 - "node": "^12 || ^14 || >= 16" 13902 - }, 13903 - "funding": { 13904 - "type": "opencollective", 13905 - "url": "https://opencollective.com/postcss/" 13906 - }, 13907 - "peerDependencies": { 13908 - "postcss": "^8.4.21" 13909 - } 13910 - }, 13911 - "node_modules/postcss-load-config": { 13912 - "version": "4.0.2", 13913 - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", 13914 - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", 13915 - "dev": true, 13916 - "funding": [ 13917 - { 13918 - "type": "opencollective", 13919 - "url": "https://opencollective.com/postcss/" 13920 - }, 13921 - { 13922 - "type": "github", 13923 - "url": "https://github.com/sponsors/ai" 13924 - } 13925 - ], 13926 - "dependencies": { 13927 - "lilconfig": "^3.0.0", 13928 - "yaml": "^2.3.4" 13929 - }, 13930 - "engines": { 13931 - "node": ">= 14" 13932 - }, 13933 - "peerDependencies": { 13934 - "postcss": ">=8.0.9", 13935 - "ts-node": ">=9.0.0" 13936 - }, 13937 - "peerDependenciesMeta": { 13938 - "postcss": { 13939 - "optional": true 13940 - }, 13941 - "ts-node": { 13942 - "optional": true 13943 - } 13944 - } 13945 - }, 13946 - "node_modules/postcss-load-config/node_modules/lilconfig": { 13947 - "version": "3.1.1", 13948 - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", 13949 - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", 13950 - "dev": true, 13951 - "engines": { 13952 - "node": ">=14" 13953 - }, 13954 - "funding": { 13955 - "url": "https://github.com/sponsors/antonk52" 13956 - } 13957 - }, 13958 - "node_modules/postcss-nested": { 13959 - "version": "6.0.1", 13960 - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", 13961 - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", 13962 - "dev": true, 13963 - "dependencies": { 13964 - "postcss-selector-parser": "^6.0.11" 13965 - }, 13966 - "engines": { 13967 - "node": ">=12.0" 13968 - }, 13969 - "funding": { 13970 - "type": "opencollective", 13971 - "url": "https://opencollective.com/postcss/" 13972 - }, 13973 - "peerDependencies": { 13974 - "postcss": "^8.2.14" 13975 - } 13976 - }, 13977 - "node_modules/postcss-selector-parser": { 13978 - "version": "6.1.0", 13979 - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", 13980 - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", 13981 - "dev": true, 13982 - "dependencies": { 13983 - "cssesc": "^3.0.0", 13984 - "util-deprecate": "^1.0.2" 13985 - }, 13986 - "engines": { 13987 - "node": ">=4" 13988 - } 13989 - }, 13990 - "node_modules/postcss-value-parser": { 13991 - "version": "4.2.0", 13992 - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 13993 - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 13994 - "dev": true 13995 - }, 13996 "node_modules/postgres": { 13997 "version": "3.4.4", 13998 "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", ··· 14377 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 14378 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 14379 }, 14380 - "node_modules/psl": { 14381 - "version": "1.15.0", 14382 - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", 14383 - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", 14384 - "license": "MIT", 14385 - "dependencies": { 14386 - "punycode": "^2.3.1" 14387 - }, 14388 - "funding": { 14389 - "url": "https://github.com/sponsors/lupomontero" 14390 - } 14391 - }, 14392 "node_modules/punycode": { 14393 "version": "2.3.1", 14394 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 14745 "peerDependencies": { 14746 "react": ">=16.13", 14747 "react-dom": ">=16.13" 14748 - } 14749 - }, 14750 - "node_modules/read-cache": { 14751 - "version": "1.0.0", 14752 - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 14753 - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 14754 - "dev": true, 14755 - "dependencies": { 14756 - "pify": "^2.3.0" 14757 } 14758 }, 14759 "node_modules/read-cmd-shim": { ··· 15797 } 15798 }, 15799 "node_modules/source-map-js": { 15800 - "version": "1.2.0", 15801 - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", 15802 - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", 15803 "engines": { 15804 "node": ">=0.10.0" 15805 } ··· 15896 "node": ">=8" 15897 } 15898 }, 15899 - "node_modules/string-width-cjs": { 15900 - "name": "string-width", 15901 - "version": "4.2.3", 15902 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 15903 - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 15904 - "dev": true, 15905 - "dependencies": { 15906 - "emoji-regex": "^8.0.0", 15907 - "is-fullwidth-code-point": "^3.0.0", 15908 - "strip-ansi": "^6.0.1" 15909 - }, 15910 - "engines": { 15911 - "node": ">=8" 15912 - } 15913 - }, 15914 - "node_modules/string-width-cjs/node_modules/emoji-regex": { 15915 - "version": "8.0.0", 15916 - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 15917 - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 15918 - "dev": true 15919 - }, 15920 "node_modules/string-width/node_modules/emoji-regex": { 15921 "version": "8.0.0", 15922 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", ··· 16058 "node": ">=8" 16059 } 16060 }, 16061 - "node_modules/strip-ansi-cjs": { 16062 - "name": "strip-ansi", 16063 - "version": "6.0.1", 16064 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 16065 - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 16066 - "dev": true, 16067 - "dependencies": { 16068 - "ansi-regex": "^5.0.1" 16069 - }, 16070 - "engines": { 16071 - "node": ">=8" 16072 - } 16073 - }, 16074 "node_modules/strip-bom": { 16075 "version": "3.0.0", 16076 "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", ··· 16123 } 16124 } 16125 }, 16126 - "node_modules/sucrase": { 16127 - "version": "3.35.0", 16128 - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", 16129 - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", 16130 - "dev": true, 16131 - "dependencies": { 16132 - "@jridgewell/gen-mapping": "^0.3.2", 16133 - "commander": "^4.0.0", 16134 - "glob": "^10.3.10", 16135 - "lines-and-columns": "^1.1.6", 16136 - "mz": "^2.7.0", 16137 - "pirates": "^4.0.1", 16138 - "ts-interface-checker": "^0.1.9" 16139 - }, 16140 - "bin": { 16141 - "sucrase": "bin/sucrase", 16142 - "sucrase-node": "bin/sucrase-node" 16143 - }, 16144 - "engines": { 16145 - "node": ">=16 || 14 >=14.17" 16146 - } 16147 - }, 16148 - "node_modules/sucrase/node_modules/brace-expansion": { 16149 - "version": "2.0.1", 16150 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 16151 - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 16152 - "dev": true, 16153 - "dependencies": { 16154 - "balanced-match": "^1.0.0" 16155 - } 16156 - }, 16157 - "node_modules/sucrase/node_modules/commander": { 16158 - "version": "4.1.1", 16159 - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", 16160 - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", 16161 - "dev": true, 16162 - "engines": { 16163 - "node": ">= 6" 16164 - } 16165 - }, 16166 - "node_modules/sucrase/node_modules/glob": { 16167 - "version": "10.4.1", 16168 - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", 16169 - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", 16170 - "dev": true, 16171 - "dependencies": { 16172 - "foreground-child": "^3.1.0", 16173 - "jackspeak": "^3.1.2", 16174 - "minimatch": "^9.0.4", 16175 - "minipass": "^7.1.2", 16176 - "path-scurry": "^1.11.1" 16177 - }, 16178 - "bin": { 16179 - "glob": "dist/esm/bin.mjs" 16180 - }, 16181 - "engines": { 16182 - "node": ">=16 || 14 >=14.18" 16183 - }, 16184 - "funding": { 16185 - "url": "https://github.com/sponsors/isaacs" 16186 - } 16187 - }, 16188 - "node_modules/sucrase/node_modules/jackspeak": { 16189 - "version": "3.1.2", 16190 - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", 16191 - "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", 16192 - "dev": true, 16193 - "dependencies": { 16194 - "@isaacs/cliui": "^8.0.2" 16195 - }, 16196 - "engines": { 16197 - "node": ">=14" 16198 - }, 16199 - "funding": { 16200 - "url": "https://github.com/sponsors/isaacs" 16201 - }, 16202 - "optionalDependencies": { 16203 - "@pkgjs/parseargs": "^0.11.0" 16204 - } 16205 - }, 16206 - "node_modules/sucrase/node_modules/minimatch": { 16207 - "version": "9.0.4", 16208 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", 16209 - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", 16210 - "dev": true, 16211 - "dependencies": { 16212 - "brace-expansion": "^2.0.1" 16213 - }, 16214 - "engines": { 16215 - "node": ">=16 || 14 >=14.17" 16216 - }, 16217 - "funding": { 16218 - "url": "https://github.com/sponsors/isaacs" 16219 - } 16220 - }, 16221 "node_modules/supabase": { 16222 "version": "1.187.3", 16223 "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.187.3.tgz", ··· 16273 } 16274 }, 16275 "node_modules/tailwindcss": { 16276 - "version": "3.4.3", 16277 - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", 16278 - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", 16279 "dev": true, 16280 - "dependencies": { 16281 - "@alloc/quick-lru": "^5.2.0", 16282 - "arg": "^5.0.2", 16283 - "chokidar": "^3.5.3", 16284 - "didyoumean": "^1.2.2", 16285 - "dlv": "^1.1.3", 16286 - "fast-glob": "^3.3.0", 16287 - "glob-parent": "^6.0.2", 16288 - "is-glob": "^4.0.3", 16289 - "jiti": "^1.21.0", 16290 - "lilconfig": "^2.1.0", 16291 - "micromatch": "^4.0.5", 16292 - "normalize-path": "^3.0.0", 16293 - "object-hash": "^3.0.0", 16294 - "picocolors": "^1.0.0", 16295 - "postcss": "^8.4.23", 16296 - "postcss-import": "^15.1.0", 16297 - "postcss-js": "^4.0.1", 16298 - "postcss-load-config": "^4.0.1", 16299 - "postcss-nested": "^6.0.1", 16300 - "postcss-selector-parser": "^6.0.11", 16301 - "resolve": "^1.22.2", 16302 - "sucrase": "^3.32.0" 16303 - }, 16304 - "bin": { 16305 - "tailwind": "lib/cli.js", 16306 - "tailwindcss": "lib/cli.js" 16307 - }, 16308 - "engines": { 16309 - "node": ">=14.0.0" 16310 - } 16311 }, 16312 "node_modules/tapable": { 16313 "version": "2.2.1", ··· 16356 "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 16357 "dev": true 16358 }, 16359 - "node_modules/thenify": { 16360 - "version": "3.3.1", 16361 - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", 16362 - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", 16363 - "dev": true, 16364 - "dependencies": { 16365 - "any-promise": "^1.0.0" 16366 - } 16367 - }, 16368 - "node_modules/thenify-all": { 16369 - "version": "1.6.0", 16370 - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", 16371 - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", 16372 - "dev": true, 16373 - "dependencies": { 16374 - "thenify": ">= 3.1.0 < 4" 16375 - }, 16376 - "engines": { 16377 - "node": ">=0.8" 16378 - } 16379 - }, 16380 "node_modules/thread-stream": { 16381 "version": "2.7.0", 16382 "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", ··· 16401 "next-tick": "1" 16402 } 16403 }, 16404 "node_modules/tlds": { 16405 "version": "1.256.0", 16406 "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.256.0.tgz", ··· 16492 "typescript": ">=4.8.4" 16493 } 16494 }, 16495 - "node_modules/ts-interface-checker": { 16496 - "version": "0.1.13", 16497 - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", 16498 - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", 16499 - "dev": true 16500 - }, 16501 "node_modules/ts-morph": { 16502 - "version": "16.0.0", 16503 - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-16.0.0.tgz", 16504 - "integrity": "sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==", 16505 "dev": true, 16506 "license": "MIT", 16507 "dependencies": { 16508 - "@ts-morph/common": "~0.17.0", 16509 - "code-block-writer": "^11.0.3" 16510 } 16511 }, 16512 "node_modules/tsconfig-paths": { ··· 16766 } 16767 }, 16768 "node_modules/undici": { 16769 - "version": "6.21.2", 16770 - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", 16771 - "integrity": "sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==", 16772 "license": "MIT", 16773 "engines": { 16774 "node": ">=18.17" ··· 16895 "node": ">= 0.8" 16896 } 16897 }, 16898 - "node_modules/update-browserslist-db": { 16899 - "version": "1.0.16", 16900 - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", 16901 - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", 16902 - "dev": true, 16903 - "funding": [ 16904 - { 16905 - "type": "opencollective", 16906 - "url": "https://opencollective.com/browserslist" 16907 - }, 16908 - { 16909 - "type": "tidelift", 16910 - "url": "https://tidelift.com/funding/github/npm/browserslist" 16911 - }, 16912 - { 16913 - "type": "github", 16914 - "url": "https://github.com/sponsors/ai" 16915 - } 16916 - ], 16917 - "dependencies": { 16918 - "escalade": "^3.1.2", 16919 - "picocolors": "^1.0.1" 16920 - }, 16921 - "bin": { 16922 - "update-browserslist-db": "cli.js" 16923 - }, 16924 - "peerDependencies": { 16925 - "browserslist": ">= 4.21.0" 16926 - } 16927 - }, 16928 "node_modules/use-callback-ref": { 16929 "version": "1.3.3", 16930 "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", ··· 16976 "peerDependencies": { 16977 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 16978 } 16979 - }, 16980 - "node_modules/util-deprecate": { 16981 - "version": "1.0.2", 16982 - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 16983 - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 16984 - "dev": true 16985 }, 16986 "node_modules/utils-merge": { 16987 "version": "1.0.1", ··· 17410 "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 17411 } 17412 }, 17413 - "node_modules/wrap-ansi-cjs": { 17414 - "name": "wrap-ansi", 17415 - "version": "7.0.0", 17416 - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 17417 - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 17418 - "dev": true, 17419 - "dependencies": { 17420 - "ansi-styles": "^4.0.0", 17421 - "string-width": "^4.1.0", 17422 - "strip-ansi": "^6.0.0" 17423 - }, 17424 - "engines": { 17425 - "node": ">=10" 17426 - }, 17427 - "funding": { 17428 - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 17429 - } 17430 - }, 17431 "node_modules/wrappy": { 17432 "version": "1.0.2", 17433 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", ··· 17562 "dev": true, 17563 "engines": { 17564 "node": ">=18" 17565 - } 17566 - }, 17567 - "node_modules/yaml": { 17568 - "version": "2.4.2", 17569 - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", 17570 - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", 17571 - "dev": true, 17572 - "bin": { 17573 - "yaml": "bin.mjs" 17574 - }, 17575 - "engines": { 17576 - "node": ">= 14" 17577 } 17578 }, 17579 "node_modules/yargs": {
··· 9 "version": "1.0.0", 10 "license": "ISC", 11 "dependencies": { 12 + "@atproto/api": "^0.16.9", 13 "@atproto/common": "^0.4.8", 14 "@atproto/identity": "^0.4.6", 15 + "@atproto/lexicon": "^0.5.1", 16 + "@atproto/oauth-client-node": "^0.3.8", 17 + "@atproto/sync": "^0.1.34", 18 "@atproto/syntax": "^0.3.3", 19 + "@atproto/xrpc": "^0.7.5", 20 + "@atproto/xrpc-server": "^0.9.5", 21 "@hono/node-server": "^1.14.3", 22 "@mdx-js/loader": "^3.1.0", 23 "@mdx-js/react": "^3.1.0", ··· 49 "katex": "^0.16.22", 50 "linkifyjs": "^4.2.0", 51 "multiformats": "^13.3.2", 52 + "next": "^15.5.3", 53 "pg": "^8.16.3", 54 "prosemirror-commands": "^1.5.2", 55 "prosemirror-inputrules": "^1.4.0", ··· 84 "zustand": "^5.0.4" 85 }, 86 "devDependencies": { 87 + "@atproto/lex-cli": "^0.9.5", 88 "@cloudflare/workers-types": "^4.20240512.0", 89 + "@tailwindcss/postcss": "^4.1.13", 90 "@types/katex": "^0.16.7", 91 "@types/node": "^22.15.17", 92 "@types/react": "19.1.3", 93 "@types/react-dom": "19.1.3", 94 "@types/uuid": "^10.0.0", 95 "drizzle-kit": "^0.21.2", 96 "esbuild": "^0.25.4", 97 "eslint": "8.57.0", 98 + "eslint-config-next": "^15.5.3", 99 "postcss": "^8.4.38", 100 "prettier": "3.2.5", 101 "supabase": "^1.187.3", 102 + "tailwindcss": "^4.1.13", 103 "tsx": "^4.19.3", 104 "typescript": "^5.8.3", 105 "wrangler": "^3.56.0" ··· 110 "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", 111 "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", 112 "dev": true, 113 + "license": "MIT", 114 "engines": { 115 "node": ">=10" 116 }, ··· 119 } 120 }, 121 "node_modules/@atproto-labs/did-resolver": { 122 + "version": "0.2.1", 123 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.1.tgz", 124 + "integrity": "sha512-zSoHyqwwRYUtMNLW+RrWsImt1U5S47nJv5FfmAXTmon6wVKjxKD/PFrD1pg/4G6THqJmQHTs1Hj+54XVupYnvQ==", 125 "license": "MIT", 126 "dependencies": { 127 + "@atproto-labs/fetch": "0.2.3", 128 + "@atproto-labs/pipe": "0.1.1", 129 + "@atproto-labs/simple-store": "0.3.0", 130 + "@atproto-labs/simple-store-memory": "0.1.4", 131 + "@atproto/did": "0.2.0", 132 "zod": "^3.23.8" 133 } 134 }, 135 "node_modules/@atproto-labs/fetch": { 136 + "version": "0.2.3", 137 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 138 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 139 "license": "MIT", 140 "dependencies": { 141 + "@atproto-labs/pipe": "0.1.1" 142 } 143 }, 144 "node_modules/@atproto-labs/fetch-node": { 145 + "version": "0.1.10", 146 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch-node/-/fetch-node-0.1.10.tgz", 147 + "integrity": "sha512-o7hGaonA71A6p7O107VhM6UBUN/g9tTyYohMp1q0Kf6xQ4npnuZYRSHSf2g6reSfGQJ1GoFNjBObETTT1ge/jQ==", 148 "license": "MIT", 149 "dependencies": { 150 + "@atproto-labs/fetch": "0.2.3", 151 + "@atproto-labs/pipe": "0.1.1", 152 "ipaddr.js": "^2.1.0", 153 "undici": "^6.14.1" 154 }, 155 "engines": { ··· 157 } 158 }, 159 "node_modules/@atproto-labs/handle-resolver": { 160 + "version": "0.3.1", 161 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.3.1.tgz", 162 + "integrity": "sha512-mLZdMNvwomgnn9sffKO1/xr02ctgeiT0FUVw7JekbchTckub2RM7qMu8Rw1mC4bpCpW+i7DXDiOxpoajkppwYQ==", 163 "license": "MIT", 164 "dependencies": { 165 + "@atproto-labs/simple-store": "0.3.0", 166 + "@atproto-labs/simple-store-memory": "0.1.4", 167 + "@atproto/did": "0.2.0", 168 "zod": "^3.23.8" 169 } 170 }, 171 "node_modules/@atproto-labs/handle-resolver-node": { 172 + "version": "0.1.19", 173 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver-node/-/handle-resolver-node-0.1.19.tgz", 174 + "integrity": "sha512-nNVCfiKudvMYfDcWCa9koOMOpCYaC0wG4Uys5dZev99s/Nka7tRlIZIV+u+GWivnG9lqCupKATkoyCd6Per8Gw==", 175 "license": "MIT", 176 "dependencies": { 177 + "@atproto-labs/fetch-node": "0.1.10", 178 + "@atproto-labs/handle-resolver": "0.3.1", 179 + "@atproto/did": "0.2.0" 180 }, 181 "engines": { 182 "node": ">=18.7.0" 183 } 184 }, 185 "node_modules/@atproto-labs/identity-resolver": { 186 + "version": "0.3.1", 187 + "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.3.1.tgz", 188 + "integrity": "sha512-jCgotRRqPykPwh4gh0FBLOqeofv1G8OH/DZ5s88HWm7biUZeksZwDrEvL5TnqEFUpXT3O9Hcyp/XEpfCAplRoQ==", 189 "license": "MIT", 190 "dependencies": { 191 + "@atproto-labs/did-resolver": "0.2.1", 192 + "@atproto-labs/handle-resolver": "0.3.1" 193 } 194 }, 195 "node_modules/@atproto-labs/pipe": { 196 + "version": "0.1.1", 197 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 198 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 199 "license": "MIT" 200 }, 201 "node_modules/@atproto-labs/simple-store": { 202 + "version": "0.3.0", 203 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 204 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 205 "license": "MIT" 206 }, 207 "node_modules/@atproto-labs/simple-store-memory": { 208 + "version": "0.1.4", 209 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 210 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 211 "license": "MIT", 212 "dependencies": { 213 + "@atproto-labs/simple-store": "0.3.0", 214 "lru-cache": "^10.2.0" 215 } 216 }, 217 "node_modules/@atproto/api": { 218 + "version": "0.16.9", 219 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.16.9.tgz", 220 + "integrity": "sha512-hXbnBIDEIwXxxyduxxZsf0aP8Z+JKyfG7L47FZqAYOI6uNm8oBTLLrHQ2RmJZZeyMIMM17gvxNtPDoULKQfupw==", 221 "license": "MIT", 222 "dependencies": { 223 + "@atproto/common-web": "^0.4.3", 224 + "@atproto/lexicon": "^0.5.1", 225 + "@atproto/syntax": "^0.4.1", 226 + "@atproto/xrpc": "^0.7.5", 227 "await-lock": "^2.2.2", 228 "multiformats": "^9.9.0", 229 "tlds": "^1.234.0", 230 "zod": "^3.23.8" 231 } 232 }, 233 + "node_modules/@atproto/api/node_modules/@atproto/syntax": { 234 + "version": "0.4.1", 235 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 236 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 237 + "license": "MIT" 238 + }, 239 "node_modules/@atproto/api/node_modules/multiformats": { 240 "version": "9.9.0", 241 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 243 "license": "(Apache-2.0 AND MIT)" 244 }, 245 "node_modules/@atproto/common": { 246 + "version": "0.4.12", 247 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.4.12.tgz", 248 + "integrity": "sha512-NC+TULLQiqs6MvNymhQS5WDms3SlbIKGLf4n33tpftRJcalh507rI+snbcUb7TLIkKw7VO17qMqxEXtIdd5auQ==", 249 "license": "MIT", 250 "dependencies": { 251 + "@atproto/common-web": "^0.4.3", 252 "@ipld/dag-cbor": "^7.0.3", 253 "cbor-x": "^1.5.1", 254 "iso-datestring-validator": "^2.2.2", ··· 260 } 261 }, 262 "node_modules/@atproto/common-web": { 263 + "version": "0.4.3", 264 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 265 + "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 266 "license": "MIT", 267 "dependencies": { 268 "graphemer": "^1.4.0", ··· 298 } 299 }, 300 "node_modules/@atproto/did": { 301 + "version": "0.2.0", 302 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.0.tgz", 303 + "integrity": "sha512-BskT39KYbwY1DUsWekkHh47xS+wvJpFq5F9acsicNfYniinyAMnNTzGKQEhnjQuG7K0qQItg/SnmC+y0tJXV7Q==", 304 "license": "MIT", 305 "dependencies": { 306 "zod": "^3.23.8" 307 } 308 }, 309 "node_modules/@atproto/identity": { 310 + "version": "0.4.9", 311 + "resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.9.tgz", 312 + "integrity": "sha512-pRYCaeaEJMZ4vQlRQYYTrF3cMiRp21n/k/pUT1o7dgKby56zuLErDmFXkbKfKWPf7SgWRgamSaNmsGLqAOD7lQ==", 313 "license": "MIT", 314 "dependencies": { 315 + "@atproto/common-web": "^0.4.3", 316 "@atproto/crypto": "^0.4.4" 317 }, 318 "engines": { ··· 320 } 321 }, 322 "node_modules/@atproto/jwk": { 323 + "version": "0.5.0", 324 + "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.5.0.tgz", 325 + "integrity": "sha512-Qi2NtEqhkG+uz3CKia4+H05WMV/z//dz3ESo5+cyBKrOnxVTJ5ZubMyltWjoYvy6v/jLhorXdDWcjn07yky7MQ==", 326 "license": "MIT", 327 "dependencies": { 328 "multiformats": "^9.9.0", ··· 330 } 331 }, 332 "node_modules/@atproto/jwk-jose": { 333 + "version": "0.1.10", 334 + "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.10.tgz", 335 + "integrity": "sha512-Eiu/u4tZHz3IIhHZt0zneYEffSAO3Oqk/ToKwlu1TqKte6sjtPs/4uquSiAAGFYozqgo92JC/AQclWzzkHI5QQ==", 336 "license": "MIT", 337 "dependencies": { 338 + "@atproto/jwk": "0.5.0", 339 "jose": "^5.2.0" 340 } 341 }, 342 "node_modules/@atproto/jwk-webcrypto": { 343 + "version": "0.1.10", 344 + "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.1.10.tgz", 345 + "integrity": "sha512-JZsavs6JiSmw5rgcjkGDwzr1aCJGdybZOjVfYH+m9sXRU1BrUCA30uwNfZY7eFyWXyRAnCFiYiGVZgypXyKotw==", 346 "license": "MIT", 347 "dependencies": { 348 + "@atproto/jwk": "0.5.0", 349 + "@atproto/jwk-jose": "0.1.10", 350 "zod": "^3.23.8" 351 } 352 }, ··· 357 "license": "(Apache-2.0 AND MIT)" 358 }, 359 "node_modules/@atproto/lex-cli": { 360 + "version": "0.9.5", 361 + "resolved": "https://registry.npmjs.org/@atproto/lex-cli/-/lex-cli-0.9.5.tgz", 362 + "integrity": "sha512-zun4jhD1dbjD7IHtLIjh/1UsMx+6E8+OyOT2GXYAKIj9N6wmLKM/v2OeQBKxcyqUmtRi57lxWnGikWjjU7pplQ==", 363 "dev": true, 364 "license": "MIT", 365 "dependencies": { 366 + "@atproto/lexicon": "^0.5.1", 367 + "@atproto/syntax": "^0.4.1", 368 "chalk": "^4.1.2", 369 "commander": "^9.4.0", 370 "prettier": "^3.2.5", 371 + "ts-morph": "^24.0.0", 372 "yesno": "^0.4.0", 373 "zod": "^3.23.8" 374 }, ··· 378 "engines": { 379 "node": ">=18.7.0" 380 } 381 + }, 382 + "node_modules/@atproto/lex-cli/node_modules/@atproto/syntax": { 383 + "version": "0.4.1", 384 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 385 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 386 + "dev": true, 387 + "license": "MIT" 388 }, 389 "node_modules/@atproto/lexicon": { 390 + "version": "0.5.1", 391 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 392 + "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 393 "license": "MIT", 394 "dependencies": { 395 + "@atproto/common-web": "^0.4.3", 396 + "@atproto/syntax": "^0.4.1", 397 "iso-datestring-validator": "^2.2.2", 398 "multiformats": "^9.9.0", 399 "zod": "^3.23.8" 400 } 401 }, 402 "node_modules/@atproto/lexicon/node_modules/@atproto/syntax": { 403 + "version": "0.4.1", 404 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 405 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 406 "license": "MIT" 407 }, 408 "node_modules/@atproto/lexicon/node_modules/multiformats": { ··· 412 "license": "(Apache-2.0 AND MIT)" 413 }, 414 "node_modules/@atproto/oauth-client": { 415 + "version": "0.5.6", 416 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.5.6.tgz", 417 + "integrity": "sha512-O1S9lPptJxWPcNd2kODaLgWntz+A7PzskU2hP4IFa7hVLs4aEnEt9dKq5wJE97tDli8mgyh/ndPQhxUaCVQ5iQ==", 418 "license": "MIT", 419 "dependencies": { 420 + "@atproto-labs/did-resolver": "0.2.1", 421 + "@atproto-labs/fetch": "0.2.3", 422 + "@atproto-labs/handle-resolver": "0.3.1", 423 + "@atproto-labs/identity-resolver": "0.3.1", 424 + "@atproto-labs/simple-store": "0.3.0", 425 + "@atproto-labs/simple-store-memory": "0.1.4", 426 + "@atproto/did": "0.2.0", 427 + "@atproto/jwk": "0.5.0", 428 + "@atproto/oauth-types": "0.4.1", 429 + "@atproto/xrpc": "0.7.5", 430 "multiformats": "^9.9.0", 431 "zod": "^3.23.8" 432 } 433 }, 434 "node_modules/@atproto/oauth-client-node": { 435 + "version": "0.3.8", 436 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client-node/-/oauth-client-node-0.3.8.tgz", 437 + "integrity": "sha512-HIBiYQERj04Xa0l8cJkqcZC0BbHH5uqDEvhqHWnJ5umSq/ms0+HZi3JKJXGv1XfYOvxUxx6NKgXJ8VhhYoQa5A==", 438 "license": "MIT", 439 "dependencies": { 440 + "@atproto-labs/did-resolver": "0.2.1", 441 + "@atproto-labs/handle-resolver-node": "0.1.19", 442 + "@atproto-labs/simple-store": "0.3.0", 443 + "@atproto/did": "0.2.0", 444 + "@atproto/jwk": "0.5.0", 445 + "@atproto/jwk-jose": "0.1.10", 446 + "@atproto/jwk-webcrypto": "0.1.10", 447 + "@atproto/oauth-client": "0.5.6", 448 + "@atproto/oauth-types": "0.4.1" 449 }, 450 "engines": { 451 "node": ">=18.7.0" 452 } 453 }, 454 "node_modules/@atproto/oauth-client/node_modules/multiformats": { 455 "version": "9.9.0", 456 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 458 "license": "(Apache-2.0 AND MIT)" 459 }, 460 "node_modules/@atproto/oauth-types": { 461 + "version": "0.4.1", 462 + "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.4.1.tgz", 463 + "integrity": "sha512-c5ixf2ZOzcltOu1fDBnO/tok6Wj7JDDK66+Z0q/+bAr8LXgOnxP7zQfJ+DD4gTkB+saTqsqWtVv8qvx/IEtm1g==", 464 "license": "MIT", 465 "dependencies": { 466 + "@atproto/jwk": "0.5.0", 467 "zod": "^3.23.8" 468 } 469 }, 470 "node_modules/@atproto/repo": { 471 + "version": "0.8.9", 472 + "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.9.tgz", 473 + "integrity": "sha512-FTePZS2KEv8++pkOB8GGvm46V6uJqd/95bPA1cXTDXyw0cqeVEOItfxkCH1ky/fY71QYr0NkmqMUwuwZ/gwEtQ==", 474 "license": "MIT", 475 "dependencies": { 476 + "@atproto/common": "^0.4.12", 477 + "@atproto/common-web": "^0.4.3", 478 "@atproto/crypto": "^0.4.4", 479 + "@atproto/lexicon": "^0.5.1", 480 "@ipld/dag-cbor": "^7.0.0", 481 "multiformats": "^9.9.0", 482 "uint8arrays": "3.0.0", ··· 494 "license": "(Apache-2.0 AND MIT)" 495 }, 496 "node_modules/@atproto/sync": { 497 + "version": "0.1.34", 498 + "resolved": "https://registry.npmjs.org/@atproto/sync/-/sync-0.1.34.tgz", 499 + "integrity": "sha512-NBdAoKGu8B5uU86791ySpsQMxpN87ge9tR7f++0wxyhnn/qE1oVd6f4hvyeD7bJJUsteJfIl0PxHist7cEC5sQ==", 500 "license": "MIT", 501 "dependencies": { 502 + "@atproto/common": "^0.4.12", 503 + "@atproto/identity": "^0.4.9", 504 + "@atproto/lexicon": "^0.5.1", 505 + "@atproto/repo": "^0.8.9", 506 + "@atproto/syntax": "^0.4.1", 507 + "@atproto/xrpc-server": "^0.9.5", 508 "multiformats": "^9.9.0", 509 "p-queue": "^6.6.2", 510 "ws": "^8.12.0" ··· 514 } 515 }, 516 "node_modules/@atproto/sync/node_modules/@atproto/syntax": { 517 + "version": "0.4.1", 518 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 519 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 520 "license": "MIT" 521 }, 522 "node_modules/@atproto/sync/node_modules/multiformats": { 523 "version": "9.9.0", 524 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 532 "license": "MIT" 533 }, 534 "node_modules/@atproto/xrpc": { 535 + "version": "0.7.5", 536 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 537 + "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 538 "license": "MIT", 539 "dependencies": { 540 + "@atproto/lexicon": "^0.5.1", 541 "zod": "^3.23.8" 542 } 543 }, 544 "node_modules/@atproto/xrpc-server": { 545 + "version": "0.9.5", 546 + "resolved": "https://registry.npmjs.org/@atproto/xrpc-server/-/xrpc-server-0.9.5.tgz", 547 + "integrity": "sha512-V0srjUgy6mQ5yf9+MSNBLs457m4qclEaWZsnqIE7RfYywvntexTAbMoo7J7ONfTNwdmA9Gw4oLak2z2cDAET4w==", 548 "license": "MIT", 549 "dependencies": { 550 + "@atproto/common": "^0.4.12", 551 "@atproto/crypto": "^0.4.4", 552 + "@atproto/lexicon": "^0.5.1", 553 + "@atproto/xrpc": "^0.7.5", 554 "cbor-x": "^1.5.1", 555 "express": "^4.17.2", 556 "http-errors": "^2.0.0", ··· 562 }, 563 "engines": { 564 "node": ">=18.7.0" 565 } 566 }, 567 "node_modules/@babel/helper-string-parser": { ··· 1145 "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 1146 "license": "(Apache-2.0 AND MIT)" 1147 }, 1148 "node_modules/@isaacs/fs-minipass": { 1149 "version": "4.0.1", 1150 "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", ··· 1164 "license": "MIT" 1165 }, 1166 "node_modules/@jridgewell/gen-mapping": { 1167 + "version": "0.3.13", 1168 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 1169 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 1170 "dev": true, 1171 + "license": "MIT", 1172 "dependencies": { 1173 + "@jridgewell/sourcemap-codec": "^1.5.0", 1174 "@jridgewell/trace-mapping": "^0.3.24" 1175 + } 1176 + }, 1177 + "node_modules/@jridgewell/remapping": { 1178 + "version": "2.3.5", 1179 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 1180 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 1181 + "dev": true, 1182 + "license": "MIT", 1183 + "dependencies": { 1184 + "@jridgewell/gen-mapping": "^0.3.5", 1185 + "@jridgewell/trace-mapping": "^0.3.24" 1186 } 1187 }, 1188 "node_modules/@jridgewell/resolve-uri": { ··· 1194 "node": ">=6.0.0" 1195 } 1196 }, 1197 + "node_modules/@jridgewell/sourcemap-codec": { 1198 + "version": "1.5.5", 1199 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 1200 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 1201 "dev": true, 1202 + "license": "MIT" 1203 }, 1204 "node_modules/@jridgewell/trace-mapping": { 1205 + "version": "0.3.31", 1206 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", 1207 + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", 1208 "dev": true, 1209 + "license": "MIT", 1210 "dependencies": { 1211 "@jridgewell/resolve-uri": "^3.1.0", 1212 "@jridgewell/sourcemap-codec": "^1.4.14" ··· 1328 } 1329 }, 1330 "node_modules/@next/env": { 1331 + "version": "15.5.3", 1332 + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.3.tgz", 1333 + "integrity": "sha512-RSEDTRqyihYXygx/OJXwvVupfr9m04+0vH8vyy0HfZ7keRto6VX9BbEk0J2PUk0VGy6YhklJUSrgForov5F9pw==", 1334 "license": "MIT" 1335 }, 1336 "node_modules/@next/eslint-plugin-next": { 1337 + "version": "15.5.3", 1338 + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.3.tgz", 1339 + "integrity": "sha512-SdhaKdko6dpsSr0DldkESItVrnPYB1NS2NpShCSX5lc7SSQmLZt5Mug6t2xbiuVWEVDLZSuIAoQyYVBYp0dR5g==", 1340 "dev": true, 1341 "license": "MIT", 1342 "dependencies": { ··· 1403 } 1404 }, 1405 "node_modules/@next/swc-darwin-arm64": { 1406 + "version": "15.5.3", 1407 + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.3.tgz", 1408 + "integrity": "sha512-nzbHQo69+au9wJkGKTU9lP7PXv0d1J5ljFpvb+LnEomLtSbJkbZyEs6sbF3plQmiOB2l9OBtN2tNSvCH1nQ9Jg==", 1409 "cpu": [ 1410 "arm64" 1411 ], ··· 1419 } 1420 }, 1421 "node_modules/@next/swc-darwin-x64": { 1422 + "version": "15.5.3", 1423 + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.3.tgz", 1424 + "integrity": "sha512-w83w4SkOOhekJOcA5HBvHyGzgV1W/XvOfpkrxIse4uPWhYTTRwtGEM4v/jiXwNSJvfRvah0H8/uTLBKRXlef8g==", 1425 "cpu": [ 1426 "x64" 1427 ], ··· 1435 } 1436 }, 1437 "node_modules/@next/swc-linux-arm64-gnu": { 1438 + "version": "15.5.3", 1439 + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.3.tgz", 1440 + "integrity": "sha512-+m7pfIs0/yvgVu26ieaKrifV8C8yiLe7jVp9SpcIzg7XmyyNE7toC1fy5IOQozmr6kWl/JONC51osih2RyoXRw==", 1441 "cpu": [ 1442 "arm64" 1443 ], ··· 1451 } 1452 }, 1453 "node_modules/@next/swc-linux-arm64-musl": { 1454 + "version": "15.5.3", 1455 + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.3.tgz", 1456 + "integrity": "sha512-u3PEIzuguSenoZviZJahNLgCexGFhso5mxWCrrIMdvpZn6lkME5vc/ADZG8UUk5K1uWRy4hqSFECrON6UKQBbQ==", 1457 "cpu": [ 1458 "arm64" 1459 ], ··· 1467 } 1468 }, 1469 "node_modules/@next/swc-linux-x64-gnu": { 1470 + "version": "15.5.3", 1471 + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.3.tgz", 1472 + "integrity": "sha512-lDtOOScYDZxI2BENN9m0pfVPJDSuUkAD1YXSvlJF0DKwZt0WlA7T7o3wrcEr4Q+iHYGzEaVuZcsIbCps4K27sA==", 1473 "cpu": [ 1474 "x64" 1475 ], ··· 1483 } 1484 }, 1485 "node_modules/@next/swc-linux-x64-musl": { 1486 + "version": "15.5.3", 1487 + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.3.tgz", 1488 + "integrity": "sha512-9vWVUnsx9PrY2NwdVRJ4dUURAQ8Su0sLRPqcCCxtX5zIQUBES12eRVHq6b70bbfaVaxIDGJN2afHui0eDm+cLg==", 1489 "cpu": [ 1490 "x64" 1491 ], ··· 1499 } 1500 }, 1501 "node_modules/@next/swc-win32-arm64-msvc": { 1502 + "version": "15.5.3", 1503 + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.3.tgz", 1504 + "integrity": "sha512-1CU20FZzY9LFQigRi6jM45oJMU3KziA5/sSG+dXeVaTm661snQP6xu3ykGxxwU5sLG3sh14teO/IOEPVsQMRfA==", 1505 "cpu": [ 1506 "arm64" 1507 ], ··· 1515 } 1516 }, 1517 "node_modules/@next/swc-win32-x64-msvc": { 1518 + "version": "15.5.3", 1519 + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.3.tgz", 1520 + "integrity": "sha512-JMoLAq3n3y5tKXPQwCK5c+6tmwkuFDa2XAxz8Wm4+IVthdBZdZGh+lmiLUHg9f9IDwIQpUjp+ysd6OkYTyZRZw==", 1521 "cpu": [ 1522 "x64" 1523 ], ··· 2917 }, 2918 "peerDependencies": { 2919 "@opentelemetry/api": "^1.1.0" 2920 } 2921 }, 2922 "node_modules/@polka/url": { ··· 5773 "tslib": "^2.8.0" 5774 } 5775 }, 5776 + "node_modules/@tailwindcss/node": { 5777 + "version": "4.1.13", 5778 + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", 5779 + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", 5780 + "dev": true, 5781 + "license": "MIT", 5782 + "dependencies": { 5783 + "@jridgewell/remapping": "^2.3.4", 5784 + "enhanced-resolve": "^5.18.3", 5785 + "jiti": "^2.5.1", 5786 + "lightningcss": "1.30.1", 5787 + "magic-string": "^0.30.18", 5788 + "source-map-js": "^1.2.1", 5789 + "tailwindcss": "4.1.13" 5790 + } 5791 + }, 5792 + "node_modules/@tailwindcss/node/node_modules/magic-string": { 5793 + "version": "0.30.19", 5794 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", 5795 + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", 5796 + "dev": true, 5797 + "license": "MIT", 5798 + "dependencies": { 5799 + "@jridgewell/sourcemap-codec": "^1.5.5" 5800 + } 5801 + }, 5802 + "node_modules/@tailwindcss/oxide": { 5803 + "version": "4.1.13", 5804 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", 5805 + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", 5806 + "dev": true, 5807 + "hasInstallScript": true, 5808 + "license": "MIT", 5809 + "dependencies": { 5810 + "detect-libc": "^2.0.4", 5811 + "tar": "^7.4.3" 5812 + }, 5813 + "engines": { 5814 + "node": ">= 10" 5815 + }, 5816 + "optionalDependencies": { 5817 + "@tailwindcss/oxide-android-arm64": "4.1.13", 5818 + "@tailwindcss/oxide-darwin-arm64": "4.1.13", 5819 + "@tailwindcss/oxide-darwin-x64": "4.1.13", 5820 + "@tailwindcss/oxide-freebsd-x64": "4.1.13", 5821 + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", 5822 + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", 5823 + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", 5824 + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", 5825 + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", 5826 + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", 5827 + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", 5828 + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" 5829 + } 5830 + }, 5831 + "node_modules/@tailwindcss/oxide-android-arm64": { 5832 + "version": "4.1.13", 5833 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", 5834 + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", 5835 + "cpu": [ 5836 + "arm64" 5837 + ], 5838 + "dev": true, 5839 + "license": "MIT", 5840 + "optional": true, 5841 + "os": [ 5842 + "android" 5843 + ], 5844 + "engines": { 5845 + "node": ">= 10" 5846 + } 5847 + }, 5848 + "node_modules/@tailwindcss/oxide-darwin-arm64": { 5849 + "version": "4.1.13", 5850 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", 5851 + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", 5852 + "cpu": [ 5853 + "arm64" 5854 + ], 5855 + "dev": true, 5856 + "license": "MIT", 5857 + "optional": true, 5858 + "os": [ 5859 + "darwin" 5860 + ], 5861 + "engines": { 5862 + "node": ">= 10" 5863 + } 5864 + }, 5865 + "node_modules/@tailwindcss/oxide-darwin-x64": { 5866 + "version": "4.1.13", 5867 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", 5868 + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", 5869 + "cpu": [ 5870 + "x64" 5871 + ], 5872 + "dev": true, 5873 + "license": "MIT", 5874 + "optional": true, 5875 + "os": [ 5876 + "darwin" 5877 + ], 5878 + "engines": { 5879 + "node": ">= 10" 5880 + } 5881 + }, 5882 + "node_modules/@tailwindcss/oxide-freebsd-x64": { 5883 + "version": "4.1.13", 5884 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", 5885 + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", 5886 + "cpu": [ 5887 + "x64" 5888 + ], 5889 + "dev": true, 5890 + "license": "MIT", 5891 + "optional": true, 5892 + "os": [ 5893 + "freebsd" 5894 + ], 5895 + "engines": { 5896 + "node": ">= 10" 5897 + } 5898 + }, 5899 + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { 5900 + "version": "4.1.13", 5901 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", 5902 + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", 5903 + "cpu": [ 5904 + "arm" 5905 + ], 5906 + "dev": true, 5907 + "license": "MIT", 5908 + "optional": true, 5909 + "os": [ 5910 + "linux" 5911 + ], 5912 + "engines": { 5913 + "node": ">= 10" 5914 + } 5915 + }, 5916 + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { 5917 + "version": "4.1.13", 5918 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", 5919 + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", 5920 + "cpu": [ 5921 + "arm64" 5922 + ], 5923 + "dev": true, 5924 + "license": "MIT", 5925 + "optional": true, 5926 + "os": [ 5927 + "linux" 5928 + ], 5929 + "engines": { 5930 + "node": ">= 10" 5931 + } 5932 + }, 5933 + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { 5934 + "version": "4.1.13", 5935 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", 5936 + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", 5937 + "cpu": [ 5938 + "arm64" 5939 + ], 5940 + "dev": true, 5941 + "license": "MIT", 5942 + "optional": true, 5943 + "os": [ 5944 + "linux" 5945 + ], 5946 + "engines": { 5947 + "node": ">= 10" 5948 + } 5949 + }, 5950 + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { 5951 + "version": "4.1.13", 5952 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", 5953 + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", 5954 + "cpu": [ 5955 + "x64" 5956 + ], 5957 + "dev": true, 5958 + "license": "MIT", 5959 + "optional": true, 5960 + "os": [ 5961 + "linux" 5962 + ], 5963 + "engines": { 5964 + "node": ">= 10" 5965 + } 5966 + }, 5967 + "node_modules/@tailwindcss/oxide-linux-x64-musl": { 5968 + "version": "4.1.13", 5969 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", 5970 + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", 5971 + "cpu": [ 5972 + "x64" 5973 + ], 5974 + "dev": true, 5975 + "license": "MIT", 5976 + "optional": true, 5977 + "os": [ 5978 + "linux" 5979 + ], 5980 + "engines": { 5981 + "node": ">= 10" 5982 + } 5983 + }, 5984 + "node_modules/@tailwindcss/oxide-wasm32-wasi": { 5985 + "version": "4.1.13", 5986 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", 5987 + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", 5988 + "bundleDependencies": [ 5989 + "@napi-rs/wasm-runtime", 5990 + "@emnapi/core", 5991 + "@emnapi/runtime", 5992 + "@tybys/wasm-util", 5993 + "@emnapi/wasi-threads", 5994 + "tslib" 5995 + ], 5996 + "cpu": [ 5997 + "wasm32" 5998 + ], 5999 + "dev": true, 6000 + "license": "MIT", 6001 + "optional": true, 6002 + "dependencies": { 6003 + "@emnapi/core": "^1.4.5", 6004 + "@emnapi/runtime": "^1.4.5", 6005 + "@emnapi/wasi-threads": "^1.0.4", 6006 + "@napi-rs/wasm-runtime": "^0.2.12", 6007 + "@tybys/wasm-util": "^0.10.0", 6008 + "tslib": "^2.8.0" 6009 + }, 6010 + "engines": { 6011 + "node": ">=14.0.0" 6012 + } 6013 + }, 6014 + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { 6015 + "version": "4.1.13", 6016 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", 6017 + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", 6018 + "cpu": [ 6019 + "arm64" 6020 + ], 6021 + "dev": true, 6022 + "license": "MIT", 6023 + "optional": true, 6024 + "os": [ 6025 + "win32" 6026 + ], 6027 + "engines": { 6028 + "node": ">= 10" 6029 + } 6030 + }, 6031 + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { 6032 + "version": "4.1.13", 6033 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", 6034 + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", 6035 + "cpu": [ 6036 + "x64" 6037 + ], 6038 + "dev": true, 6039 + "license": "MIT", 6040 + "optional": true, 6041 + "os": [ 6042 + "win32" 6043 + ], 6044 + "engines": { 6045 + "node": ">= 10" 6046 + } 6047 + }, 6048 + "node_modules/@tailwindcss/oxide/node_modules/tar": { 6049 + "version": "7.5.1", 6050 + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", 6051 + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", 6052 + "dev": true, 6053 + "license": "ISC", 6054 + "dependencies": { 6055 + "@isaacs/fs-minipass": "^4.0.0", 6056 + "chownr": "^3.0.0", 6057 + "minipass": "^7.1.2", 6058 + "minizlib": "^3.1.0", 6059 + "yallist": "^5.0.0" 6060 + }, 6061 + "engines": { 6062 + "node": ">=18" 6063 + } 6064 + }, 6065 + "node_modules/@tailwindcss/postcss": { 6066 + "version": "4.1.13", 6067 + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz", 6068 + "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==", 6069 + "dev": true, 6070 + "license": "MIT", 6071 + "dependencies": { 6072 + "@alloc/quick-lru": "^5.2.0", 6073 + "@tailwindcss/node": "4.1.13", 6074 + "@tailwindcss/oxide": "4.1.13", 6075 + "postcss": "^8.4.41", 6076 + "tailwindcss": "4.1.13" 6077 + } 6078 + }, 6079 "node_modules/@tiptap/core": { 6080 "version": "2.11.5", 6081 "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.5.tgz", ··· 6121 } 6122 }, 6123 "node_modules/@ts-morph/common": { 6124 + "version": "0.25.0", 6125 + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.25.0.tgz", 6126 + "integrity": "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==", 6127 "dev": true, 6128 "license": "MIT", 6129 "dependencies": { 6130 + "minimatch": "^9.0.4", 6131 + "path-browserify": "^1.0.1", 6132 + "tinyglobby": "^0.2.9" 6133 } 6134 }, 6135 "node_modules/@ts-morph/common/node_modules/brace-expansion": { 6136 + "version": "2.0.2", 6137 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", 6138 + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", 6139 "dev": true, 6140 "license": "MIT", 6141 "dependencies": { ··· 6143 } 6144 }, 6145 "node_modules/@ts-morph/common/node_modules/minimatch": { 6146 + "version": "9.0.5", 6147 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 6148 + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 6149 "dev": true, 6150 "license": "ISC", 6151 "dependencies": { 6152 "brace-expansion": "^2.0.1" 6153 }, 6154 "engines": { 6155 + "node": ">=16 || 14 >=14.17" 6156 }, 6157 + "funding": { 6158 + "url": "https://github.com/sponsors/isaacs" 6159 } 6160 }, 6161 "node_modules/@types/acorn": { ··· 6805 "url": "https://github.com/chalk/ansi-styles?sponsor=1" 6806 } 6807 }, 6808 "node_modules/anymatch": { 6809 "version": "3.1.3", 6810 "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", ··· 6817 "engines": { 6818 "node": ">= 8" 6819 } 6820 }, 6821 "node_modules/argparse": { 6822 "version": "2.0.1", ··· 7052 "node": ">=8.0.0" 7053 } 7054 }, 7055 "node_modules/available-typed-arrays": { 7056 "version": "1.0.7", 7057 "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", ··· 7266 "node": ">=8" 7267 } 7268 }, 7269 "node_modules/buffer": { 7270 "version": "6.0.3", 7271 "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", ··· 7368 "node": ">=6" 7369 } 7370 }, 7371 "node_modules/caniuse-lite": { 7372 "version": "1.0.30001717", 7373 "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", ··· 7617 } 7618 }, 7619 "node_modules/code-block-writer": { 7620 + "version": "13.0.3", 7621 + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", 7622 + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", 7623 "dev": true, 7624 "license": "MIT" 7625 }, ··· 7796 "node": ">= 8" 7797 } 7798 }, 7799 "node_modules/csstype": { 7800 "version": "3.1.3", 7801 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", ··· 8050 "url": "https://github.com/sponsors/wooorm" 8051 } 8052 }, 8053 "node_modules/difflib": { 8054 "version": "0.2.4", 8055 "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", ··· 8062 "node": "*" 8063 } 8064 }, 8065 "node_modules/doctrine": { 8066 "version": "3.0.0", 8067 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", ··· 8291 "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", 8292 "license": "MIT" 8293 }, 8294 "node_modules/ecdsa-sig-formatter": { 8295 "version": "1.0.11", 8296 "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", ··· 8306 "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 8307 "license": "MIT" 8308 }, 8309 "node_modules/emoji-regex": { 8310 "version": "9.2.2", 8311 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", ··· 8322 } 8323 }, 8324 "node_modules/enhanced-resolve": { 8325 + "version": "5.18.3", 8326 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", 8327 + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", 8328 "dev": true, 8329 + "license": "MIT", 8330 "dependencies": { 8331 "graceful-fs": "^4.2.4", 8332 "tapable": "^2.2.0" ··· 8740 } 8741 }, 8742 "node_modules/eslint-config-next": { 8743 + "version": "15.5.3", 8744 + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.3.tgz", 8745 + "integrity": "sha512-e6j+QhQFOr5pfsc8VJbuTD9xTXJaRvMHYjEeLPA2pFkheNlgPLCkxdvhxhfuM4KGcqSZj2qEnpHisdTVs3BxuQ==", 8746 "dev": true, 8747 "license": "MIT", 8748 "dependencies": { 8749 + "@next/eslint-plugin-next": "15.5.3", 8750 "@rushstack/eslint-patch": "^1.10.3", 8751 "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", 8752 "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", ··· 9619 "url": "https://github.com/sponsors/ljharb" 9620 } 9621 }, 9622 "node_modules/form-data": { 9623 "version": "4.0.0", 9624 "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", ··· 9658 "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", 9659 "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", 9660 "license": "MIT" 9661 }, 9662 "node_modules/fractional-indexing": { 9663 "version": "3.2.0", ··· 11275 } 11276 }, 11277 "node_modules/jiti": { 11278 + "version": "2.6.0", 11279 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz", 11280 + "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==", 11281 "dev": true, 11282 + "license": "MIT", 11283 "bin": { 11284 + "jiti": "lib/jiti-cli.mjs" 11285 } 11286 }, 11287 "node_modules/jose": { ··· 11521 "url": "https://github.com/sponsors/dmonad" 11522 } 11523 }, 11524 + "node_modules/lightningcss": { 11525 + "version": "1.30.1", 11526 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", 11527 + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", 11528 + "dev": true, 11529 + "license": "MPL-2.0", 11530 + "dependencies": { 11531 + "detect-libc": "^2.0.3" 11532 + }, 11533 + "engines": { 11534 + "node": ">= 12.0.0" 11535 + }, 11536 + "funding": { 11537 + "type": "opencollective", 11538 + "url": "https://opencollective.com/parcel" 11539 + }, 11540 + "optionalDependencies": { 11541 + "lightningcss-darwin-arm64": "1.30.1", 11542 + "lightningcss-darwin-x64": "1.30.1", 11543 + "lightningcss-freebsd-x64": "1.30.1", 11544 + "lightningcss-linux-arm-gnueabihf": "1.30.1", 11545 + "lightningcss-linux-arm64-gnu": "1.30.1", 11546 + "lightningcss-linux-arm64-musl": "1.30.1", 11547 + "lightningcss-linux-x64-gnu": "1.30.1", 11548 + "lightningcss-linux-x64-musl": "1.30.1", 11549 + "lightningcss-win32-arm64-msvc": "1.30.1", 11550 + "lightningcss-win32-x64-msvc": "1.30.1" 11551 + } 11552 + }, 11553 + "node_modules/lightningcss-darwin-arm64": { 11554 + "version": "1.30.1", 11555 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", 11556 + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", 11557 + "cpu": [ 11558 + "arm64" 11559 + ], 11560 + "dev": true, 11561 + "license": "MPL-2.0", 11562 + "optional": true, 11563 + "os": [ 11564 + "darwin" 11565 + ], 11566 + "engines": { 11567 + "node": ">= 12.0.0" 11568 + }, 11569 + "funding": { 11570 + "type": "opencollective", 11571 + "url": "https://opencollective.com/parcel" 11572 + } 11573 + }, 11574 + "node_modules/lightningcss-darwin-x64": { 11575 + "version": "1.30.1", 11576 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", 11577 + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", 11578 + "cpu": [ 11579 + "x64" 11580 + ], 11581 + "dev": true, 11582 + "license": "MPL-2.0", 11583 + "optional": true, 11584 + "os": [ 11585 + "darwin" 11586 + ], 11587 + "engines": { 11588 + "node": ">= 12.0.0" 11589 + }, 11590 + "funding": { 11591 + "type": "opencollective", 11592 + "url": "https://opencollective.com/parcel" 11593 + } 11594 + }, 11595 + "node_modules/lightningcss-freebsd-x64": { 11596 + "version": "1.30.1", 11597 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", 11598 + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", 11599 + "cpu": [ 11600 + "x64" 11601 + ], 11602 + "dev": true, 11603 + "license": "MPL-2.0", 11604 + "optional": true, 11605 + "os": [ 11606 + "freebsd" 11607 + ], 11608 + "engines": { 11609 + "node": ">= 12.0.0" 11610 + }, 11611 + "funding": { 11612 + "type": "opencollective", 11613 + "url": "https://opencollective.com/parcel" 11614 + } 11615 + }, 11616 + "node_modules/lightningcss-linux-arm-gnueabihf": { 11617 + "version": "1.30.1", 11618 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", 11619 + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", 11620 + "cpu": [ 11621 + "arm" 11622 + ], 11623 + "dev": true, 11624 + "license": "MPL-2.0", 11625 + "optional": true, 11626 + "os": [ 11627 + "linux" 11628 + ], 11629 + "engines": { 11630 + "node": ">= 12.0.0" 11631 + }, 11632 + "funding": { 11633 + "type": "opencollective", 11634 + "url": "https://opencollective.com/parcel" 11635 + } 11636 + }, 11637 + "node_modules/lightningcss-linux-arm64-gnu": { 11638 + "version": "1.30.1", 11639 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", 11640 + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", 11641 + "cpu": [ 11642 + "arm64" 11643 + ], 11644 + "dev": true, 11645 + "license": "MPL-2.0", 11646 + "optional": true, 11647 + "os": [ 11648 + "linux" 11649 + ], 11650 + "engines": { 11651 + "node": ">= 12.0.0" 11652 + }, 11653 + "funding": { 11654 + "type": "opencollective", 11655 + "url": "https://opencollective.com/parcel" 11656 + } 11657 + }, 11658 + "node_modules/lightningcss-linux-arm64-musl": { 11659 + "version": "1.30.1", 11660 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", 11661 + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", 11662 + "cpu": [ 11663 + "arm64" 11664 + ], 11665 + "dev": true, 11666 + "license": "MPL-2.0", 11667 + "optional": true, 11668 + "os": [ 11669 + "linux" 11670 + ], 11671 + "engines": { 11672 + "node": ">= 12.0.0" 11673 + }, 11674 + "funding": { 11675 + "type": "opencollective", 11676 + "url": "https://opencollective.com/parcel" 11677 + } 11678 + }, 11679 + "node_modules/lightningcss-linux-x64-gnu": { 11680 + "version": "1.30.1", 11681 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", 11682 + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", 11683 + "cpu": [ 11684 + "x64" 11685 + ], 11686 "dev": true, 11687 + "license": "MPL-2.0", 11688 + "optional": true, 11689 + "os": [ 11690 + "linux" 11691 + ], 11692 "engines": { 11693 + "node": ">= 12.0.0" 11694 + }, 11695 + "funding": { 11696 + "type": "opencollective", 11697 + "url": "https://opencollective.com/parcel" 11698 } 11699 }, 11700 + "node_modules/lightningcss-linux-x64-musl": { 11701 + "version": "1.30.1", 11702 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", 11703 + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", 11704 + "cpu": [ 11705 + "x64" 11706 + ], 11707 + "dev": true, 11708 + "license": "MPL-2.0", 11709 + "optional": true, 11710 + "os": [ 11711 + "linux" 11712 + ], 11713 + "engines": { 11714 + "node": ">= 12.0.0" 11715 + }, 11716 + "funding": { 11717 + "type": "opencollective", 11718 + "url": "https://opencollective.com/parcel" 11719 + } 11720 + }, 11721 + "node_modules/lightningcss-win32-arm64-msvc": { 11722 + "version": "1.30.1", 11723 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", 11724 + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", 11725 + "cpu": [ 11726 + "arm64" 11727 + ], 11728 + "dev": true, 11729 + "license": "MPL-2.0", 11730 + "optional": true, 11731 + "os": [ 11732 + "win32" 11733 + ], 11734 + "engines": { 11735 + "node": ">= 12.0.0" 11736 + }, 11737 + "funding": { 11738 + "type": "opencollective", 11739 + "url": "https://opencollective.com/parcel" 11740 + } 11741 + }, 11742 + "node_modules/lightningcss-win32-x64-msvc": { 11743 + "version": "1.30.1", 11744 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", 11745 + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", 11746 + "cpu": [ 11747 + "x64" 11748 + ], 11749 + "dev": true, 11750 + "license": "MPL-2.0", 11751 + "optional": true, 11752 + "os": [ 11753 + "win32" 11754 + ], 11755 + "engines": { 11756 + "node": ">= 12.0.0" 11757 + }, 11758 + "funding": { 11759 + "type": "opencollective", 11760 + "url": "https://opencollective.com/parcel" 11761 + } 11762 }, 11763 "node_modules/linkify-it": { 11764 "version": "5.0.0", ··· 13110 } 13111 }, 13112 "node_modules/minizlib": { 13113 + "version": "3.1.0", 13114 + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", 13115 + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", 13116 "dev": true, 13117 + "license": "MIT", 13118 "dependencies": { 13119 + "minipass": "^7.1.2" 13120 }, 13121 "engines": { 13122 "node": ">= 18" 13123 } 13124 }, 13125 "node_modules/mkdirp": { 13126 "version": "3.0.1", 13127 "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", ··· 13173 "mustache": "bin/mustache" 13174 } 13175 }, 13176 "node_modules/nanoid": { 13177 + "version": "3.3.11", 13178 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 13179 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 13180 "funding": [ 13181 { 13182 "type": "github", 13183 "url": "https://github.com/sponsors/ai" 13184 } 13185 ], 13186 + "license": "MIT", 13187 "bin": { 13188 "nanoid": "bin/nanoid.cjs" 13189 }, ··· 13207 } 13208 }, 13209 "node_modules/next": { 13210 + "version": "15.5.3", 13211 + "resolved": "https://registry.npmjs.org/next/-/next-15.5.3.tgz", 13212 + "integrity": "sha512-r/liNAx16SQj4D+XH/oI1dlpv9tdKJ6cONYPwwcCC46f2NjpaRWY+EKCzULfgQYV6YKXjHBchff2IZBSlZmJNw==", 13213 "license": "MIT", 13214 "dependencies": { 13215 + "@next/env": "15.5.3", 13216 "@swc/helpers": "0.5.15", 13217 "caniuse-lite": "^1.0.30001579", 13218 "postcss": "8.4.31", ··· 13225 "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" 13226 }, 13227 "optionalDependencies": { 13228 + "@next/swc-darwin-arm64": "15.5.3", 13229 + "@next/swc-darwin-x64": "15.5.3", 13230 + "@next/swc-linux-arm64-gnu": "15.5.3", 13231 + "@next/swc-linux-arm64-musl": "15.5.3", 13232 + "@next/swc-linux-x64-gnu": "15.5.3", 13233 + "@next/swc-linux-x64-musl": "15.5.3", 13234 + "@next/swc-win32-arm64-msvc": "15.5.3", 13235 + "@next/swc-win32-x64-msvc": "15.5.3", 13236 "sharp": "^0.34.3" 13237 }, 13238 "peerDependencies": { ··· 13358 "node-gyp-build-optional-packages-test": "build-test.js" 13359 } 13360 }, 13361 "node_modules/normalize-path": { 13362 "version": "3.0.0", 13363 "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", ··· 13367 "node": ">=0.10.0" 13368 } 13369 }, 13370 "node_modules/npm-normalize-package-bin": { 13371 "version": "3.0.1", 13372 "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", ··· 13383 "dev": true, 13384 "engines": { 13385 "node": ">=0.10.0" 13386 } 13387 }, 13388 "node_modules/object-inspect": { ··· 13655 "node": ">=8" 13656 } 13657 }, 13658 "node_modules/parent-module": { 13659 "version": "1.0.1", 13660 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", ··· 13750 "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 13751 "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 13752 }, 13753 "node_modules/path-to-regexp": { 13754 "version": "6.2.2", 13755 "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", ··· 13846 } 13847 }, 13848 "node_modules/picocolors": { 13849 + "version": "1.1.1", 13850 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 13851 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 13852 + "license": "ISC" 13853 }, 13854 "node_modules/picomatch": { 13855 "version": "2.3.1", ··· 13863 "url": "https://github.com/sponsors/jonschlinkert" 13864 } 13865 }, 13866 "node_modules/pino": { 13867 "version": "8.21.0", 13868 "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", ··· 13901 "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 13902 "license": "MIT" 13903 }, 13904 "node_modules/possible-typed-array-names": { 13905 "version": "1.1.0", 13906 "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", ··· 13912 } 13913 }, 13914 "node_modules/postcss": { 13915 + "version": "8.5.6", 13916 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 13917 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 13918 "dev": true, 13919 "funding": [ 13920 { ··· 13930 "url": "https://github.com/sponsors/ai" 13931 } 13932 ], 13933 + "license": "MIT", 13934 "dependencies": { 13935 + "nanoid": "^3.3.11", 13936 + "picocolors": "^1.1.1", 13937 + "source-map-js": "^1.2.1" 13938 }, 13939 "engines": { 13940 "node": "^10 || ^12 || >=14" 13941 } 13942 }, 13943 "node_modules/postgres": { 13944 "version": "3.4.4", 13945 "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", ··· 14324 "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 14325 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 14326 }, 14327 "node_modules/punycode": { 14328 "version": "2.3.1", 14329 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 14680 "peerDependencies": { 14681 "react": ">=16.13", 14682 "react-dom": ">=16.13" 14683 } 14684 }, 14685 "node_modules/read-cmd-shim": { ··· 15723 } 15724 }, 15725 "node_modules/source-map-js": { 15726 + "version": "1.2.1", 15727 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 15728 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 15729 + "license": "BSD-3-Clause", 15730 "engines": { 15731 "node": ">=0.10.0" 15732 } ··· 15823 "node": ">=8" 15824 } 15825 }, 15826 "node_modules/string-width/node_modules/emoji-regex": { 15827 "version": "8.0.0", 15828 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", ··· 15964 "node": ">=8" 15965 } 15966 }, 15967 "node_modules/strip-bom": { 15968 "version": "3.0.0", 15969 "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", ··· 16016 } 16017 } 16018 }, 16019 "node_modules/supabase": { 16020 "version": "1.187.3", 16021 "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.187.3.tgz", ··· 16071 } 16072 }, 16073 "node_modules/tailwindcss": { 16074 + "version": "4.1.13", 16075 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", 16076 + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", 16077 "dev": true, 16078 + "license": "MIT" 16079 }, 16080 "node_modules/tapable": { 16081 "version": "2.2.1", ··· 16124 "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 16125 "dev": true 16126 }, 16127 "node_modules/thread-stream": { 16128 "version": "2.7.0", 16129 "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", ··· 16148 "next-tick": "1" 16149 } 16150 }, 16151 + "node_modules/tinyglobby": { 16152 + "version": "0.2.15", 16153 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 16154 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 16155 + "dev": true, 16156 + "license": "MIT", 16157 + "dependencies": { 16158 + "fdir": "^6.5.0", 16159 + "picomatch": "^4.0.3" 16160 + }, 16161 + "engines": { 16162 + "node": ">=12.0.0" 16163 + }, 16164 + "funding": { 16165 + "url": "https://github.com/sponsors/SuperchupuDev" 16166 + } 16167 + }, 16168 + "node_modules/tinyglobby/node_modules/fdir": { 16169 + "version": "6.5.0", 16170 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 16171 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 16172 + "dev": true, 16173 + "license": "MIT", 16174 + "engines": { 16175 + "node": ">=12.0.0" 16176 + }, 16177 + "peerDependencies": { 16178 + "picomatch": "^3 || ^4" 16179 + }, 16180 + "peerDependenciesMeta": { 16181 + "picomatch": { 16182 + "optional": true 16183 + } 16184 + } 16185 + }, 16186 + "node_modules/tinyglobby/node_modules/picomatch": { 16187 + "version": "4.0.3", 16188 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 16189 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 16190 + "dev": true, 16191 + "license": "MIT", 16192 + "engines": { 16193 + "node": ">=12" 16194 + }, 16195 + "funding": { 16196 + "url": "https://github.com/sponsors/jonschlinkert" 16197 + } 16198 + }, 16199 "node_modules/tlds": { 16200 "version": "1.256.0", 16201 "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.256.0.tgz", ··· 16287 "typescript": ">=4.8.4" 16288 } 16289 }, 16290 "node_modules/ts-morph": { 16291 + "version": "24.0.0", 16292 + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-24.0.0.tgz", 16293 + "integrity": "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==", 16294 "dev": true, 16295 "license": "MIT", 16296 "dependencies": { 16297 + "@ts-morph/common": "~0.25.0", 16298 + "code-block-writer": "^13.0.3" 16299 } 16300 }, 16301 "node_modules/tsconfig-paths": { ··· 16555 } 16556 }, 16557 "node_modules/undici": { 16558 + "version": "6.21.3", 16559 + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", 16560 + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", 16561 "license": "MIT", 16562 "engines": { 16563 "node": ">=18.17" ··· 16684 "node": ">= 0.8" 16685 } 16686 }, 16687 "node_modules/use-callback-ref": { 16688 "version": "1.3.3", 16689 "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", ··· 16735 "peerDependencies": { 16736 "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 16737 } 16738 }, 16739 "node_modules/utils-merge": { 16740 "version": "1.0.1", ··· 17163 "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 17164 } 17165 }, 17166 "node_modules/wrappy": { 17167 "version": "1.0.2", 17168 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", ··· 17297 "dev": true, 17298 "engines": { 17299 "node": ">=18" 17300 } 17301 }, 17302 "node_modules/yargs": {
+11 -11
package.json
··· 19 "author": "", 20 "license": "ISC", 21 "dependencies": { 22 - "@atproto/api": "^0.14.2", 23 "@atproto/common": "^0.4.8", 24 "@atproto/identity": "^0.4.6", 25 - "@atproto/oauth-client-node": "^0.2.17", 26 - "@atproto/sync": "^0.1.32", 27 "@atproto/syntax": "^0.3.3", 28 - "@atproto/xrpc": "^0.6.9", 29 - "@atproto/xrpc-server": "^0.7.19", 30 "@hono/node-server": "^1.14.3", 31 "@mdx-js/loader": "^3.1.0", 32 "@mdx-js/react": "^3.1.0", ··· 58 "katex": "^0.16.22", 59 "linkifyjs": "^4.2.0", 60 "multiformats": "^13.3.2", 61 - "next": "^15.5.0", 62 "pg": "^8.16.3", 63 "prosemirror-commands": "^1.5.2", 64 "prosemirror-inputrules": "^1.4.0", ··· 93 "zustand": "^5.0.4" 94 }, 95 "devDependencies": { 96 - "@atproto/lex-cli": "^0.6.1", 97 - "@atproto/lexicon": "^0.4.7", 98 "@cloudflare/workers-types": "^4.20240512.0", 99 "@types/katex": "^0.16.7", 100 "@types/node": "^22.15.17", 101 "@types/react": "19.1.3", 102 "@types/react-dom": "19.1.3", 103 "@types/uuid": "^10.0.0", 104 - "autoprefixer": "^10.4.19", 105 "drizzle-kit": "^0.21.2", 106 "esbuild": "^0.25.4", 107 "eslint": "8.57.0", 108 - "eslint-config-next": "^15.4.4", 109 "postcss": "^8.4.38", 110 "prettier": "3.2.5", 111 "supabase": "^1.187.3", 112 - "tailwindcss": "^3.4.3", 113 "tsx": "^4.19.3", 114 "typescript": "^5.8.3", 115 "wrangler": "^3.56.0"
··· 19 "author": "", 20 "license": "ISC", 21 "dependencies": { 22 + "@atproto/api": "^0.16.9", 23 "@atproto/common": "^0.4.8", 24 "@atproto/identity": "^0.4.6", 25 + "@atproto/lexicon": "^0.5.1", 26 + "@atproto/oauth-client-node": "^0.3.8", 27 + "@atproto/sync": "^0.1.34", 28 "@atproto/syntax": "^0.3.3", 29 + "@atproto/xrpc": "^0.7.5", 30 + "@atproto/xrpc-server": "^0.9.5", 31 "@hono/node-server": "^1.14.3", 32 "@mdx-js/loader": "^3.1.0", 33 "@mdx-js/react": "^3.1.0", ··· 59 "katex": "^0.16.22", 60 "linkifyjs": "^4.2.0", 61 "multiformats": "^13.3.2", 62 + "next": "^15.5.3", 63 "pg": "^8.16.3", 64 "prosemirror-commands": "^1.5.2", 65 "prosemirror-inputrules": "^1.4.0", ··· 94 "zustand": "^5.0.4" 95 }, 96 "devDependencies": { 97 + "@atproto/lex-cli": "^0.9.5", 98 "@cloudflare/workers-types": "^4.20240512.0", 99 + "@tailwindcss/postcss": "^4.1.13", 100 "@types/katex": "^0.16.7", 101 "@types/node": "^22.15.17", 102 "@types/react": "19.1.3", 103 "@types/react-dom": "19.1.3", 104 "@types/uuid": "^10.0.0", 105 "drizzle-kit": "^0.21.2", 106 "esbuild": "^0.25.4", 107 "eslint": "8.57.0", 108 + "eslint-config-next": "^15.5.3", 109 "postcss": "^8.4.38", 110 "prettier": "3.2.5", 111 "supabase": "^1.187.3", 112 + "tailwindcss": "^4.1.13", 113 "tsx": "^4.19.3", 114 "typescript": "^5.8.3", 115 "wrangler": "^3.56.0"
+1 -2
postcss.config.js
··· 1 module.exports = { 2 plugins: { 3 - tailwindcss: {}, 4 - autoprefixer: {}, 5 }, 6 }
··· 1 module.exports = { 2 plugins: { 3 + '@tailwindcss/postcss': {}, 4 }, 5 }
+7 -5
src/utils/getBlocksAsHTML.tsx
··· 152 let [url] = await scanIndex(tx).eav(b.value, "link/url"); 153 let [title] = await scanIndex(tx).eav(b.value, "link/title"); 154 if (!url) return ""; 155 - return renderToStaticMarkup( 156 <a href={url.data.value} target="_blank"> 157 {title.data.value} 158 - </a>, 159 ); 160 }, 161 card: async (b, tx, a) => { 162 let [card] = await scanIndex(tx).eav(b.value, "block/card"); 163 let facts = await getAllFacts(tx, card.data.value); 164 - return renderToStaticMarkup( 165 <div 166 data-type="card" 167 data-facts={btoa(JSON.stringify(facts))} 168 data-entityid={card.data.value} 169 - />, 170 ); 171 }, 172 text: async (b, tx, a) => { ··· 186 async function renderBlock(b: Block, tx: ReadTransaction) { 187 let [alignment] = await scanIndex(tx).eav(b.value, "block/text-alignment"); 188 let toHtml = BlockTypeToHTML[b.type]; 189 - return renderToStaticMarkup(await toHtml(b, tx, alignment?.data.value)); 190 }
··· 152 let [url] = await scanIndex(tx).eav(b.value, "link/url"); 153 let [title] = await scanIndex(tx).eav(b.value, "link/title"); 154 if (!url) return ""; 155 + return ( 156 <a href={url.data.value} target="_blank"> 157 {title.data.value} 158 + </a> 159 ); 160 }, 161 card: async (b, tx, a) => { 162 let [card] = await scanIndex(tx).eav(b.value, "block/card"); 163 let facts = await getAllFacts(tx, card.data.value); 164 + return ( 165 <div 166 data-type="card" 167 data-facts={btoa(JSON.stringify(facts))} 168 data-entityid={card.data.value} 169 + /> 170 ); 171 }, 172 text: async (b, tx, a) => { ··· 186 async function renderBlock(b: Block, tx: ReadTransaction) { 187 let [alignment] = await scanIndex(tx).eav(b.value, "block/text-alignment"); 188 let toHtml = BlockTypeToHTML[b.type]; 189 + let element = await toHtml(b, tx, alignment?.data.value); 190 + console.log(element); 191 + return renderToStaticMarkup(element); 192 }
+4 -1
src/utils/getMicroLinkOgImage.ts
··· 9 let protocol = headersList.get("x-forwarded-proto"); 10 let full_path = `${protocol}://${hostname}${path}`; 11 let response = await fetch( 12 - `https://pro.microlink.io/?url=${encodeURIComponent(full_path)}&screenshot=true&viewport.width=${options?.width || 1200}&viewport.height=${options?.height || 733}&viewport.deviceScaleFactor=${options?.deviceScaleFactor || 1}&meta=false&embed=screenshot.url&force=true`, 13 { 14 headers: { 15 "x-api-key": process.env.MICROLINK_API_KEY!, 16 }, 17 }, 18 );
··· 9 let protocol = headersList.get("x-forwarded-proto"); 10 let full_path = `${protocol}://${hostname}${path}`; 11 let response = await fetch( 12 + `https://pro.microlink.io/?url=${encodeURIComponent(full_path)}&screenshot=true&viewport.width=${options?.width || 1400}&viewport.height=${options?.height || 733}&viewport.deviceScaleFactor=${options?.deviceScaleFactor || 1}&meta=false&embed=screenshot.url&force=true`, 13 { 14 headers: { 15 "x-api-key": process.env.MICROLINK_API_KEY!, 16 + }, 17 + next: { 18 + revalidate: 600, 19 }, 20 }, 21 );
+22
src/utils/timeAgo.ts
···
··· 1 + export function timeAgo(timestamp: string): string { 2 + const now = new Date(); 3 + const date = new Date(timestamp); 4 + const diffMs = now.getTime() - date.getTime(); 5 + const diffSeconds = Math.floor(diffMs / 1000); 6 + const diffMinutes = Math.floor(diffSeconds / 60); 7 + const diffHours = Math.floor(diffMinutes / 60); 8 + const diffDays = Math.floor(diffHours / 24); 9 + const diffYears = Math.floor(diffDays / 365); 10 + 11 + if (diffYears > 0) { 12 + return `${diffYears} year${diffYears === 1 ? "" : "s"} ago`; 13 + } else if (diffDays > 0) { 14 + return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`; 15 + } else if (diffHours > 0) { 16 + return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`; 17 + } else if (diffMinutes > 0) { 18 + return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`; 19 + } else { 20 + return "just now"; 21 + } 22 + }
+3
supabase/database.types.ts
··· 461 email: string | null 462 home_page: string 463 id: string 464 } 465 Insert: { 466 atp_did?: string | null ··· 468 email?: string | null 469 home_page: string 470 id?: string 471 } 472 Update: { 473 atp_did?: string | null ··· 475 email?: string | null 476 home_page?: string 477 id?: string 478 } 479 Relationships: [ 480 {
··· 461 email: string | null 462 home_page: string 463 id: string 464 + interface_state: Json | null 465 } 466 Insert: { 467 atp_did?: string | null ··· 469 email?: string | null 470 home_page: string 471 id?: string 472 + interface_state?: Json | null 473 } 474 Update: { 475 atp_did?: string | null ··· 477 email?: string | null 478 home_page?: string 479 id?: string 480 + interface_state?: Json | null 481 } 482 Relationships: [ 483 {
+1
supabase/migrations/20250922052532_add_interface_state_col_to_identity_table.sql
···
··· 1 + alter table "public"."identities" add column "interface_state" jsonb;