From 97ce005ccd3c7e37e71b57f5de37557d3ea83c9c Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Tue, 16 Sep 2025 15:17:04 +0900 Subject: [PATCH] add page block type to lexicon and basic component --- actions/publishToPublication.ts | 485 ++++++++---------- .../[publication]/[rkey]/PostContent.tsx | 23 +- .../[did]/[publication]/[rkey]/PostPage.tsx | 56 +- components/Blocks/BlockCommands.tsx | 1 - components/Pages/index.tsx | 4 +- lexicons/api/index.ts | 2 + lexicons/api/lexicons.ts | 21 + .../types/pub/leaflet/pages/linearDocument.ts | 5 +- .../pub/leaflet/pages/linearDocument.json | 9 +- lexicons/src/blocks.ts | 15 + lexicons/src/pages/LinearDocument.ts | 2 + 11 files changed, 356 insertions(+), 267 deletions(-) diff --git a/actions/publishToPublication.ts b/actions/publishToPublication.ts index 74993a57..33fb9fff 100644 --- a/actions/publishToPublication.ts +++ b/actions/publishToPublication.ts @@ -20,6 +20,7 @@ import { PubLeafletBlocksBskyPost, PubLeafletBlocksBlockquote, PubLeafletBlocksIframe, + PubLeafletBlocksPage, } from "lexicons/api"; import { Block } from "components/Blocks/Block"; import { TID } from "@atproto/common"; @@ -72,35 +73,10 @@ export async function publishToPublication({ root: root_entity, }); let facts = (data as unknown as Fact[]) || []; - let scan = scanIndexLocal(facts); - let firstEntity = scan.eav(root_entity, "root/page")?.[0]; - if (!firstEntity) throw new Error("No root page"); - let blocks = getBlocksWithTypeLocal(facts, firstEntity?.data.value); - let images = blocks - .filter((b) => b.type === "image") - .map((b) => scan.eav(b.value, "block/image")[0]); - let links = blocks - .filter((b) => b.type == "link") - .map((b) => scan.eav(b.value, "link/preview")[0]); - let imageMap = new Map(); - await Promise.all( - [...links, ...images].map(async (b) => { - if (!b) return; - let data = await fetch(b.data.src); - if (data.status !== 200) return; - let binary = await data.blob(); - let blob = await agent.com.atproto.repo.uploadBlob(binary, { - headers: { "Content-Type": binary.type }, - }); - imageMap.set(b.data.src, blob.data.blob); - }), - ); - - let b: PubLeafletPagesLinearDocument.Block[] = blocksToRecord( - blocks, - imageMap, - scan, + let { firstPageBlocks, pages } = await processBlocksToPages( + facts, + agent, root_entity, ); @@ -117,8 +93,13 @@ export async function publishToPublication({ pages: [ { $type: "pub.leaflet.pages.linearDocument", - blocks: b, + blocks: firstPageBlocks, }, + ...pages.map((p) => ({ + $type: "pub.leaflet.pages.linearDocument", + id: p.id, + blocks: p.blocks, + })), ], }; let rkey = draft?.doc ? new AtUri(draft.doc).rkey : TID.nextStr(); @@ -152,248 +133,232 @@ export async function publishToPublication({ return { rkey, record: JSON.parse(JSON.stringify(record)) }; } -function blocksToRecord( - blocks: Block[], - imageMap: Map, - scan: ReturnType, - root_entity: string, -): PubLeafletPagesLinearDocument.Block[] { - let parsedBlocks = parseBlocksToList(blocks); - return parsedBlocks.flatMap((blockOrList) => { - if (blockOrList.type === "block") { - let alignmentValue = - scan.eav(blockOrList.block.value, "block/text-alignment")[0]?.data - .value || "left"; - let alignment = - alignmentValue === "center" - ? "lex:pub.leaflet.pages.linearDocument#textAlignCenter" - : alignmentValue === "right" - ? "lex:pub.leaflet.pages.linearDocument#textAlignRight" - : undefined; - let b = blockToRecord(blockOrList.block, imageMap, scan, root_entity); - if (!b) return []; - let block: PubLeafletPagesLinearDocument.Block = { - $type: "pub.leaflet.pages.linearDocument#block", - alignment, - block: b, - }; - return [block]; - } else { - let block: PubLeafletPagesLinearDocument.Block = { - $type: "pub.leaflet.pages.linearDocument#block", - block: { - $type: "pub.leaflet.blocks.unorderedList", - children: childrenToRecord( - blockOrList.children, - imageMap, - scan, - root_entity, - ), - }, - }; - return [block]; - } - }); -} - -function childrenToRecord( - children: List[], - imageMap: Map, - scan: ReturnType, +async function processBlocksToPages( + facts: Fact[], + agent: AtpBaseClient, root_entity: string, ) { - return children.flatMap((child) => { - let content = blockToRecord(child.block, imageMap, scan, root_entity); - if (!content) return []; - let record: PubLeafletBlocksUnorderedList.ListItem = { - $type: "pub.leaflet.blocks.unorderedList#listItem", - content, - children: childrenToRecord(child.children, imageMap, scan, root_entity), - }; - return record; - }); -} -function blockToRecord( - b: Block, - imageMap: Map, - scan: ReturnType, - root_entity: string, -) { - const getBlockContent = (b: string) => { - let [content] = scan.eav(b, "block/text"); - if (!content) return ["", [] as PubLeafletRichtextFacet.Main[]] as const; - let doc = new Y.Doc(); - const update = base64.toByteArray(content.data.value); - Y.applyUpdate(doc, update); - let nodes = doc.getXmlElement("prosemirror").toArray(); - let stringValue = YJSFragmentToString(nodes[0]); - let facets = YJSFragmentToFacets(nodes[0]); - return [stringValue, facets] as const; - }; - - if (b.type === "bluesky-post") { - let [post] = scan.eav(b.value, "block/bluesky-post"); - if (!post || !post.data.value.post) return; - let block: $Typed = { - $type: ids.PubLeafletBlocksBskyPost, - postRef: { - uri: post.data.value.post.uri, - cid: post.data.value.post.cid, - }, - }; - return block; - } - if (b.type === "horizontal-rule") { - let block: $Typed = { - $type: ids.PubLeafletBlocksHorizontalRule, - }; - return block; - } + let scan = scanIndexLocal(facts); + let pages: { id: string; blocks: PubLeafletPagesLinearDocument.Block[] }[] = + []; - if (b.type === "heading") { - let [headingLevel] = scan.eav(b.value, "block/heading-level"); + let firstEntity = scan.eav(root_entity, "root/page")?.[0]; + if (!firstEntity) throw new Error("No root page"); + let blocks = getBlocksWithTypeLocal(facts, firstEntity?.data.value); + let b = await blocksToRecord(blocks); + return { firstPageBlocks: b, pages }; - let [stringValue, facets] = getBlockContent(b.value); - let block: $Typed = { - $type: "pub.leaflet.blocks.header", - level: headingLevel?.data.value || 1, - plaintext: stringValue, - facets, - }; - return block; + async function uploadImage(src: string) { + let data = await fetch(src); + if (data.status !== 200) return; + let binary = await data.blob(); + let blob = await agent.com.atproto.repo.uploadBlob(binary, { + headers: { "Content-Type": binary.type }, + }); + return blob.data.blob; } - - if (b.type === "blockquote") { - let [stringValue, facets] = getBlockContent(b.value); - let block: $Typed = { - $type: ids.PubLeafletBlocksBlockquote, - plaintext: stringValue, - facets, - }; - return block; + async function blocksToRecord( + blocks: Block[], + ): Promise { + let parsedBlocks = parseBlocksToList(blocks); + return ( + await Promise.all( + parsedBlocks.map(async (blockOrList) => { + if (blockOrList.type === "block") { + let alignmentValue = + scan.eav(blockOrList.block.value, "block/text-alignment")[0]?.data + .value || "left"; + let alignment = + alignmentValue === "center" + ? "lex:pub.leaflet.pages.linearDocument#textAlignCenter" + : alignmentValue === "right" + ? "lex:pub.leaflet.pages.linearDocument#textAlignRight" + : undefined; + let b = await blockToRecord(blockOrList.block); + if (!b) return []; + let block: PubLeafletPagesLinearDocument.Block = { + $type: "pub.leaflet.pages.linearDocument#block", + alignment, + block: b, + }; + return [block]; + } else { + let block: PubLeafletPagesLinearDocument.Block = { + $type: "pub.leaflet.pages.linearDocument#block", + block: { + $type: "pub.leaflet.blocks.unorderedList", + children: await childrenToRecord(blockOrList.children), + }, + }; + return [block]; + } + }), + ) + ).flat(); } - if (b.type == "text") { - let [stringValue, facets] = getBlockContent(b.value); - let block: $Typed = { - $type: ids.PubLeafletBlocksText, - plaintext: stringValue, - facets, - }; - return block; - } - if (b.type === "embed") { - let [url] = scan.eav(b.value, "embed/url"); - let [height] = scan.eav(b.value, "embed/height"); - if (!url) return; - let block: $Typed = { - $type: "pub.leaflet.blocks.iframe", - url: url.data.value, - height: height?.data.value || 600, - }; - return block; - } - if (b.type == "image") { - let [image] = scan.eav(b.value, "block/image"); - if (!image) return; - let [altText] = scan.eav(b.value, "image/alt"); - let blobref = imageMap.get(image.data.src); - if (!blobref) return; - let block: $Typed = { - $type: "pub.leaflet.blocks.image", - image: blobref, - aspectRatio: { - height: image.data.height, - width: image.data.width, - }, - alt: altText ? altText.data.value : undefined, - }; - return block; - } - if (b.type === "link") { - let [previewImage] = scan.eav(b.value, "link/preview"); - let [description] = scan.eav(b.value, "link/description"); - let [src] = scan.eav(b.value, "link/url"); - if (!src) return; - let blobref = previewImage - ? imageMap.get(previewImage?.data.src) - : undefined; - let [title] = scan.eav(b.value, "link/title"); - let block: $Typed = { - $type: "pub.leaflet.blocks.website", - previewImage: blobref, - src: src.data.value, - description: description?.data.value, - title: title?.data.value, - }; - return block; - } - if (b.type === "code") { - let [language] = scan.eav(b.value, "block/code-language"); - let [code] = scan.eav(b.value, "block/code"); - let [theme] = scan.eav(root_entity, "theme/code-theme"); - let block: $Typed = { - $type: "pub.leaflet.blocks.code", - language: language?.data.value, - plaintext: code?.data.value || "", - syntaxHighlightingTheme: theme?.data.value, - }; - return block; + async function childrenToRecord(children: List[]) { + return ( + await Promise.all( + children.map(async (child) => { + let content = await blockToRecord(child.block); + if (!content) return []; + let record: PubLeafletBlocksUnorderedList.ListItem = { + $type: "pub.leaflet.blocks.unorderedList#listItem", + content, + children: await childrenToRecord(child.children), + }; + return record; + }), + ) + ).flat(); } - if (b.type === "math") { - let [math] = scan.eav(b.value, "block/math"); - let block: $Typed = { - $type: "pub.leaflet.blocks.math", - tex: math?.data.value || "", + async function blockToRecord(b: Block) { + const getBlockContent = (b: string) => { + let [content] = scan.eav(b, "block/text"); + if (!content) return ["", [] as PubLeafletRichtextFacet.Main[]] as const; + let doc = new Y.Doc(); + const update = base64.toByteArray(content.data.value); + Y.applyUpdate(doc, update); + let nodes = doc.getXmlElement("prosemirror").toArray(); + let stringValue = YJSFragmentToString(nodes[0]); + let facets = YJSFragmentToFacets(nodes[0]); + return [stringValue, facets] as const; }; - return block; - } - return; -} + if (b.type === "card") { + let [page] = scan.eav(b.value, "block/card"); + if (!page) return; + let blocks = getBlocksWithTypeLocal(facts, page.data.value); + pages.push({ + id: page.data.value, + blocks: await blocksToRecord(blocks), + }); + let block: $Typed = { + $type: "pub.leaflet.blocks.page", + id: page.data.value, + }; + return block; + } -async function sendPostToEmailSubscribers( - publication_uri: string, - post: { content: string; title: string }, -) { - let { data: publication } = await supabaseServerClient - .from("publications") - .select("*, subscribers_to_publications(*)") - .eq("uri", publication_uri) - .single(); + if (b.type === "bluesky-post") { + let [post] = scan.eav(b.value, "block/bluesky-post"); + if (!post || !post.data.value.post) return; + let block: $Typed = { + $type: ids.PubLeafletBlocksBskyPost, + postRef: { + uri: post.data.value.post.uri, + cid: post.data.value.post.cid, + }, + }; + return block; + } + if (b.type === "horizontal-rule") { + let block: $Typed = { + $type: ids.PubLeafletBlocksHorizontalRule, + }; + return block; + } - let res = await fetch("https://api.postmarkapp.com/email/batch", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Postmark-Server-Token": process.env.POSTMARK_API_KEY!, - }, - body: JSON.stringify( - publication?.subscribers_to_publications.map((sub) => ({ - Headers: [ - { - Name: "List-Unsubscribe-Post", - Value: "List-Unsubscribe=One-Click", - }, - { - Name: "List-Unsubscribe", - Value: `<${"TODO"}/mail/unsubscribe?sub_id=${sub.identity}>`, - }, - ], - MessageStream: "broadcast", - From: `${publication.name} `, - Subject: post.title, - To: sub.identity, - HtmlBody: ` -

${publication.name}

-
- ${post.content} -
- This is a super alpha release! Ask Jared if you want to unsubscribe (sorry) - `, - TextBody: post.content, - })), - ), - }); + if (b.type === "heading") { + let [headingLevel] = scan.eav(b.value, "block/heading-level"); + + let [stringValue, facets] = getBlockContent(b.value); + let block: $Typed = { + $type: "pub.leaflet.blocks.header", + level: headingLevel?.data.value || 1, + plaintext: stringValue, + facets, + }; + return block; + } + + if (b.type === "blockquote") { + let [stringValue, facets] = getBlockContent(b.value); + let block: $Typed = { + $type: ids.PubLeafletBlocksBlockquote, + plaintext: stringValue, + facets, + }; + return block; + } + + if (b.type == "text") { + let [stringValue, facets] = getBlockContent(b.value); + let block: $Typed = { + $type: ids.PubLeafletBlocksText, + plaintext: stringValue, + facets, + }; + return block; + } + if (b.type === "embed") { + let [url] = scan.eav(b.value, "embed/url"); + let [height] = scan.eav(b.value, "embed/height"); + if (!url) return; + let block: $Typed = { + $type: "pub.leaflet.blocks.iframe", + url: url.data.value, + height: height?.data.value || 600, + }; + return block; + } + if (b.type == "image") { + let [image] = scan.eav(b.value, "block/image"); + if (!image) return; + let [altText] = scan.eav(b.value, "image/alt"); + let blobref = await uploadImage(image.data.src); + if (!blobref) return; + let block: $Typed = { + $type: "pub.leaflet.blocks.image", + image: blobref, + aspectRatio: { + height: image.data.height, + width: image.data.width, + }, + alt: altText ? altText.data.value : undefined, + }; + return block; + } + if (b.type === "link") { + let [previewImage] = scan.eav(b.value, "link/preview"); + let [description] = scan.eav(b.value, "link/description"); + let [src] = scan.eav(b.value, "link/url"); + if (!src) return; + let blobref = previewImage + ? await uploadImage(previewImage?.data.src) + : undefined; + let [title] = scan.eav(b.value, "link/title"); + let block: $Typed = { + $type: "pub.leaflet.blocks.website", + previewImage: blobref, + src: src.data.value, + description: description?.data.value, + title: title?.data.value, + }; + return block; + } + if (b.type === "code") { + let [language] = scan.eav(b.value, "block/code-language"); + let [code] = scan.eav(b.value, "block/code"); + let [theme] = scan.eav(root_entity, "theme/code-theme"); + let block: $Typed = { + $type: "pub.leaflet.blocks.code", + language: language?.data.value, + plaintext: code?.data.value || "", + syntaxHighlightingTheme: theme?.data.value, + }; + return block; + } + if (b.type === "math") { + let [math] = scan.eav(b.value, "block/math"); + let block: $Typed = { + $type: "pub.leaflet.blocks.math", + tex: math?.data.value || "", + }; + return block; + } + return; + } } function YJSFragmentToFacets( diff --git a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx index 51da42a0..1801882a 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx @@ -1,3 +1,4 @@ +"use client"; import { PubLeafletBlocksMath, PubLeafletBlocksCode, @@ -12,18 +13,19 @@ import { PubLeafletBlocksBlockquote, PubLeafletBlocksBskyPost, PubLeafletBlocksIframe, + PubLeafletBlocksPage, } from "lexicons/api"; + import { blobRefToSrc } from "src/utils/blobRefToSrc"; import { TextBlock } from "./TextBlock"; import { Popover } from "components/Popover"; import { theme } from "tailwind.config"; import { ImageAltSmall } from "components/Icons/ImageAlt"; -import { codeToHtml } from "shiki"; -import Katex from "katex"; import { StaticMathBlock } from "./StaticMathBlock"; import { PubCodeBlock } from "./PubCodeBlock"; import { AppBskyFeedDefs } from "@atproto/api"; import { PubBlueskyPostBlock } from "./PublishBskyPostBlock"; +import { openPage, usePostPageUIState } from "./PostPage"; export function PostContent({ blocks, @@ -32,8 +34,10 @@ export function PostContent({ className, prerenderedCodeBlocks, bskyPostData, + pageId, }: { blocks: PubLeafletPagesLinearDocument.Block[]; + pageId?: string; did: string; preview?: boolean; className?: string; @@ -48,6 +52,7 @@ export function PostContent({ {blocks.map((b, index) => { return ( { + openPage(pageId, id); + }} + > + ITS A BLOCK + + ); + } case PubLeafletBlocksBskyPost.isMain(b.block): { let uri = b.block.postRef.uri; let post = bskyPostData.find((p) => p.uri === uri); diff --git a/app/lish/[did]/[publication]/[rkey]/PostPage.tsx b/app/lish/[did]/[publication]/[rkey]/PostPage.tsx index 8ee361cb..ef8726ee 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostPage.tsx @@ -1,5 +1,6 @@ "use client"; import { + PubLeafletDocument, PubLeafletPagesLinearDocument, PubLeafletPublication, } from "lexicons/api"; @@ -13,6 +14,29 @@ import { PostContent } from "./PostContent"; import { PostHeader } from "./PostHeader/PostHeader"; import { useIdentityData } from "components/IdentityProvider"; import { AppBskyFeedDefs } from "@atproto/api"; +import { create } from "zustand/react"; +export const usePostPageUIState = create(() => ({ + pages: [] as string[], +})); + +export const openPage = (parent: string | undefined, page: string) => + usePostPageUIState.setState((state) => { + let parentPosition = state.pages.findIndex((s) => s == parent); + return { + pages: + parentPosition === -1 + ? [page] + : [...state.pages.slice(0, parentPosition + 1), page], + }; + }); + +export const closePage = (page: string) => + usePostPageUIState.setState((state) => { + let parentPosition = state.pages.findIndex((s) => s == page); + return { + pages: state.pages.slice(0, parentPosition), + }; + }); export function PostPage({ document, @@ -37,6 +61,7 @@ export function PostPage({ }) { let { identity } = useIdentityData(); let { drawerOpen } = useInteractionState(); + let pages = usePostPageUIState((s) => s.pages); if (!document || !document.documents_in_publications[0].publications) return null; @@ -87,7 +112,7 @@ export function PostPage({ document.documents_in_publications[0]?.publications ?.identity_did ? ( Edit Post @@ -108,6 +133,35 @@ export function PostPage({ )} + {pages.map((p) => { + let record = document.data as PubLeafletDocument.Record; + let page = record.pages.find( + (page) => (page as PubLeafletPagesLinearDocument.Main).id === p, + ) as PubLeafletPagesLinearDocument.Main | undefined; + if (!page) return null; + return ( +
+ + +
+ ); + })} ); } diff --git a/components/Blocks/BlockCommands.tsx b/components/Blocks/BlockCommands.tsx index d5de9318..de82808a 100644 --- a/components/Blocks/BlockCommands.tsx +++ b/components/Blocks/BlockCommands.tsx @@ -330,7 +330,6 @@ export const blockCommands: Command[] = [ name: "New Page", icon: , type: "page", - hiddenInPublication: true, onSelect: async (rep, props, um) => { props.entityID && clearCommandSearchText(props.entityID); let entity = await createBlockWithType(rep, props, "card"); diff --git a/components/Pages/index.tsx b/components/Pages/index.tsx index bb80a878..d469d8e9 100644 --- a/components/Pages/index.tsx +++ b/components/Pages/index.tsx @@ -144,6 +144,9 @@ function Page(props: { entityID: string; first?: boolean }) { )} + {props.first && ( + + )} @@ -233,7 +236,6 @@ const DocContent = (props: { entityID: string }) => { }} /> ) : null} - {/* we handle page bg in this sepate div so that we can apply an opacity the background image diff --git a/lexicons/api/index.ts b/lexicons/api/index.ts index 6e01f58e..b51cb1a8 100644 --- a/lexicons/api/index.ts +++ b/lexicons/api/index.ts @@ -16,6 +16,7 @@ import * as PubLeafletBlocksHorizontalRule from './types/pub/leaflet/blocks/hori import * as PubLeafletBlocksIframe from './types/pub/leaflet/blocks/iframe' import * as PubLeafletBlocksImage from './types/pub/leaflet/blocks/image' import * as PubLeafletBlocksMath from './types/pub/leaflet/blocks/math' +import * as PubLeafletBlocksPage from './types/pub/leaflet/blocks/page' import * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' import * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' import * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' @@ -50,6 +51,7 @@ export * as PubLeafletBlocksHorizontalRule from './types/pub/leaflet/blocks/hori export * as PubLeafletBlocksIframe from './types/pub/leaflet/blocks/iframe' export * as PubLeafletBlocksImage from './types/pub/leaflet/blocks/image' export * as PubLeafletBlocksMath from './types/pub/leaflet/blocks/math' +export * as PubLeafletBlocksPage from './types/pub/leaflet/blocks/page' export * as PubLeafletBlocksText from './types/pub/leaflet/blocks/text' export * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' export * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' diff --git a/lexicons/api/lexicons.ts b/lexicons/api/lexicons.ts index ed01a3ee..fc0e6405 100644 --- a/lexicons/api/lexicons.ts +++ b/lexicons/api/lexicons.ts @@ -405,6 +405,21 @@ export const schemaDict = { }, }, }, + PubLeafletBlocksPage: { + lexicon: 1, + id: 'pub.leaflet.blocks.page', + defs: { + main: { + type: 'object', + required: ['id'], + properties: { + id: { + type: 'string', + }, + }, + }, + }, + }, PubLeafletBlocksText: { lexicon: 1, id: 'pub.leaflet.blocks.text', @@ -521,7 +536,11 @@ export const schemaDict = { defs: { main: { type: 'object', + required: ['blocks'], properties: { + id: { + type: 'string', + }, blocks: { type: 'array', items: { @@ -549,6 +568,7 @@ export const schemaDict = { 'lex:pub.leaflet.blocks.code', 'lex:pub.leaflet.blocks.horizontalRule', 'lex:pub.leaflet.blocks.bskyPost', + 'lex:pub.leaflet.blocks.page', ], }, alignment: { @@ -1835,6 +1855,7 @@ export const ids = { PubLeafletBlocksIframe: 'pub.leaflet.blocks.iframe', PubLeafletBlocksImage: 'pub.leaflet.blocks.image', PubLeafletBlocksMath: 'pub.leaflet.blocks.math', + PubLeafletBlocksPage: 'pub.leaflet.blocks.page', PubLeafletBlocksText: 'pub.leaflet.blocks.text', PubLeafletBlocksUnorderedList: 'pub.leaflet.blocks.unorderedList', PubLeafletBlocksWebsite: 'pub.leaflet.blocks.website', diff --git a/lexicons/api/types/pub/leaflet/pages/linearDocument.ts b/lexicons/api/types/pub/leaflet/pages/linearDocument.ts index 9c181f6e..3cc63cce 100644 --- a/lexicons/api/types/pub/leaflet/pages/linearDocument.ts +++ b/lexicons/api/types/pub/leaflet/pages/linearDocument.ts @@ -16,6 +16,7 @@ import type * as PubLeafletBlocksMath from '../blocks/math' import type * as PubLeafletBlocksCode from '../blocks/code' import type * as PubLeafletBlocksHorizontalRule from '../blocks/horizontalRule' import type * as PubLeafletBlocksBskyPost from '../blocks/bskyPost' +import type * as PubLeafletBlocksPage from '../blocks/page' const is$typed = _is$typed, validate = _validate @@ -23,7 +24,8 @@ const id = 'pub.leaflet.pages.linearDocument' export interface Main { $type?: 'pub.leaflet.pages.linearDocument' - blocks?: Block[] + id?: string + blocks: Block[] } const hashMain = 'main' @@ -50,6 +52,7 @@ export interface Block { | $Typed | $Typed | $Typed + | $Typed | { $type: string } alignment?: | 'lex:pub.leaflet.pages.linearDocument#textAlignLeft' diff --git a/lexicons/pub/leaflet/pages/linearDocument.json b/lexicons/pub/leaflet/pages/linearDocument.json index 9bb60800..86be3080 100644 --- a/lexicons/pub/leaflet/pages/linearDocument.json +++ b/lexicons/pub/leaflet/pages/linearDocument.json @@ -4,7 +4,13 @@ "defs": { "main": { "type": "object", + "required": [ + "blocks" + ], "properties": { + "id": { + "type": "string" + }, "blocks": { "type": "array", "items": { @@ -33,7 +39,8 @@ "pub.leaflet.blocks.math", "pub.leaflet.blocks.code", "pub.leaflet.blocks.horizontalRule", - "pub.leaflet.blocks.bskyPost" + "pub.leaflet.blocks.bskyPost", + "pub.leaflet.blocks.page" ] }, "alignment": { diff --git a/lexicons/src/blocks.ts b/lexicons/src/blocks.ts index 16bb6d4a..e4779091 100644 --- a/lexicons/src/blocks.ts +++ b/lexicons/src/blocks.ts @@ -19,6 +19,20 @@ export const PubLeafletBlocksText: LexiconDoc = { }, }; +export const PubLeafletBlocksPage: LexiconDoc = { + lexicon: 1, + id: "pub.leaflet.blocks.page", + defs: { + main: { + type: "object", + required: ["id"], + properties: { + id: { type: "string" }, + }, + }, + }, +}; + export const PubLeafletBlocksBskyPost: LexiconDoc = { lexicon: 1, id: "pub.leaflet.blocks.bskyPost", @@ -232,6 +246,7 @@ export const BlockLexicons = [ PubLeafletBlocksCode, PubLeafletBlocksHorizontalRule, PubLeafletBlocksBskyPost, + PubLeafletBlocksPage, ]; export const BlockUnion: LexRefUnion = { type: "union", diff --git a/lexicons/src/pages/LinearDocument.ts b/lexicons/src/pages/LinearDocument.ts index 9d699407..9c5a7625 100644 --- a/lexicons/src/pages/LinearDocument.ts +++ b/lexicons/src/pages/LinearDocument.ts @@ -7,7 +7,9 @@ export const PubLeafletPagesLinearDocument: LexiconDoc = { defs: { main: { type: "object", + required: ["blocks"], properties: { + id: { type: "string" }, blocks: { type: "array", items: { type: "ref", ref: "#block" } }, }, }, -- 2.51.2