diff --git a/app/[leaflet_id]/Leaflet.tsx b/app/[leaflet_id]/Leaflet.tsx index 9266f7ea..a98ee708 100644 --- a/app/[leaflet_id]/Leaflet.tsx +++ b/app/[leaflet_id]/Leaflet.tsx @@ -12,7 +12,6 @@ import { EntitySetProvider } from "components/EntitySetProvider"; import { AddLeafletToHomepage } from "components/utils/AddLeafletToHomepage"; import { UpdateLeafletTitle } from "components/utils/UpdateLeafletTitle"; import { useUIState } from "src/useUIState"; -import { LeafletSidebar } from "./Sidebar"; import { LeafletLayout } from "components/LeafletLayout"; export function Leaflet(props: { @@ -37,7 +36,7 @@ export function Leaflet(props: { {/* we need the padding bottom here because if we don't have it the mobile footer will cut off... the dropshadow on the page... the padding is compensated by a negative top margin in mobile footer */} - + diff --git a/app/[leaflet_id]/Sidebar.tsx b/app/[leaflet_id]/Sidebar.tsx index fb8d0105..edbdf086 100644 --- a/app/[leaflet_id]/Sidebar.tsx +++ b/app/[leaflet_id]/Sidebar.tsx @@ -12,43 +12,49 @@ import { Watermark } from "components/Watermark"; import { useUIState } from "src/useUIState"; import { BackToPubButton, PublishButton } from "./Actions"; import { useIdentityData } from "components/IdentityProvider"; +import { useReplicache } from "src/replicache"; -export function LeafletSidebar(props: { leaflet_id: string }) { +export function LeafletSidebar() { let entity_set = useEntitySetContext(); + let { rootEntity } = useReplicache(); let { data: pub } = useLeafletPublicationData(); let { identity } = useIdentityData(); return ( - - {entity_set.permissions.write && ( - - {pub?.publications && - identity?.atp_did && - pub.publications.identity_did === identity.atp_did ? ( - <> - - - - -
- - - ) : ( - <> - - - -
- - + +
+
+ {entity_set.permissions.write && ( + + {pub?.publications && + identity?.atp_did && + pub.publications.identity_did === identity.atp_did ? ( + <> + + + + +
+ + + ) : ( + <> + + + +
+ + + )} +
)} - - )} -
- +
+ +
+
); diff --git a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx index 1801882a..f8468faf 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostContent.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostContent.tsx @@ -25,7 +25,7 @@ import { StaticMathBlock } from "./StaticMathBlock"; import { PubCodeBlock } from "./PubCodeBlock"; import { AppBskyFeedDefs } from "@atproto/api"; import { PubBlueskyPostBlock } from "./PublishBskyPostBlock"; -import { openPage, usePostPageUIState } from "./PostPage"; +import { openPage } from "./PostPages"; export function PostContent({ blocks, @@ -47,7 +47,7 @@ export function PostContent({ return (
{blocks.map((b, index) => { return ( diff --git a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx index c728922a..909ed971 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx @@ -25,80 +25,72 @@ export function PostHeader(props: { if (!document?.data || !document.documents_in_publications[0].publications) return; return ( - <> - {/* */} -
-
-
- - {props.name} - - {identity && - identity.atp_did === - document.documents_in_publications[0]?.publications - .identity_did && ( - - - - )} -
-

{record.title}

- {record.description ? ( -

{record.description}

- ) : null} +
+
+
+ + {props.name} + + {identity && + identity.atp_did === + document.documents_in_publications[0]?.publications + .identity_did && ( + + + + )} +
+

{record.title}

+ {record.description ? ( +

{record.description}

+ ) : null} -
- {profile ? ( - <> - - by {profile.displayName || profile.handle} - - - ) : null} - {record.publishedAt ? ( - <> - | -

- {new Date(record.publishedAt).toLocaleDateString(undefined, { - year: "numeric", - month: "long", - day: "2-digit", - })} -

- - ) : null} - |{" "} - -
+
+ {profile ? ( + <> + + by {profile.displayName || profile.handle} + + + ) : null} + {record.publishedAt ? ( + <> + | +

+ {new Date(record.publishedAt).toLocaleDateString(undefined, { + year: "numeric", + month: "long", + day: "2-digit", + })} +

+ + ) : null} + |{" "} +
- +
); } diff --git a/app/lish/[did]/[publication]/[rkey]/PostPage.tsx b/app/lish/[did]/[publication]/[rkey]/PostPages.tsx similarity index 73% rename from app/lish/[did]/[publication]/[rkey]/PostPage.tsx rename to app/lish/[did]/[publication]/[rkey]/PostPages.tsx index 407c2588..88b4c1a9 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostPages.tsx @@ -16,7 +16,11 @@ import { useIdentityData } from "components/IdentityProvider"; import { AppBskyFeedDefs } from "@atproto/api"; import { create } from "zustand/react"; import { InteractionDrawer } from "./Interactions/InteractionDrawer"; -import { BookendSpacers, SandwichSpacer } from "components/LeafletLayout"; +import { BookendSpacer, SandwichSpacer } from "components/LeafletLayout"; +import { CSS } from "@react-spring/web"; +import { PageOptionButton } from "components/Pages/PageOptions"; +import { CloseTiny } from "components/Icons/CloseTiny"; +import { PageWrapper } from "components/Pages/Page"; export const usePostPageUIState = create(() => ({ pages: [] as string[], })); @@ -68,12 +72,14 @@ export function PostPages({ return null; let hasPageBackground = !!pubRecord.theme?.showPageBackground; + let fullPageScroll = !hasPageBackground && !drawerOpen && pages.length === 0; return ( <> - {(drawerOpen || hasPageBackground) && } + {!fullPageScroll && } closePage(page?.id!)} + hasPageBackground={hasPageBackground} + /> + } > - - ); })} - + {!fullPageScroll && } ); } -const PageWrapper = (props: { +const PageOptions = (props: { + onClick: () => void; + hasPageBackground: boolean; +}) => { + return ( +
+ + + +
+ ); +}; + +const PostPageWrapper = (props: { children: React.ReactNode; hasPageBackground: boolean; - drawerOpen: boolean | undefined; + fullPageScroll: boolean; }) => { return (
{props.children}
diff --git a/app/lish/[did]/[publication]/[rkey]/page.tsx b/app/lish/[did]/[publication]/[rkey]/page.tsx index 3f31d7fa..76edd022 100644 --- a/app/lish/[did]/[publication]/[rkey]/page.tsx +++ b/app/lish/[did]/[publication]/[rkey]/page.tsx @@ -17,7 +17,7 @@ import { } from "components/ThemeManager/PublicationThemeProvider"; import { getPostPageData } from "./getPostPageData"; import { PostPageContextProvider } from "./PostPageContext"; -import { PostPages } from "./PostPage"; +import { PostPages } from "./PostPages"; import { extractCodeBlocks } from "./extractCodeBlocks"; import { LeafletLayout } from "components/LeafletLayout"; diff --git a/app/lish/[did]/[publication]/[rkey]/useHighlight.tsx b/app/lish/[did]/[publication]/[rkey]/useHighlight.tsx index 7c8b0951..f3525ecc 100644 --- a/app/lish/[did]/[publication]/[rkey]/useHighlight.tsx +++ b/app/lish/[did]/[publication]/[rkey]/useHighlight.tsx @@ -20,7 +20,6 @@ export const useHighlight = (pos: number[]) => { let highlights = activeHighlight ? [activeHighlight] : []; let decodedQuote = quote ? decodeQuotePosition(quote as string) : null; if (decodedQuote) highlights.push(decodedQuote); - console.log(highlights); return highlights .map((quotePosition) => { if (!quotePosition) return null; diff --git a/components/LeafletLayout.tsx b/components/LeafletLayout.tsx index b051a3b1..1370df20 100644 --- a/components/LeafletLayout.tsx +++ b/components/LeafletLayout.tsx @@ -11,8 +11,7 @@ export const LeafletLayout = (props: { flex items-stretch grow`} id="page-carousel" > - {/* if you adjust this padding, remember to adjust the negative margins on page - in [rkey]/page/PostPage when card borders are hidden */} + {/* if you adjust this padding, remember to adjust the negative margins on page in components/Pages/Page.tsx in pageScrollWrapper when card borders are hidden */}
void; children?: React.ReactNode; }) => { diff --git a/components/Pages/Page.tsx b/components/Pages/Page.tsx new file mode 100644 index 00000000..5befa4e2 --- /dev/null +++ b/components/Pages/Page.tsx @@ -0,0 +1,200 @@ +"use client"; + +import React from "react"; +import { useUIState } from "src/useUIState"; + +import { elementId } from "src/utils/elementId"; + +import { useEntity, useReferenceToEntity, useReplicache } from "src/replicache"; + +import { DesktopPageFooter } from "../DesktopFooter"; +import { Canvas } from "../Canvas"; +import { Blocks } from "components/Blocks"; +import { PublicationMetadata } from "./PublicationMetadata"; +import { useCardBorderHidden } from "./useCardBorderHidden"; +import { focusPage } from "."; +import { PageOptions } from "./PageOptions"; +import { CardThemeProvider } from "components/ThemeManager/ThemeProvider"; + +export function Page(props: { + entityID: string; + first?: boolean; + fullPageScroll: boolean; +}) { + let { rep } = useReplicache(); + + let isFocused = useUIState((s) => { + let focusedElement = s.focusedEntity; + let focusedPageID = + focusedElement?.entityType === "page" + ? focusedElement.entityID + : focusedElement?.parent; + return focusedPageID === props.entityID; + }); + let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; + let cardBorderHidden = useCardBorderHidden(props.entityID); + return ( + + { + if (e.defaultPrevented) return; + if (rep) { + if (isFocused) return; + focusPage(props.entityID, rep); + } + }} + id={elementId.page(props.entityID).container} + cardBorderHidden={!!cardBorderHidden} + isFocused={isFocused} + fullPageScroll={props.fullPageScroll} + pageType={pageType} + pageOptions={ + + } + > + {props.first && ( + <> + + + )} + + + + + ); +} + +export const PageWrapper = (props: { + children: React.ReactNode; + pageOptions?: React.ReactNode; + id: string; + cardBorderHidden: boolean; + fullPageScroll: boolean; + isFocused?: boolean; + onClickAction?: (e: React.MouseEvent) => void; + pageType?: "canvas" | "doc"; +}) => { + return ( + // this div wraps the contents AND the page options. + // it needs to be its own div because this container does NOT scroll, and therefore doesn't clip the absolutely positioned pageOptions +
+ {/* + this div is the scrolling container that wraps only the contents div. + + it needs to be a separate div so that the user can scroll from anywhere on the page if there isn't a card border + */} +
+ {/* this div controls the width of the content*/} +
+ {props.children} +
+
+ {props.pageOptions} +
+ ); +}; + +const PageContent = (props: { entityID: string }) => { + let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; + if (pageType === "doc") return ; + return ; +}; + +const DocContent = (props: { entityID: string }) => { + let { rootEntity } = useReplicache(); + + let cardBorderHidden = useCardBorderHidden(props.entityID); + let rootBackgroundImage = useEntity( + rootEntity, + "theme/card-background-image", + ); + let rootBackgroundRepeat = useEntity( + rootEntity, + "theme/card-background-image-repeat", + ); + let rootBackgroundOpacity = useEntity( + rootEntity, + "theme/card-background-image-opacity", + ); + + let cardBackgroundImage = useEntity( + props.entityID, + "theme/card-background-image", + ); + + let cardBackgroundImageRepeat = useEntity( + props.entityID, + "theme/card-background-image-repeat", + ); + + let cardBackgroundImageOpacity = useEntity( + props.entityID, + "theme/card-background-image-opacity", + ); + + let backgroundImage = cardBackgroundImage || rootBackgroundImage; + let backgroundImageRepeat = cardBackgroundImage + ? cardBackgroundImageRepeat?.data?.value + : rootBackgroundRepeat?.data.value; + let backgroundImageOpacity = cardBackgroundImage + ? cardBackgroundImageOpacity?.data.value + : rootBackgroundOpacity?.data.value || 1; + + return ( + <> + {!cardBorderHidden ? ( +
+ ) : null} + + {/* we handle page bg in this sepate div so that + we can apply an opacity the background image + without affecting the opacity of the rest of the page */} + + ); +}; diff --git a/components/Pages/PageOptions.tsx b/components/Pages/PageOptions.tsx new file mode 100644 index 00000000..2903ce9d --- /dev/null +++ b/components/Pages/PageOptions.tsx @@ -0,0 +1,217 @@ +"use client"; + +import React, { JSX, useState } from "react"; +import { useUIState } from "src/useUIState"; +import { useEntitySetContext } from "../EntitySetProvider"; + +import { useReplicache } from "src/replicache"; + +import { Media } from "../Media"; +import { MenuItem, Menu } from "../Layout"; +import { PageThemeSetter } from "../ThemeManager/PageThemeSetter"; +import { PageShareMenu } from "./PageShareMenu"; +import { useUndoState } from "src/undoManager"; +import { CloseTiny } from "components/Icons/CloseTiny"; +import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; +import { PaintSmall } from "components/Icons/PaintSmall"; +import { ShareSmall } from "components/Icons/ShareSmall"; +import { useCardBorderHidden } from "./useCardBorderHidden"; +import { useLeafletPublicationData } from "components/PageSWRDataProvider"; + +export const PageOptionButton = ({ + children, + secondary, + cardBorderHidden, + className, + disabled, + ...props +}: { + children: React.ReactNode; + secondary?: boolean; + cardBorderHidden: boolean | undefined; + className?: string; + disabled?: boolean; +} & Omit) => { + return ( + + ); +}; + +export const PageOptions = (props: { + entityID: string; + first: boolean | undefined; + isFocused: boolean; +}) => { + let cardBorderHidden = useCardBorderHidden(props.entityID); + + return ( +
+ {!props.first && ( + { + useUIState.getState().closePage(props.entityID); + }} + > + + + )} + + +
+ ); +}; + +export const UndoButtons = (props: { + cardBorderHidden: boolean | undefined; +}) => { + let undoState = useUndoState(); + let { undoManager } = useReplicache(); + return ( + + {undoState.canUndo && ( +
+ undoManager.undo()} + > + + + + undoManager.undo()} + disabled={!undoState.canRedo} + > + + +
+ )} +
+ ); +}; + +export const OptionsMenu = (props: { + entityID: string; + first: boolean; + cardBorderHidden: boolean | undefined; +}) => { + let [state, setState] = useState<"normal" | "theme" | "share">("normal"); + let { permissions } = useEntitySetContext(); + if (!permissions.write) return null; + + let { data: pub, mutate } = useLeafletPublicationData(); + if (pub && props.first) return; + return ( + { + if (!open) setState("normal"); + }} + trigger={ + + + + } + > + {state === "normal" ? ( + <> + {!props.first && ( + { + e.preventDefault(); + setState("share"); + }} + > + Share Page + + )} + {!pub && ( + { + e.preventDefault(); + setState("theme"); + }} + > + Theme Page + + )} + + ) : state === "theme" ? ( + + ) : state === "share" ? ( + + ) : null} + + ); +}; + +const UndoTiny = () => { + return ( + + + + ); +}; + +const RedoTiny = () => { + return ( + + + + ); +}; diff --git a/components/Pages/PublicationMetadata.tsx b/components/Pages/PublicationMetadata.tsx index 00a981d1..d9b000d8 100644 --- a/components/Pages/PublicationMetadata.tsx +++ b/components/Pages/PublicationMetadata.tsx @@ -43,13 +43,11 @@ export const PublicationMetadata = ({ description = pub?.description || ""; } return ( -
+
{pub.publications?.name} diff --git a/components/Pages/index.tsx b/components/Pages/index.tsx index 2a3dd2fc..a5b9f974 100644 --- a/components/Pages/index.tsx +++ b/components/Pages/index.tsx @@ -1,43 +1,22 @@ "use client"; -import React, { JSX, useState } from "react"; +import React from "react"; import { useUIState } from "src/useUIState"; -import { useEntitySetContext } from "../EntitySetProvider"; import { useSearchParams } from "next/navigation"; import { focusBlock } from "src/utils/focusBlock"; import { elementId } from "src/utils/elementId"; import { Replicache } from "replicache"; -import { - Fact, - ReplicacheMutators, - useEntity, - useReferenceToEntity, - useReplicache, -} from "src/replicache"; +import { Fact, ReplicacheMutators, useEntity } from "src/replicache"; -import { Media } from "../Media"; -import { DesktopPageFooter } from "../DesktopFooter"; -import { ThemePopover } from "../ThemeManager/ThemeSetter"; -import { Canvas } from "../Canvas"; -import { Blocks } from "components/Blocks"; -import { MenuItem, Menu } from "../Layout"; import { scanIndex } from "src/replicache/utils"; -import { PageThemeSetter } from "../ThemeManager/PageThemeSetter"; import { CardThemeProvider } from "../ThemeManager/ThemeProvider"; -import { PageShareMenu } from "./PageShareMenu"; import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; -import { useUndoState } from "src/undoManager"; -import { CloseTiny } from "components/Icons/CloseTiny"; -import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; -import { PaintSmall } from "components/Icons/PaintSmall"; -import { ShareSmall } from "components/Icons/ShareSmall"; -import { PublicationMetadata } from "./PublicationMetadata"; import { useCardBorderHidden } from "./useCardBorderHidden"; -import { useLeafletPublicationData } from "components/PageSWRDataProvider"; -import { BookendSpacers, SandwichSpacer } from "components/LeafletLayout"; +import { BookendSpacer, SandwichSpacer } from "components/LeafletLayout"; import { LeafletSidebar } from "app/[leaflet_id]/Sidebar"; +import { Page } from "./Page"; export function Pages(props: { rootPage: string }) { let rootPage = useEntity(props.rootPage, "root/page")[0]; @@ -45,352 +24,42 @@ export function Pages(props: { rootPage: string }) { let params = useSearchParams(); let queryRoot = params.get("page"); let firstPage = queryRoot || rootPage?.data.value || props.rootPage; + let cardBorderHidden = useCardBorderHidden(rootPage.id); - return ( - <> - { - e.currentTarget === e.target && blurPage(); - }} - > - - -
- - - -
- {pages.map((page) => ( -
- - - -
- ))} - { - e.currentTarget === e.target && blurPage(); - }} - /> - - ); -} + let fullPageScroll = !!cardBorderHidden && pages.length === 0; -export const LeafletOptions = (props: { entityID: string }) => { return ( <> - - - ); -}; - -function Page(props: { entityID: string; first?: boolean }) { - let { rep } = useReplicache(); - let isDraft = useReferenceToEntity("mailbox/draft", props.entityID); - - let isFocused = useUIState((s) => { - let focusedElement = s.focusedEntity; - let focusedPageID = - focusedElement?.entityType === "page" - ? focusedElement.entityID - : focusedElement?.parent; - return focusedPageID === props.entityID; - }); - let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; - let cardBorderHidden = useCardBorderHidden(props.entityID); - return ( - <> - {!props.first && ( - + {!fullPageScroll && ( + { e.currentTarget === e.target && blurPage(); }} /> )} -
-
{ - if (e.defaultPrevented) return; - if (rep) { - if (isFocused) return; - focusPage(props.entityID, rep); - } - }} - id={elementId.page(props.entityID).container} - style={{ - width: pageType === "doc" ? "var(--page-width-units)" : undefined, - backgroundColor: cardBorderHidden - ? "" - : "rgba(var(--bg-page), var(--bg-page-alpha))", - }} - className={` - ${pageType === "canvas" ? "!lg:max-w-[1152px]" : "max-w-[var(--page-width-units)]"} - page - grow flex flex-col - overscroll-y-none - overflow-y-auto - ${cardBorderHidden ? "border-0 !shadow-none sm:-mt-6 sm:-mb-12 -mt-2 -mb-1 pt-3 " : "border rounded-lg"} - ${isFocused ? "shadow-md border-border" : "border-border-light"} - `} - > - - {props.first && ( - - )} - -
- -
- - ); -} - -const PageContent = (props: { entityID: string }) => { - let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; - if (pageType === "doc") return ; - return ; -}; - -const DocContent = (props: { entityID: string }) => { - let { rootEntity } = useReplicache(); - let isFocused = useUIState((s) => { - let focusedElement = s.focusedEntity; - let focusedPageID = - focusedElement?.entityType === "page" - ? focusedElement.entityID - : focusedElement?.parent; - return focusedPageID === props.entityID; - }); - let cardBorderHidden = useCardBorderHidden(props.entityID); - let rootBackgroundImage = useEntity( - rootEntity, - "theme/card-background-image", - ); - let rootBackgroundRepeat = useEntity( - rootEntity, - "theme/card-background-image-repeat", - ); - let rootBackgroundOpacity = useEntity( - rootEntity, - "theme/card-background-image-opacity", - ); - - let cardBackgroundImage = useEntity( - props.entityID, - "theme/card-background-image", - ); - - let cardBackgroundImageRepeat = useEntity( - props.entityID, - "theme/card-background-image-repeat", - ); - - let cardBackgroundImageOpacity = useEntity( - props.entityID, - "theme/card-background-image-opacity", - ); - - let backgroundImage = cardBackgroundImage || rootBackgroundImage; - let backgroundImageRepeat = cardBackgroundImage - ? cardBackgroundImageRepeat?.data?.value - : rootBackgroundRepeat?.data.value; - let backgroundImageOpacity = cardBackgroundImage - ? cardBackgroundImageOpacity?.data.value - : rootBackgroundOpacity?.data.value || 1; - - return ( - <> - {!cardBorderHidden ? ( -
+ {pages.map((page) => ( + + { + e.currentTarget === e.target && blurPage(); + }} + /> + + + ))} + {!fullPageScroll && ( + { + e.currentTarget === e.target && blurPage(); }} /> - ) : null} - - {/* we handle page bg in this sepate div so that - we can apply an opacity the background image - without affecting the opacity of the rest of the page */} - - ); -}; - -const PageOptionButton = ({ - children, - secondary, - cardBorderHidden, - className, - disabled, - ...props -}: { - children: React.ReactNode; - secondary?: boolean; - cardBorderHidden: boolean | undefined; - className?: string; - disabled?: boolean; -} & Omit) => { - return ( - - ); -}; - -const PageOptions = (props: { - entityID: string; - first: boolean | undefined; - isFocused: boolean; -}) => { - let cardBorderHidden = useCardBorderHidden(props.entityID); - - return ( -
- {!props.first && ( - { - useUIState.getState().closePage(props.entityID); - }} - > - - - )} - - -
- ); -}; - -const UndoButtons = (props: { cardBorderHidden: boolean | undefined }) => { - let undoState = useUndoState(); - let { undoManager } = useReplicache(); - return ( - - {undoState.canUndo && ( -
- undoManager.undo()} - > - - - - undoManager.undo()} - disabled={!undoState.canRedo} - > - - -
)} -
- ); -}; - -const OptionsMenu = (props: { - entityID: string; - first: boolean; - cardBorderHidden: boolean | undefined; -}) => { - let [state, setState] = useState<"normal" | "theme" | "share">("normal"); - let { permissions } = useEntitySetContext(); - if (!permissions.write) return null; - - let { data: pub, mutate } = useLeafletPublicationData(); - if (pub && props.first) return; - return ( - { - if (!open) setState("normal"); - }} - trigger={ - - - - } - > - {state === "normal" ? ( - <> - {!props.first && ( - { - e.preventDefault(); - setState("share"); - }} - > - Share Page - - )} - {!pub && ( - { - e.preventDefault(); - setState("theme"); - }} - > - Theme Page - - )} - - ) : state === "theme" ? ( - - ) : state === "share" ? ( - - ) : null} - + ); -}; +} export async function focusPage( pageID: string, @@ -458,46 +127,9 @@ export async function focusPage( }, 50); } -const blurPage = () => { +export const blurPage = () => { useUIState.setState(() => ({ focusedEntity: null, selectedBlocks: [], })); }; -const UndoTiny = () => { - return ( - - - - ); -}; - -const RedoTiny = () => { - return ( - - - - ); -};