a tool for shared writing and social publishing

Merge branch 'main' of https://github.com/hyperlink-academy/minilink into feature/small-text

+1621 -987
+13 -13
app/globals.css
··· 107 --highlight-3: 255, 205, 195; 108 109 --list-marker-width: 36px; 110 - --page-width-unitless: min(624, calc(var(--leaflet-width-unitless) - 12)); 111 - --page-width-units: min(624px, calc(100vw - 12px)); 112 113 --gripperSVG: url("/gripperPattern.svg"); 114 --gripperSVG2: url("/gripperPattern2.svg"); ··· 125 126 @media (min-width: 640px) { 127 :root { 128 --page-width-unitless: min( 129 - 624, 130 calc(var(--leaflet-width-unitless) - 128) 131 ); 132 - --page-width-units: min(624px, calc(100vw - 128px)); 133 - } 134 - } 135 - 136 - @media (min-width: 1280px) { 137 - :root { 138 - --page-width-unitless: min( 139 - 624, 140 - calc((var(--leaflet-width-unitless) / 2) - 32) 141 ); 142 - --page-width-units: min(624px, calc((100vw / 2) - 32px)); 143 } 144 } 145
··· 107 --highlight-3: 255, 205, 195; 108 109 --list-marker-width: 36px; 110 + --page-width-unitless: min( 111 + var(--page-width-setting), 112 + calc(var(--leaflet-width-unitless) - 12) 113 + ); 114 + --page-width-units: min( 115 + calc(var(--page-width-unitless) * 1px), 116 + calc(100vw - 12px) 117 + ); 118 119 --gripperSVG: url("/gripperPattern.svg"); 120 --gripperSVG2: url("/gripperPattern2.svg"); ··· 131 132 @media (min-width: 640px) { 133 :root { 134 + /*picks between max width and screen width with 64px of padding*/ 135 --page-width-unitless: min( 136 + var(--page-width-setting), 137 calc(var(--leaflet-width-unitless) - 128) 138 ); 139 + --page-width-units: min( 140 + calc(var(--page-width-unitless) * 1px), 141 + calc(100vw - 128px) 142 ); 143 } 144 } 145
+1 -1
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
··· 108 }) => { 109 return ( 110 <div 111 - className="postHeader max-w-prose w-full flex flex-col px-3 sm:px-4 sm:pt-3 pt-2 pb-5" 112 id="post-header" 113 > 114 <div className="pubInfo flex text-accent-contrast font-bold justify-between w-full">
··· 108 }) => { 109 return ( 110 <div 111 + className="postHeader w-full flex flex-col px-3 sm:px-4 sm:pt-3 pt-2 pb-5" 112 id="post-header" 113 > 114 <div className="pubInfo flex text-accent-contrast font-bold justify-between w-full">
+1 -1
app/lish/[did]/[publication]/dashboard/Actions.tsx
··· 1 "use client"; 2 3 import { NewDraftActionButton } from "./NewDraftButton"; 4 - import { PublicationSettingsButton } from "./PublicationSettings"; 5 import { ActionButton } from "components/ActionBar/ActionButton"; 6 import { ShareSmall } from "components/Icons/ShareSmall"; 7 import { Menu, MenuItem } from "components/Menu";
··· 1 "use client"; 2 3 import { NewDraftActionButton } from "./NewDraftButton"; 4 + import { PublicationSettingsButton } from "./settings/PublicationSettings"; 5 import { ActionButton } from "components/ActionBar/ActionButton"; 6 import { ShareSmall } from "components/Icons/ShareSmall"; 7 import { Menu, MenuItem } from "components/Menu";
+28 -14
app/lish/[did]/[publication]/dashboard/PublicationSettings.tsx app/lish/[did]/[publication]/dashboard/settings/PublicationSettings.tsx
··· 12 import { ButtonPrimary } from "components/Buttons"; 13 import { DotLoader } from "components/utils/DotLoader"; 14 import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 15 16 export function PublicationSettingsButton(props: { publication: string }) { 17 let isMobile = useIsMobile(); 18 - let [state, setState] = useState<"menu" | "general" | "theme">("menu"); 19 let [loading, setLoading] = useState(false); 20 21 return ( ··· 42 /> 43 ) : state === "theme" ? ( 44 <PubThemeSetter 45 backToMenu={() => setState("menu")} 46 loading={loading} 47 setLoading={setLoading} ··· 59 } 60 61 const PubSettingsMenu = (props: { 62 - state: "menu" | "general" | "theme"; 63 - setState: (s: typeof props.state) => void; 64 loading: boolean; 65 setLoading: (l: boolean) => void; 66 }) => { ··· 73 loading={props.loading} 74 setLoadingAction={props.setLoading} 75 state={"menu"} 76 - /> 77 <button 78 className={menuItemClassName} 79 type="button" ··· 81 props.setState("general"); 82 }} 83 > 84 - Publication Settings 85 <ArrowRightTiny /> 86 </button> 87 <button ··· 89 type="button" 90 onClick={() => props.setState("theme")} 91 > 92 - Publication Theme 93 <ArrowRightTiny /> 94 </button> 95 </div> 96 ); 97 }; 98 99 export const PubSettingsHeader = (props: { 100 - state: "menu" | "general" | "theme"; 101 backToMenuAction?: () => void; 102 loading: boolean; 103 setLoadingAction: (l: boolean) => void; 104 }) => { 105 return ( 106 <div className="flex justify-between font-bold text-secondary bg-border-light -mx-3 -mt-2 px-3 py-2 mb-1"> 107 - {props.state === "menu" 108 - ? "Settings" 109 - : props.state === "general" 110 - ? "General" 111 - : props.state === "theme" 112 - ? "Publication Theme" 113 - : ""} 114 {props.state !== "menu" && ( 115 <div className="flex gap-2"> 116 <button
··· 12 import { ButtonPrimary } from "components/Buttons"; 13 import { DotLoader } from "components/utils/DotLoader"; 14 import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 15 + import { PostOptions } from "./PostOptions"; 16 + 17 + type menuState = "menu" | "general" | "theme" | "post-options"; 18 19 export function PublicationSettingsButton(props: { publication: string }) { 20 let isMobile = useIsMobile(); 21 + let [state, setState] = useState<menuState>("menu"); 22 let [loading, setLoading] = useState(false); 23 24 return ( ··· 45 /> 46 ) : state === "theme" ? ( 47 <PubThemeSetter 48 + backToMenu={() => setState("menu")} 49 + loading={loading} 50 + setLoading={setLoading} 51 + /> 52 + ) : state === "post-options" ? ( 53 + <PostOptions 54 backToMenu={() => setState("menu")} 55 loading={loading} 56 setLoading={setLoading} ··· 68 } 69 70 const PubSettingsMenu = (props: { 71 + state: menuState; 72 + setState: (s: menuState) => void; 73 loading: boolean; 74 setLoading: (l: boolean) => void; 75 }) => { ··· 82 loading={props.loading} 83 setLoadingAction={props.setLoading} 84 state={"menu"} 85 + > 86 + Settings 87 + </PubSettingsHeader> 88 <button 89 className={menuItemClassName} 90 type="button" ··· 92 props.setState("general"); 93 }} 94 > 95 + General Settings 96 <ArrowRightTiny /> 97 </button> 98 <button ··· 100 type="button" 101 onClick={() => props.setState("theme")} 102 > 103 + Theme and Layout 104 <ArrowRightTiny /> 105 </button> 106 + {/*<button 107 + className={menuItemClassName} 108 + type="button" 109 + onClick={() => props.setState("post-options")} 110 + > 111 + Post Options 112 + <ArrowRightTiny /> 113 + </button>*/} 114 </div> 115 ); 116 }; 117 118 export const PubSettingsHeader = (props: { 119 + state: menuState; 120 backToMenuAction?: () => void; 121 loading: boolean; 122 setLoadingAction: (l: boolean) => void; 123 + children: React.ReactNode; 124 }) => { 125 return ( 126 <div className="flex justify-between font-bold text-secondary bg-border-light -mx-3 -mt-2 px-3 py-2 mb-1"> 127 + {props.children} 128 {props.state !== "menu" && ( 129 <div className="flex gap-2"> 130 <button
+102
app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx
···
··· 1 + import { PubLeafletPublication } from "lexicons/api"; 2 + import { usePublicationData } from "../PublicationSWRProvider"; 3 + import { PubSettingsHeader } from "./PublicationSettings"; 4 + import { useState } from "react"; 5 + import { Toggle } from "components/Toggle"; 6 + import { updatePublication } from "app/lish/createPub/updatePublication"; 7 + import { useToaster } from "components/Toast"; 8 + import { mutate } from "swr"; 9 + 10 + export const PostOptions = (props: { 11 + backToMenu: () => void; 12 + loading: boolean; 13 + setLoading: (l: boolean) => void; 14 + }) => { 15 + let { data } = usePublicationData(); 16 + 17 + let { publication: pubData } = data || {}; 18 + let record = pubData?.record as PubLeafletPublication.Record; 19 + 20 + let [showComments, setShowComments] = useState( 21 + record?.preferences?.showComments === undefined 22 + ? true 23 + : record.preferences.showComments, 24 + ); 25 + let [showMentions, setShowMentions] = useState(true); 26 + let [showPrevNext, setShowPrevNext] = useState(true); 27 + 28 + let toast = useToaster(); 29 + return ( 30 + <form 31 + onSubmit={async (e) => { 32 + // if (!pubData) return; 33 + // e.preventDefault(); 34 + // props.setLoading(true); 35 + // let data = await updatePublication({ 36 + // uri: pubData.uri, 37 + // name: nameValue, 38 + // description: descriptionValue, 39 + // iconFile: iconFile, 40 + // preferences: { 41 + // showInDiscover: showInDiscover, 42 + // showComments: showComments, 43 + // }, 44 + // }); 45 + // toast({ type: "success", content: "Posts Updated!" }); 46 + // props.setLoading(false); 47 + // mutate("publication-data"); 48 + }} 49 + className="text-primary flex flex-col" 50 + > 51 + <PubSettingsHeader 52 + loading={props.loading} 53 + setLoadingAction={props.setLoading} 54 + backToMenuAction={props.backToMenu} 55 + state={"post-options"} 56 + > 57 + Post Options 58 + </PubSettingsHeader> 59 + <h4 className="mb-1">Layout</h4> 60 + {/*<div>Max Post Width</div>*/} 61 + <Toggle 62 + toggle={showPrevNext} 63 + onToggle={() => { 64 + setShowPrevNext(!showPrevNext); 65 + }} 66 + > 67 + <div className="flex flex-col justify-start"> 68 + <div className="font-bold">Show Prev/Next Buttons</div> 69 + <div className="text-tertiary text-sm leading-tight"> 70 + Show buttons that navigate to the previous and next posts 71 + </div> 72 + </div> 73 + </Toggle> 74 + <hr className="my-2 border-border-light" /> 75 + <h4 className="mb-1">Interactions</h4> 76 + <div className="flex flex-col gap-2"> 77 + <Toggle 78 + toggle={showComments} 79 + onToggle={() => { 80 + setShowComments(!showComments); 81 + }} 82 + > 83 + <div className="font-bold">Show Comments</div> 84 + </Toggle> 85 + 86 + <Toggle 87 + toggle={showMentions} 88 + onToggle={() => { 89 + setShowMentions(!showMentions); 90 + }} 91 + > 92 + <div className="flex flex-col justify-start"> 93 + <div className="font-bold">Show Mentions</div> 94 + <div className="text-tertiary text-sm leading-tight"> 95 + Display a list of posts on Bluesky that mention your post 96 + </div> 97 + </div> 98 + </Toggle> 99 + </div> 100 + </form> 101 + ); 102 + };
+4 -2
app/lish/createPub/UpdatePubForm.tsx
··· 20 import Link from "next/link"; 21 import { Checkbox } from "components/Checkbox"; 22 import type { GetDomainConfigResponseBody } from "@vercel/sdk/esm/models/getdomainconfigop"; 23 - import { PubSettingsHeader } from "../[did]/[publication]/dashboard/PublicationSettings"; 24 25 export const EditPubForm = (props: { 26 backToMenuAction: () => void; ··· 86 setLoadingAction={props.setLoadingAction} 87 backToMenuAction={props.backToMenuAction} 88 state={"theme"} 89 - /> 90 <div className="flex flex-col gap-3 w-[1000px] max-w-full pb-2"> 91 <div className="flex items-center justify-between gap-2 "> 92 <p className="pl-0.5 pb-0.5 text-tertiary italic text-sm font-bold">
··· 20 import Link from "next/link"; 21 import { Checkbox } from "components/Checkbox"; 22 import type { GetDomainConfigResponseBody } from "@vercel/sdk/esm/models/getdomainconfigop"; 23 + import { PubSettingsHeader } from "../[did]/[publication]/dashboard/settings/PublicationSettings"; 24 25 export const EditPubForm = (props: { 26 backToMenuAction: () => void; ··· 86 setLoadingAction={props.setLoadingAction} 87 backToMenuAction={props.backToMenuAction} 88 state={"theme"} 89 + > 90 + General Settings 91 + </PubSettingsHeader> 92 <div className="flex flex-col gap-3 w-[1000px] max-w-full pb-2"> 93 <div className="flex items-center justify-between gap-2 "> 94 <p className="pl-0.5 pb-0.5 text-tertiary italic text-sm font-bold">
+3 -4
app/lish/createPub/updatePublication.ts
··· 5 PubLeafletPublication, 6 PubLeafletThemeColor, 7 } from "lexicons/api"; 8 - import { 9 - restoreOAuthSession, 10 - OAuthSessionError, 11 - } from "src/atproto-oauth"; 12 import { getIdentityData } from "actions/getIdentityData"; 13 import { supabaseServerClient } from "supabase/serverClient"; 14 import { Json } from "supabase/database.types"; ··· 184 backgroundImage?: File | null; 185 backgroundRepeat?: number | null; 186 backgroundColor: Color; 187 primary: Color; 188 pageBackground: Color; 189 showPageBackground: boolean; ··· 246 ...theme.backgroundColor, 247 } 248 : undefined, 249 primary: { 250 ...theme.primary, 251 },
··· 5 PubLeafletPublication, 6 PubLeafletThemeColor, 7 } from "lexicons/api"; 8 + import { restoreOAuthSession, OAuthSessionError } from "src/atproto-oauth"; 9 import { getIdentityData } from "actions/getIdentityData"; 10 import { supabaseServerClient } from "supabase/serverClient"; 11 import { Json } from "supabase/database.types"; ··· 181 backgroundImage?: File | null; 182 backgroundRepeat?: number | null; 183 backgroundColor: Color; 184 + pageWidth?: number; 185 primary: Color; 186 pageBackground: Color; 187 showPageBackground: boolean; ··· 244 ...theme.backgroundColor, 245 } 246 : undefined, 247 + pageWidth: theme.pageWidth, 248 primary: { 249 ...theme.primary, 250 },
+4 -4
components/ThemeManager/Pickers/ImagePicker.tsx
··· 73 }); 74 }} 75 > 76 - <div className="flex flex-col gap-2 w-full"> 77 <div className="flex gap-2"> 78 <div 79 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${repeat ? "text-[#595959]" : " text-[#969696]"}`} ··· 122 }} 123 > 124 <Slider.Track 125 - className={`${repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px]`} 126 ></Slider.Track> 127 <Slider.Thumb 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>
··· 73 }); 74 }} 75 > 76 + <div className="flex flex-col w-full"> 77 <div className="flex gap-2"> 78 <div 79 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${repeat ? "text-[#595959]" : " text-[#969696]"}`} ··· 122 }} 123 > 124 <Slider.Track 125 + className={`${repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px] my-2`} 126 ></Slider.Track> 127 <Slider.Thumb 128 className={` 129 flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 130 + ${repeat ? "bg-[#595959] shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]" : " bg-[#C3C3C3] "} 131 + `} 132 aria-label="Volume" 133 /> 134 </Slider.Root>
+14 -20
components/ThemeManager/Pickers/PageThemePickers.tsx
··· 51 <hr className="border-border-light w-full" /> 52 </> 53 )} 54 - <PageTextPicker 55 value={primaryValue} 56 setValue={set("theme/primary")} 57 openPicker={props.openPicker} ··· 663 ); 664 }; 665 666 - export const PageTextPicker = (props: { 667 openPicker: pickers; 668 setOpenPicker: (thisPicker: pickers) => void; 669 value: Color; ··· 710 711 return ( 712 <> 713 - <div className="flex gap-2 items-center"> 714 - <Toggle 715 - toggleOn={!pageBorderHidden} 716 - setToggleOn={() => { 717 - handleToggle(); 718 - }} 719 - disabledColor1="#8C8C8C" 720 - disabledColor2="#DBDBDB" 721 - /> 722 - <button 723 - className="flex gap-2 items-center" 724 - onClick={() => { 725 - handleToggle(); 726 - }} 727 - > 728 <div className="font-bold">Page Background</div> 729 <div className="italic text-[#8C8C8C]"> 730 - {pageBorderHidden ? "hidden" : ""} 731 </div> 732 - </button> 733 - </div> 734 </> 735 ); 736 };
··· 51 <hr className="border-border-light w-full" /> 52 </> 53 )} 54 + <TextPickers 55 value={primaryValue} 56 setValue={set("theme/primary")} 57 openPicker={props.openPicker} ··· 663 ); 664 }; 665 666 + export const TextPickers = (props: { 667 openPicker: pickers; 668 setOpenPicker: (thisPicker: pickers) => void; 669 value: Color; ··· 710 711 return ( 712 <> 713 + <Toggle 714 + toggle={!pageBorderHidden} 715 + onToggle={() => { 716 + handleToggle(); 717 + }} 718 + disabledColor1="#8C8C8C" 719 + disabledColor2="#DBDBDB" 720 + > 721 + <div className="flex gap-2"> 722 <div className="font-bold">Page Background</div> 723 <div className="italic text-[#8C8C8C]"> 724 + {pageBorderHidden ? "none" : ""} 725 </div> 726 + </div> 727 + </Toggle> 728 </> 729 ); 730 };
+215
components/ThemeManager/Pickers/PageWidthSetter.tsx
···
··· 1 + import * as Slider from "@radix-ui/react-slider"; 2 + import { Input } from "components/Input"; 3 + import { Radio } from "components/Checkbox"; 4 + import { useEntity, useReplicache } from "src/replicache"; 5 + import { pickers } from "../ThemeSetter"; 6 + import { useState, useEffect } from "react"; 7 + 8 + export const PageWidthSetter = (props: { 9 + entityID: string; 10 + openPicker: pickers; 11 + thisPicker: pickers; 12 + setOpenPicker: (thisPicker: pickers) => void; 13 + closePicker: () => void; 14 + }) => { 15 + let { rep } = useReplicache(); 16 + 17 + let defaultPreset = 624; 18 + let widePreset = 768; 19 + let pageWidth = useEntity(props.entityID, "theme/page-width")?.data.value; 20 + let currentValue = pageWidth || defaultPreset; 21 + let [interimValue, setInterimValue] = useState<number>(currentValue); 22 + let [selectedPreset, setSelectedPreset] = useState< 23 + "default" | "wide" | "custom" 24 + >( 25 + currentValue === defaultPreset 26 + ? "default" 27 + : currentValue === widePreset 28 + ? "wide" 29 + : "custom", 30 + ); 31 + let min = 320; 32 + let max = 1200; 33 + 34 + let open = props.openPicker == props.thisPicker; 35 + 36 + // Update interim value when current value changes 37 + useEffect(() => { 38 + setInterimValue(currentValue); 39 + }, [currentValue]); 40 + 41 + const setPageWidth = (value: number) => { 42 + rep?.mutate.assertFact({ 43 + entity: props.entityID, 44 + attribute: "theme/page-width", 45 + data: { 46 + type: "number", 47 + value: value, 48 + }, 49 + }); 50 + }; 51 + 52 + return ( 53 + <div className="pageWidthSetter flex flex-col gap-2 px-2 py-[6px] border border-[#CCCCCC] rounded-md"> 54 + <div className="flex flex-col gap-2"> 55 + <div className="flex gap-2 items-center"> 56 + <button 57 + className="font-bold text-[#000000] shrink-0 grow-0 w-full flex gap-2 items-start text-left" 58 + onClick={() => { 59 + if (props.openPicker === props.thisPicker) { 60 + props.setOpenPicker("null"); 61 + } else { 62 + props.setOpenPicker(props.thisPicker); 63 + } 64 + }} 65 + > 66 + Max Page Width 67 + <span className="flex font-normal text-[#969696]"> 68 + {currentValue}px 69 + </span> 70 + </button> 71 + </div> 72 + {open && ( 73 + <div className="flex flex-col gap-1 px-3"> 74 + <label htmlFor="default" className="w-full"> 75 + <Radio 76 + radioCheckedClassName="text-[#595959]!" 77 + radioEmptyClassName="text-[#969696]!" 78 + type="radio" 79 + id="default" 80 + name="page-width-options" 81 + value="default" 82 + checked={selectedPreset === "default"} 83 + onChange={(e) => { 84 + if (!e.currentTarget.checked) return; 85 + setSelectedPreset("default"); 86 + setPageWidth(defaultPreset); 87 + }} 88 + > 89 + <div 90 + className={`w-full cursor-pointer ${selectedPreset === "default" ? "text-[#595959]" : "text-[#969696]"}`} 91 + > 92 + default (624px) 93 + </div> 94 + </Radio> 95 + </label> 96 + <label htmlFor="wide" className="w-full"> 97 + <Radio 98 + radioCheckedClassName="text-[#595959]!" 99 + radioEmptyClassName="text-[#969696]!" 100 + type="radio" 101 + id="wide" 102 + name="page-width-options" 103 + value="wide" 104 + checked={selectedPreset === "wide"} 105 + onChange={(e) => { 106 + if (!e.currentTarget.checked) return; 107 + setSelectedPreset("wide"); 108 + setPageWidth(widePreset); 109 + }} 110 + > 111 + <div 112 + className={`w-full cursor-pointer ${selectedPreset === "wide" ? "text-[#595959]" : "text-[#969696]"}`} 113 + > 114 + wide (756px) 115 + </div> 116 + </Radio> 117 + </label> 118 + <label htmlFor="custom" className="pb-3 w-full"> 119 + <Radio 120 + type="radio" 121 + id="custom" 122 + name="page-width-options" 123 + value="custom" 124 + radioCheckedClassName="text-[#595959]!" 125 + radioEmptyClassName="text-[#969696]!" 126 + checked={selectedPreset === "custom"} 127 + onChange={(e) => { 128 + if (!e.currentTarget.checked) return; 129 + setSelectedPreset("custom"); 130 + if (selectedPreset !== "custom") { 131 + setPageWidth(currentValue); 132 + setInterimValue(currentValue); 133 + } 134 + }} 135 + > 136 + <div className="flex flex-col w-full"> 137 + <div className="flex gap-2"> 138 + <div 139 + className={`shrink-0 grow-0 w-fit z-10 cursor-pointer ${selectedPreset === "custom" ? "text-[#595959]" : "text-[#969696]"}`} 140 + > 141 + custom 142 + </div> 143 + <div 144 + className={`flex font-normal ${selectedPreset === "custom" ? "text-[#969696]" : "text-[#C3C3C3]"}`} 145 + > 146 + <Input 147 + type="number" 148 + className="w-10 text-right appearance-none bg-transparent" 149 + max={max} 150 + min={min} 151 + value={interimValue} 152 + onChange={(e) => { 153 + setInterimValue(parseInt(e.currentTarget.value)); 154 + }} 155 + onKeyDown={(e) => { 156 + if (e.key === "Enter" || e.key === "Escape") { 157 + e.preventDefault(); 158 + let clampedValue = interimValue; 159 + if (!isNaN(interimValue)) { 160 + clampedValue = Math.max( 161 + min, 162 + Math.min(max, interimValue), 163 + ); 164 + setInterimValue(clampedValue); 165 + } 166 + setPageWidth(clampedValue); 167 + } 168 + }} 169 + onBlur={() => { 170 + let clampedValue = interimValue; 171 + if (!isNaN(interimValue)) { 172 + clampedValue = Math.max( 173 + min, 174 + Math.min(max, interimValue), 175 + ); 176 + setInterimValue(clampedValue); 177 + } 178 + setPageWidth(clampedValue); 179 + }} 180 + /> 181 + px 182 + </div> 183 + </div> 184 + <Slider.Root 185 + className={`relative grow flex items-center select-none touch-none w-full h-fit px-1`} 186 + value={[interimValue]} 187 + max={max} 188 + min={min} 189 + step={16} 190 + onValueChange={(value) => { 191 + setInterimValue(value[0]); 192 + }} 193 + onValueCommit={(value) => { 194 + setPageWidth(value[0]); 195 + }} 196 + > 197 + <Slider.Track 198 + className={`${selectedPreset === "custom" ? "bg-[#595959]" : "bg-[#C3C3C3]"} relative grow rounded-full h-[3px] my-2`} 199 + /> 200 + <Slider.Thumb 201 + className={`flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 202 + ${selectedPreset === "custom" ? "bg-[#595959] shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]" : "bg-[#C3C3C3]"} 203 + `} 204 + aria-label="Max Page Width" 205 + /> 206 + </Slider.Root> 207 + </div> 208 + </Radio> 209 + </label> 210 + </div> 211 + )} 212 + </div> 213 + </div> 214 + ); 215 + };
+10 -15
components/ThemeManager/PubPickers/PubBackgroundPickers.tsx
··· 106 <hr className="border-border-light" /> 107 <div className="flex gap-2 items-center"> 108 <Toggle 109 - toggleOn={props.hasPageBackground} 110 - setToggleOn={() => { 111 props.setHasPageBackground(!props.hasPageBackground); 112 props.hasPageBackground && 113 props.openPicker === "page" && ··· 115 }} 116 disabledColor1="#8C8C8C" 117 disabledColor2="#DBDBDB" 118 - /> 119 - <button 120 - className="flex gap-2 items-center" 121 - onClick={() => { 122 - props.setHasPageBackground(!props.hasPageBackground); 123 - props.hasPageBackground && props.setOpenPicker("null"); 124 - }} 125 > 126 - <div className="font-bold">Page Background</div> 127 - <div className="italic text-[#8C8C8C]"> 128 - {props.hasPageBackground ? "" : "hidden"} 129 </div> 130 - </button> 131 </div> 132 </> 133 ); ··· 261 props.setBgImage({ ...props.bgImage, repeat: 500 }); 262 }} 263 > 264 - <div className="flex flex-col gap-2 w-full"> 265 <div className="flex gap-2"> 266 <div 267 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${props.bgImage?.repeat ? "text-[#595959]" : " text-[#969696]"}`} ··· 300 }} 301 > 302 <Slider.Track 303 - className={`${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px]`} 304 ></Slider.Track> 305 <Slider.Thumb 306 className={`
··· 106 <hr className="border-border-light" /> 107 <div className="flex gap-2 items-center"> 108 <Toggle 109 + toggle={props.hasPageBackground} 110 + onToggle={() => { 111 props.setHasPageBackground(!props.hasPageBackground); 112 props.hasPageBackground && 113 props.openPicker === "page" && ··· 115 }} 116 disabledColor1="#8C8C8C" 117 disabledColor2="#DBDBDB" 118 > 119 + <div className="flex gap-2"> 120 + <div className="font-bold">Page Background</div> 121 + <div className="italic text-[#8C8C8C]"> 122 + {props.hasPageBackground ? "" : "none"} 123 + </div> 124 </div> 125 + </Toggle> 126 </div> 127 </> 128 ); ··· 256 props.setBgImage({ ...props.bgImage, repeat: 500 }); 257 }} 258 > 259 + <div className="flex flex-col w-full"> 260 <div className="flex gap-2"> 261 <div 262 className={`shink-0 grow-0 w-fit z-10 cursor-pointer ${props.bgImage?.repeat ? "text-[#595959]" : " text-[#969696]"}`} ··· 295 }} 296 > 297 <Slider.Track 298 + className={`${props.bgImage?.repeat ? "bg-[#595959]" : " bg-[#C3C3C3]"} relative grow rounded-full h-[3px] my-2`} 299 ></Slider.Track> 300 <Slider.Thumb 301 className={`
+201
components/ThemeManager/PubPickers/PubPageWidthSetter.tsx
···
··· 1 + import * as Slider from "@radix-ui/react-slider"; 2 + import { Input } from "components/Input"; 3 + import { Radio } from "components/Checkbox"; 4 + import { useState, useEffect } from "react"; 5 + import { pickers } from "../ThemeSetter"; 6 + 7 + export const PubPageWidthSetter = (props: { 8 + pageWidth: number | undefined; 9 + setPageWidth: (value: number) => void; 10 + thisPicker: pickers; 11 + openPicker: pickers; 12 + setOpenPicker: (p: pickers) => void; 13 + }) => { 14 + let defaultPreset = 624; 15 + let widePreset = 768; 16 + 17 + let currentValue = props.pageWidth || defaultPreset; 18 + let [interimValue, setInterimValue] = useState<number>(currentValue); 19 + let [selectedPreset, setSelectedPreset] = useState< 20 + "default" | "wide" | "custom" 21 + >( 22 + currentValue === defaultPreset 23 + ? "default" 24 + : currentValue === widePreset 25 + ? "wide" 26 + : "custom", 27 + ); 28 + let min = 320; 29 + let max = 1200; 30 + 31 + // Update interim value when current value changes 32 + useEffect(() => { 33 + setInterimValue(currentValue); 34 + }, [currentValue]); 35 + 36 + const setPageWidth = (value: number) => { 37 + props.setPageWidth(value); 38 + }; 39 + 40 + let open = props.openPicker == props.thisPicker; 41 + 42 + return ( 43 + <div className="pageWidthSetter flex flex-col gap-2 px-2 py-[6px] border border-[#CCCCCC] rounded-md bg-white"> 44 + <button 45 + type="button" 46 + className="font-bold text-[#000000] shrink-0 grow-0 w-full flex gap-2 text-left items-center" 47 + onClick={() => { 48 + if (!open) { 49 + props.setOpenPicker(props.thisPicker); 50 + } else { 51 + props.setOpenPicker("null"); 52 + } 53 + }} 54 + > 55 + Max Page Width 56 + <div className="flex font-normal text-[#969696]">{currentValue}px</div> 57 + </button> 58 + 59 + {open && ( 60 + <div className="flex flex-col gap-1 px-3"> 61 + <label htmlFor="pub-default" className="w-full"> 62 + <Radio 63 + radioCheckedClassName="text-[#595959]!" 64 + radioEmptyClassName="text-[#969696]!" 65 + type="radio" 66 + id="pub-default" 67 + name="pub-page-width-options" 68 + value="default" 69 + checked={selectedPreset === "default"} 70 + onChange={(e) => { 71 + if (!e.currentTarget.checked) return; 72 + setSelectedPreset("default"); 73 + setPageWidth(defaultPreset); 74 + }} 75 + > 76 + <div 77 + className={`w-full cursor-pointer ${selectedPreset === "default" ? "text-[#595959]" : "text-[#969696]"}`} 78 + > 79 + default (624px) 80 + </div> 81 + </Radio> 82 + </label> 83 + <label htmlFor="pub-wide" className="w-full"> 84 + <Radio 85 + radioCheckedClassName="text-[#595959]!" 86 + radioEmptyClassName="text-[#969696]!" 87 + type="radio" 88 + id="pub-wide" 89 + name="pub-page-width-options" 90 + value="wide" 91 + checked={selectedPreset === "wide"} 92 + onChange={(e) => { 93 + if (!e.currentTarget.checked) return; 94 + setSelectedPreset("wide"); 95 + setPageWidth(widePreset); 96 + }} 97 + > 98 + <div 99 + className={`w-full cursor-pointer ${selectedPreset === "wide" ? "text-[#595959]" : "text-[#969696]"}`} 100 + > 101 + wide (756px) 102 + </div> 103 + </Radio> 104 + </label> 105 + <label htmlFor="pub-custom" className="pb-3 w-full"> 106 + <Radio 107 + type="radio" 108 + id="pub-custom" 109 + name="pub-page-width-options" 110 + value="custom" 111 + radioCheckedClassName="text-[#595959]!" 112 + radioEmptyClassName="text-[#969696]!" 113 + checked={selectedPreset === "custom"} 114 + onChange={(e) => { 115 + if (!e.currentTarget.checked) return; 116 + setSelectedPreset("custom"); 117 + if (selectedPreset !== "custom") { 118 + setPageWidth(currentValue); 119 + setInterimValue(currentValue); 120 + } 121 + }} 122 + > 123 + <div className="flex flex-col w-full"> 124 + <div className="flex gap-2"> 125 + <div 126 + className={`shrink-0 grow-0 w-fit z-10 cursor-pointer ${selectedPreset === "custom" ? "text-[#595959]" : "text-[#969696]"}`} 127 + > 128 + custom 129 + </div> 130 + <div 131 + className={`flex font-normal ${selectedPreset === "custom" ? "text-[#969696]" : "text-[#C3C3C3]"}`} 132 + > 133 + <Input 134 + type="number" 135 + className="w-10 text-right appearance-none bg-transparent" 136 + max={max} 137 + min={min} 138 + value={interimValue} 139 + onChange={(e) => { 140 + setInterimValue(parseInt(e.currentTarget.value)); 141 + }} 142 + onKeyDown={(e) => { 143 + if (e.key === "Enter" || e.key === "Escape") { 144 + e.preventDefault(); 145 + let clampedValue = interimValue; 146 + if (!isNaN(interimValue)) { 147 + clampedValue = Math.max( 148 + min, 149 + Math.min(max, interimValue), 150 + ); 151 + setInterimValue(clampedValue); 152 + } 153 + setPageWidth(clampedValue); 154 + } 155 + }} 156 + onBlur={() => { 157 + let clampedValue = interimValue; 158 + if (!isNaN(interimValue)) { 159 + clampedValue = Math.max( 160 + min, 161 + Math.min(max, interimValue), 162 + ); 163 + setInterimValue(clampedValue); 164 + } 165 + setPageWidth(clampedValue); 166 + }} 167 + /> 168 + px 169 + </div> 170 + </div> 171 + <Slider.Root 172 + className={`relative grow flex items-center select-none touch-none w-full h-fit px-1`} 173 + value={[interimValue]} 174 + max={max} 175 + min={min} 176 + step={16} 177 + onValueChange={(value) => { 178 + setInterimValue(value[0]); 179 + }} 180 + onValueCommit={(value) => { 181 + setPageWidth(value[0]); 182 + }} 183 + > 184 + <Slider.Track 185 + className={`${selectedPreset === "custom" ? "bg-[#595959]" : "bg-[#C3C3C3]"} relative grow rounded-full h-[3px] my-2`} 186 + /> 187 + <Slider.Thumb 188 + className={`flex w-4 h-4 rounded-full border-2 border-white cursor-pointer 189 + ${selectedPreset === "custom" ? "bg-[#595959] shadow-[0_0_0_1px_#8C8C8C,inset_0_0_0_1px_#8C8C8C]" : "bg-[#C3C3C3]"} 190 + `} 191 + aria-label="Max Page Width" 192 + /> 193 + </Slider.Root> 194 + </div> 195 + </Radio> 196 + </label> 197 + </div> 198 + )} 199 + </div> 200 + ); 201 + };
+2 -2
components/ThemeManager/PubPickers/PubTextPickers.tsx
··· 1 import { pickers } from "../ThemeSetter"; 2 - import { PageTextPicker } from "../Pickers/PageThemePickers"; 3 import { Color } from "react-aria-components"; 4 5 export const PagePickers = (props: { ··· 20 : "transparent", 21 }} 22 > 23 - <PageTextPicker 24 value={props.primary} 25 setValue={props.setPrimary} 26 openPicker={props.openPicker}
··· 1 import { pickers } from "../ThemeSetter"; 2 + import { TextPickers } from "../Pickers/PageThemePickers"; 3 import { Color } from "react-aria-components"; 4 5 export const PagePickers = (props: { ··· 20 : "transparent", 21 }} 22 > 23 + <TextPickers 24 value={props.primary} 25 setValue={props.setPrimary} 26 openPicker={props.openPicker}
+19 -6
components/ThemeManager/PubThemeSetter.tsx
··· 15 import { BackgroundPicker } from "./PubPickers/PubBackgroundPickers"; 16 import { PubAccentPickers } from "./PubPickers/PubAcccentPickers"; 17 import { Separator } from "components/Layout"; 18 - import { PubSettingsHeader } from "app/lish/[did]/[publication]/dashboard/PublicationSettings"; 19 import { ColorToRGB, ColorToRGBA } from "./colorToLexicons"; 20 import { useToaster } from "components/Toast"; 21 import { OAuthErrorMessage, isOAuthSessionError } from "components/OAuthError"; 22 23 export type ImageState = { 24 src: string; ··· 56 } 57 : null, 58 ); 59 - 60 let pubBGImage = image?.src || null; 61 let leafletBGRepeat = image?.repeat || null; 62 let toaster = useToaster(); ··· 78 : ColorToRGB(localPubTheme.bgLeaflet), 79 backgroundRepeat: image?.repeat, 80 backgroundImage: image ? image.file : null, 81 primary: ColorToRGB(localPubTheme.primary), 82 accentBackground: ColorToRGB(localPubTheme.accent1), 83 accentText: ColorToRGB(localPubTheme.accent2), ··· 116 setLoadingAction={props.setLoading} 117 backToMenuAction={props.backToMenu} 118 state={"theme"} 119 - /> 120 </form> 121 122 - <div className="themeSetterContent flex flex-col w-full overflow-y-scroll -mb-2 "> 123 - <div className="themeBGLeaflet flex"> 124 <div 125 - className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `} 126 > 127 <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md text-[#595959] bg-white"> 128 <BackgroundPicker
··· 15 import { BackgroundPicker } from "./PubPickers/PubBackgroundPickers"; 16 import { PubAccentPickers } from "./PubPickers/PubAcccentPickers"; 17 import { Separator } from "components/Layout"; 18 + import { PubSettingsHeader } from "app/lish/[did]/[publication]/dashboard/settings/PublicationSettings"; 19 import { ColorToRGB, ColorToRGBA } from "./colorToLexicons"; 20 import { useToaster } from "components/Toast"; 21 import { OAuthErrorMessage, isOAuthSessionError } from "components/OAuthError"; 22 + import { PubPageWidthSetter } from "./PubPickers/PubPageWidthSetter"; 23 24 export type ImageState = { 25 src: string; ··· 57 } 58 : null, 59 ); 60 + let [pageWidth, setPageWidth] = useState<number>( 61 + record?.theme?.pageWidth || 624, 62 + ); 63 let pubBGImage = image?.src || null; 64 let leafletBGRepeat = image?.repeat || null; 65 let toaster = useToaster(); ··· 81 : ColorToRGB(localPubTheme.bgLeaflet), 82 backgroundRepeat: image?.repeat, 83 backgroundImage: image ? image.file : null, 84 + pageWidth: pageWidth, 85 primary: ColorToRGB(localPubTheme.primary), 86 accentBackground: ColorToRGB(localPubTheme.accent1), 87 accentText: ColorToRGB(localPubTheme.accent2), ··· 120 setLoadingAction={props.setLoading} 121 backToMenuAction={props.backToMenu} 122 state={"theme"} 123 + > 124 + Theme and Layout 125 + </PubSettingsHeader> 126 </form> 127 128 + <div className="themeSetterContent flex flex-col w-full overflow-y-scroll -mb-2 mt-2 "> 129 + <PubPageWidthSetter 130 + pageWidth={pageWidth} 131 + setPageWidth={setPageWidth} 132 + thisPicker="page-width" 133 + openPicker={openPicker} 134 + setOpenPicker={setOpenPicker} 135 + /> 136 + <div className="themeBGLeaflet flex flex-col"> 137 <div 138 + className={`themeBgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `} 139 > 140 <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md text-[#595959] bg-white"> 141 <BackgroundPicker
+6 -3
components/ThemeManager/PublicationThemeProvider.tsx
··· 102 pub_creator: string; 103 isStandalone?: boolean; 104 }) { 105 - let colors = usePubTheme(props.theme, props.isStandalone); 106 - let cardBorderHidden = !colors.showPageBackground; 107 let hasBackgroundImage = !!props.theme?.backgroundImage?.image?.ref; 108 return ( 109 <CardBorderHiddenContext.Provider value={cardBorderHidden}> 110 <BaseThemeProvider 111 local={props.local} 112 - {...colors} 113 hasBackgroundImage={hasBackgroundImage} 114 > 115 {props.children} ··· 132 bgPage = bgLeaflet; 133 } 134 let showPageBackground = theme?.showPageBackground; 135 136 let primary = useColor(theme, "primary"); 137 ··· 152 highlight2, 153 highlight3, 154 showPageBackground, 155 }; 156 }; 157
··· 102 pub_creator: string; 103 isStandalone?: boolean; 104 }) { 105 + let theme = usePubTheme(props.theme, props.isStandalone); 106 + let cardBorderHidden = !theme.showPageBackground; 107 let hasBackgroundImage = !!props.theme?.backgroundImage?.image?.ref; 108 + 109 return ( 110 <CardBorderHiddenContext.Provider value={cardBorderHidden}> 111 <BaseThemeProvider 112 local={props.local} 113 + {...theme} 114 hasBackgroundImage={hasBackgroundImage} 115 > 116 {props.children} ··· 133 bgPage = bgLeaflet; 134 } 135 let showPageBackground = theme?.showPageBackground; 136 + let pageWidth = theme?.pageWidth; 137 138 let primary = useColor(theme, "primary"); 139 ··· 154 highlight2, 155 highlight3, 156 showPageBackground, 157 + pageWidth, 158 }; 159 }; 160
+15
components/ThemeManager/ThemeProvider.tsx
··· 76 let accent1 = useColorAttribute(props.entityID, "theme/accent-background"); 77 let accent2 = useColorAttribute(props.entityID, "theme/accent-text"); 78 79 return ( 80 <CardBorderHiddenContext.Provider value={!!cardBorderHiddenValue}> 81 <BaseThemeProvider ··· 89 accent1={accent1} 90 accent2={accent2} 91 showPageBackground={showPageBackground} 92 hasBackgroundImage={hasBackgroundImage} 93 > 94 {props.children} ··· 109 highlight2, 110 highlight3, 111 showPageBackground, 112 hasBackgroundImage, 113 children, 114 }: { ··· 123 highlight1?: string; 124 highlight2: AriaColor; 125 highlight3: AriaColor; 126 children: React.ReactNode; 127 }) => { 128 // When showPageBackground is false and there's no background image, ··· 205 "--accent-1-is-contrast", 206 accentContrast === accent1 ? "1" : "0", 207 ); 208 }, [ 209 local, 210 bgLeaflet, ··· 216 accent1, 217 accent2, 218 accentContrast, 219 ]); 220 return ( 221 <div ··· 235 : "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)", 236 "--highlight-2": colorToString(highlight2, "rgb"), 237 "--highlight-3": colorToString(highlight3, "rgb"), 238 } as CSSProperties 239 } 240 >
··· 76 let accent1 = useColorAttribute(props.entityID, "theme/accent-background"); 77 let accent2 = useColorAttribute(props.entityID, "theme/accent-text"); 78 79 + let pageWidth = useEntity(props.entityID, "theme/page-width"); 80 + 81 return ( 82 <CardBorderHiddenContext.Provider value={!!cardBorderHiddenValue}> 83 <BaseThemeProvider ··· 91 accent1={accent1} 92 accent2={accent2} 93 showPageBackground={showPageBackground} 94 + pageWidth={pageWidth?.data.value} 95 hasBackgroundImage={hasBackgroundImage} 96 > 97 {props.children} ··· 112 highlight2, 113 highlight3, 114 showPageBackground, 115 + pageWidth, 116 hasBackgroundImage, 117 children, 118 }: { ··· 127 highlight1?: string; 128 highlight2: AriaColor; 129 highlight3: AriaColor; 130 + pageWidth?: number; 131 children: React.ReactNode; 132 }) => { 133 // When showPageBackground is false and there's no background image, ··· 210 "--accent-1-is-contrast", 211 accentContrast === accent1 ? "1" : "0", 212 ); 213 + 214 + // Set page width CSS variable 215 + el?.style.setProperty( 216 + "--page-width-setting", 217 + (pageWidth || 624).toString(), 218 + ); 219 }, [ 220 local, 221 bgLeaflet, ··· 227 accent1, 228 accent2, 229 accentContrast, 230 + pageWidth, 231 ]); 232 return ( 233 <div ··· 247 : "color-mix(in oklab, rgb(var(--accent-contrast)), rgb(var(--bg-page)) 75%)", 248 "--highlight-2": colorToString(highlight2, "rgb"), 249 "--highlight-3": colorToString(highlight3, "rgb"), 250 + "--page-width-setting": pageWidth || 624, 251 + "--page-width-unitless": pageWidth || 624, 252 + "--page-width-units": `min(${pageWidth || 624}px, calc(100vw - 12px))`, 253 } as CSSProperties 254 } 255 >
+17 -12
components/ThemeManager/ThemeSetter.tsx
··· 8 LeafletBackgroundPicker, 9 PageThemePickers, 10 } from "./Pickers/PageThemePickers"; 11 import { useMemo, useState } from "react"; 12 import { ReplicacheMutators, useEntity, useReplicache } from "src/replicache"; 13 import { Replicache } from "replicache"; ··· 33 | "highlight-1" 34 | "highlight-2" 35 | "highlight-3" 36 - | "page-background-image"; 37 38 export function setColorAttribute( 39 rep: Replicache<ReplicacheMutators> | null, ··· 73 return ( 74 <> 75 <Popover 76 - className="w-80 bg-white" 77 arrowFill="#FFFFFF" 78 asChild 79 side={isMobile ? "top" : "right"} ··· 112 if (pub?.publications) return null; 113 return ( 114 <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> 115 <div className="themeBGLeaflet flex"> 116 <div className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `}> 117 <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md"> ··· 191 return ( 192 <div className="flex gap-2 items-start mt-0.5"> 193 <Toggle 194 - toggleOn={!!checked?.data.value} 195 - setToggleOn={() => { 196 handleToggle(); 197 }} 198 disabledColor1="#8C8C8C" 199 disabledColor2="#DBDBDB" 200 - /> 201 - <button 202 - className="flex gap-2 items-center -mt-0.5" 203 - onClick={() => { 204 - handleToggle(); 205 - }} 206 > 207 - <div className="flex flex-col gap-0 items-start"> 208 <div className="font-bold">Show Leaflet Watermark</div> 209 <div className="text-sm text-[#969696]">Help us spread the word!</div> 210 </div> 211 - </button> 212 </div> 213 ); 214 }
··· 8 LeafletBackgroundPicker, 9 PageThemePickers, 10 } from "./Pickers/PageThemePickers"; 11 + import { PageWidthSetter } from "./Pickers/PageWidthSetter"; 12 import { useMemo, useState } from "react"; 13 import { ReplicacheMutators, useEntity, useReplicache } from "src/replicache"; 14 import { Replicache } from "replicache"; ··· 34 | "highlight-1" 35 | "highlight-2" 36 | "highlight-3" 37 + | "page-background-image" 38 + | "page-width"; 39 40 export function setColorAttribute( 41 rep: Replicache<ReplicacheMutators> | null, ··· 75 return ( 76 <> 77 <Popover 78 + className="w-80 bg-white py-3!" 79 arrowFill="#FFFFFF" 80 asChild 81 side={isMobile ? "top" : "right"} ··· 114 if (pub?.publications) return null; 115 return ( 116 <div className="themeSetterContent flex flex-col w-full overflow-y-scroll no-scrollbar"> 117 + {!props.home && ( 118 + <PageWidthSetter 119 + entityID={props.entityID} 120 + thisPicker={"page-width"} 121 + openPicker={openPicker} 122 + setOpenPicker={setOpenPicker} 123 + closePicker={() => setOpenPicker("null")} 124 + /> 125 + )} 126 <div className="themeBGLeaflet flex"> 127 <div className={`bgPicker flex flex-col gap-0 -mb-[6px] z-10 w-full `}> 128 <div className="bgPickerBody w-full flex flex-col gap-2 p-2 mt-1 border border-[#CCCCCC] rounded-md"> ··· 202 return ( 203 <div className="flex gap-2 items-start mt-0.5"> 204 <Toggle 205 + toggle={!!checked?.data.value} 206 + onToggle={() => { 207 handleToggle(); 208 }} 209 disabledColor1="#8C8C8C" 210 disabledColor2="#DBDBDB" 211 > 212 + <div className="flex flex-col gap-0 items-start "> 213 <div className="font-bold">Show Leaflet Watermark</div> 214 <div className="text-sm text-[#969696]">Help us spread the word!</div> 215 </div> 216 + </Toggle> 217 </div> 218 ); 219 }
+32 -20
components/Toggle.tsx
··· 1 import { theme } from "tailwind.config"; 2 3 export const Toggle = (props: { 4 - toggleOn: boolean; 5 - setToggleOn: (s: boolean) => void; 6 disabledColor1?: string; 7 disabledColor2?: string; 8 }) => { 9 return ( 10 <button 11 - className="toggle selected-outline transparent-outline flex items-center h-[20px] w-6 rounded-md border-border" 12 - style={{ 13 - border: props.toggleOn 14 - ? "1px solid " + theme.colors["accent-2"] 15 - : "1px solid " + props.disabledColor2 || theme.colors["border-light"], 16 - justifyContent: props.toggleOn ? "flex-end" : "flex-start", 17 - background: props.toggleOn 18 - ? theme.colors["accent-1"] 19 - : props.disabledColor1 || theme.colors["tertiary"], 20 }} 21 - onClick={() => props.setToggleOn(!props.toggleOn)} 22 > 23 - <div 24 - className="h-[14px] w-[10px] m-0.5 rounded-[2px]" 25 - style={{ 26 - background: props.toggleOn 27 - ? theme.colors["accent-2"] 28 - : props.disabledColor2 || theme.colors["border-light"], 29 - }} 30 - /> 31 </button> 32 ); 33 };
··· 1 import { theme } from "tailwind.config"; 2 3 export const Toggle = (props: { 4 + toggle: boolean; 5 + onToggle: () => void; 6 disabledColor1?: string; 7 disabledColor2?: string; 8 + children: React.ReactNode; 9 }) => { 10 return ( 11 <button 12 + type="button" 13 + className="toggle flex gap-2 items-start justify-start text-left" 14 + onClick={() => { 15 + props.onToggle(); 16 }} 17 > 18 + <div className="h-6 flex place-items-center"> 19 + <div 20 + className="selected-outline transparent-outline flex items-center h-[20px] w-6 rounded-md border-border" 21 + style={{ 22 + border: props.toggle 23 + ? "1px solid " + theme.colors["accent-2"] 24 + : "1px solid " + props.disabledColor2 || 25 + theme.colors["border-light"], 26 + justifyContent: props.toggle ? "flex-end" : "flex-start", 27 + background: props.toggle 28 + ? theme.colors["accent-1"] 29 + : props.disabledColor1 || theme.colors["tertiary"], 30 + }} 31 + > 32 + <div 33 + className="h-[14px] w-[10px] m-0.5 rounded-[2px]" 34 + style={{ 35 + background: props.toggle 36 + ? theme.colors["accent-2"] 37 + : props.disabledColor2 || theme.colors["border-light"], 38 + }} 39 + /> 40 + </div> 41 + </div> 42 + {props.children} 43 </button> 44 ); 45 };
+831 -826
lexicons/api/lexicons.ts
··· 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 }, ··· 71 }, 72 ComAtprotoLabelDefs: { 73 lexicon: 1, 74 - id: 'com.atproto.label.defs', 75 defs: { 76 label: { 77 - type: 'object', 78 description: 79 - 'Metadata tag on an atproto resource (eg, repo or record).', 80 - required: ['src', 'uri', 'val', 'cts'], 81 properties: { 82 ver: { 83 - type: 'integer', 84 - description: 'The AT Protocol version of the label object.', 85 }, 86 src: { 87 - type: 'string', 88 - format: 'did', 89 - description: 'DID of the actor who created this label.', 90 }, 91 uri: { 92 - type: 'string', 93 - format: 'uri', 94 description: 95 - 'AT URI of the record, repository (account), or other resource that this label applies to.', 96 }, 97 cid: { 98 - type: 'string', 99 - format: 'cid', 100 description: 101 "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 102 }, 103 val: { 104 - type: 'string', 105 maxLength: 128, 106 description: 107 - 'The short string name of the value or type of this label.', 108 }, 109 neg: { 110 - type: 'boolean', 111 description: 112 - 'If true, this is a negation label, overwriting a previous label.', 113 }, 114 cts: { 115 - type: 'string', 116 - format: 'datetime', 117 - description: 'Timestamp when this label was created.', 118 }, 119 exp: { 120 - type: 'string', 121 - format: 'datetime', 122 description: 123 - 'Timestamp at which this label expires (no longer applies).', 124 }, 125 sig: { 126 - type: 'bytes', 127 - description: 'Signature of dag-cbor encoded label.', 128 }, 129 }, 130 }, 131 selfLabels: { 132 - type: 'object', 133 description: 134 - 'Metadata tags on an atproto record, published by the author within the record.', 135 - required: ['values'], 136 properties: { 137 values: { 138 - type: 'array', 139 items: { 140 - type: 'ref', 141 - ref: 'lex:com.atproto.label.defs#selfLabel', 142 }, 143 maxLength: 10, 144 }, 145 }, 146 }, 147 selfLabel: { 148 - type: 'object', 149 description: 150 - 'Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.', 151 - required: ['val'], 152 properties: { 153 val: { 154 - type: 'string', 155 maxLength: 128, 156 description: 157 - 'The short string name of the value or type of this label.', 158 }, 159 }, 160 }, 161 labelValueDefinition: { 162 - type: 'object', 163 description: 164 - 'Declares a label value and its expected interpretations and behaviors.', 165 - required: ['identifier', 'severity', 'blurs', 'locales'], 166 properties: { 167 identifier: { 168 - type: 'string', 169 description: 170 "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", 171 maxLength: 100, 172 maxGraphemes: 100, 173 }, 174 severity: { 175 - type: 'string', 176 description: 177 "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", 178 - knownValues: ['inform', 'alert', 'none'], 179 }, 180 blurs: { 181 - type: 'string', 182 description: 183 "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", 184 - knownValues: ['content', 'media', 'none'], 185 }, 186 defaultSetting: { 187 - type: 'string', 188 - description: 'The default setting for this label.', 189 - knownValues: ['ignore', 'warn', 'hide'], 190 - default: 'warn', 191 }, 192 adultOnly: { 193 - type: 'boolean', 194 description: 195 - 'Does the user need to have adult content enabled in order to configure this label?', 196 }, 197 locales: { 198 - type: 'array', 199 items: { 200 - type: 'ref', 201 - ref: 'lex:com.atproto.label.defs#labelValueDefinitionStrings', 202 }, 203 }, 204 }, 205 }, 206 labelValueDefinitionStrings: { 207 - type: 'object', 208 description: 209 - 'Strings which describe the label in the UI, localized into a specific language.', 210 - required: ['lang', 'name', 'description'], 211 properties: { 212 lang: { 213 - type: 'string', 214 description: 215 - 'The code of the language these strings are written in.', 216 - format: 'language', 217 }, 218 name: { 219 - type: 'string', 220 - description: 'A short human-readable name for the label.', 221 maxGraphemes: 64, 222 maxLength: 640, 223 }, 224 description: { 225 - type: 'string', 226 description: 227 - 'A longer description of what the label means and why it might be applied.', 228 maxGraphemes: 10000, 229 maxLength: 100000, 230 }, 231 }, 232 }, 233 labelValue: { 234 - type: 'string', 235 knownValues: [ 236 - '!hide', 237 - '!no-promote', 238 - '!warn', 239 - '!no-unauthenticated', 240 - 'dmca-violation', 241 - 'doxxing', 242 - 'porn', 243 - 'sexual', 244 - 'nudity', 245 - 'nsfl', 246 - 'gore', 247 ], 248 }, 249 }, 250 }, 251 ComAtprotoRepoApplyWrites: { 252 lexicon: 1, 253 - id: 'com.atproto.repo.applyWrites', 254 defs: { 255 main: { 256 - type: 'procedure', 257 description: 258 - 'Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.', 259 input: { 260 - encoding: 'application/json', 261 schema: { 262 - type: 'object', 263 - required: ['repo', 'writes'], 264 properties: { 265 repo: { 266 - type: 'string', 267 - format: 'at-identifier', 268 description: 269 - 'The handle or DID of the repo (aka, current account).', 270 }, 271 validate: { 272 - type: 'boolean', 273 description: 274 "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.", 275 }, 276 writes: { 277 - type: 'array', 278 items: { 279 - type: 'union', 280 refs: [ 281 - 'lex:com.atproto.repo.applyWrites#create', 282 - 'lex:com.atproto.repo.applyWrites#update', 283 - 'lex:com.atproto.repo.applyWrites#delete', 284 ], 285 closed: true, 286 }, 287 }, 288 swapCommit: { 289 - type: 'string', 290 description: 291 - 'If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.', 292 - format: 'cid', 293 }, 294 }, 295 }, 296 }, 297 output: { 298 - encoding: 'application/json', 299 schema: { 300 - type: 'object', 301 required: [], 302 properties: { 303 commit: { 304 - type: 'ref', 305 - ref: 'lex:com.atproto.repo.defs#commitMeta', 306 }, 307 results: { 308 - type: 'array', 309 items: { 310 - type: 'union', 311 refs: [ 312 - 'lex:com.atproto.repo.applyWrites#createResult', 313 - 'lex:com.atproto.repo.applyWrites#updateResult', 314 - 'lex:com.atproto.repo.applyWrites#deleteResult', 315 ], 316 closed: true, 317 }, ··· 321 }, 322 errors: [ 323 { 324 - name: 'InvalidSwap', 325 description: 326 "Indicates that the 'swapCommit' parameter did not match current commit.", 327 }, 328 ], 329 }, 330 create: { 331 - type: 'object', 332 - description: 'Operation which creates a new record.', 333 - required: ['collection', 'value'], 334 properties: { 335 collection: { 336 - type: 'string', 337 - format: 'nsid', 338 }, 339 rkey: { 340 - type: 'string', 341 maxLength: 512, 342 - format: 'record-key', 343 description: 344 - 'NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.', 345 }, 346 value: { 347 - type: 'unknown', 348 }, 349 }, 350 }, 351 update: { 352 - type: 'object', 353 - description: 'Operation which updates an existing record.', 354 - required: ['collection', 'rkey', 'value'], 355 properties: { 356 collection: { 357 - type: 'string', 358 - format: 'nsid', 359 }, 360 rkey: { 361 - type: 'string', 362 - format: 'record-key', 363 }, 364 value: { 365 - type: 'unknown', 366 }, 367 }, 368 }, 369 delete: { 370 - type: 'object', 371 - description: 'Operation which deletes an existing record.', 372 - required: ['collection', 'rkey'], 373 properties: { 374 collection: { 375 - type: 'string', 376 - format: 'nsid', 377 }, 378 rkey: { 379 - type: 'string', 380 - format: 'record-key', 381 }, 382 }, 383 }, 384 createResult: { 385 - type: 'object', 386 - required: ['uri', 'cid'], 387 properties: { 388 uri: { 389 - type: 'string', 390 - format: 'at-uri', 391 }, 392 cid: { 393 - type: 'string', 394 - format: 'cid', 395 }, 396 validationStatus: { 397 - type: 'string', 398 - knownValues: ['valid', 'unknown'], 399 }, 400 }, 401 }, 402 updateResult: { 403 - type: 'object', 404 - required: ['uri', 'cid'], 405 properties: { 406 uri: { 407 - type: 'string', 408 - format: 'at-uri', 409 }, 410 cid: { 411 - type: 'string', 412 - format: 'cid', 413 }, 414 validationStatus: { 415 - type: 'string', 416 - knownValues: ['valid', 'unknown'], 417 }, 418 }, 419 }, 420 deleteResult: { 421 - type: 'object', 422 required: [], 423 properties: {}, 424 }, ··· 426 }, 427 ComAtprotoRepoCreateRecord: { 428 lexicon: 1, 429 - id: 'com.atproto.repo.createRecord', 430 defs: { 431 main: { 432 - type: 'procedure', 433 description: 434 - 'Create a single new repository record. Requires auth, implemented by PDS.', 435 input: { 436 - encoding: 'application/json', 437 schema: { 438 - type: 'object', 439 - required: ['repo', 'collection', 'record'], 440 properties: { 441 repo: { 442 - type: 'string', 443 - format: 'at-identifier', 444 description: 445 - 'The handle or DID of the repo (aka, current account).', 446 }, 447 collection: { 448 - type: 'string', 449 - format: 'nsid', 450 - description: 'The NSID of the record collection.', 451 }, 452 rkey: { 453 - type: 'string', 454 - format: 'record-key', 455 - description: 'The Record Key.', 456 maxLength: 512, 457 }, 458 validate: { 459 - type: 'boolean', 460 description: 461 "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 462 }, 463 record: { 464 - type: 'unknown', 465 - description: 'The record itself. Must contain a $type field.', 466 }, 467 swapCommit: { 468 - type: 'string', 469 - format: 'cid', 470 description: 471 - 'Compare and swap with the previous commit by CID.', 472 }, 473 }, 474 }, 475 }, 476 output: { 477 - encoding: 'application/json', 478 schema: { 479 - type: 'object', 480 - required: ['uri', 'cid'], 481 properties: { 482 uri: { 483 - type: 'string', 484 - format: 'at-uri', 485 }, 486 cid: { 487 - type: 'string', 488 - format: 'cid', 489 }, 490 commit: { 491 - type: 'ref', 492 - ref: 'lex:com.atproto.repo.defs#commitMeta', 493 }, 494 validationStatus: { 495 - type: 'string', 496 - knownValues: ['valid', 'unknown'], 497 }, 498 }, 499 }, 500 }, 501 errors: [ 502 { 503 - name: 'InvalidSwap', 504 description: 505 "Indicates that 'swapCommit' didn't match current repo commit.", 506 }, ··· 510 }, 511 ComAtprotoRepoDefs: { 512 lexicon: 1, 513 - id: 'com.atproto.repo.defs', 514 defs: { 515 commitMeta: { 516 - type: 'object', 517 - required: ['cid', 'rev'], 518 properties: { 519 cid: { 520 - type: 'string', 521 - format: 'cid', 522 }, 523 rev: { 524 - type: 'string', 525 - format: 'tid', 526 }, 527 }, 528 }, ··· 530 }, 531 ComAtprotoRepoDeleteRecord: { 532 lexicon: 1, 533 - id: 'com.atproto.repo.deleteRecord', 534 defs: { 535 main: { 536 - type: 'procedure', 537 description: 538 "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.", 539 input: { 540 - encoding: 'application/json', 541 schema: { 542 - type: 'object', 543 - required: ['repo', 'collection', 'rkey'], 544 properties: { 545 repo: { 546 - type: 'string', 547 - format: 'at-identifier', 548 description: 549 - 'The handle or DID of the repo (aka, current account).', 550 }, 551 collection: { 552 - type: 'string', 553 - format: 'nsid', 554 - description: 'The NSID of the record collection.', 555 }, 556 rkey: { 557 - type: 'string', 558 - format: 'record-key', 559 - description: 'The Record Key.', 560 }, 561 swapRecord: { 562 - type: 'string', 563 - format: 'cid', 564 description: 565 - 'Compare and swap with the previous record by CID.', 566 }, 567 swapCommit: { 568 - type: 'string', 569 - format: 'cid', 570 description: 571 - 'Compare and swap with the previous commit by CID.', 572 }, 573 }, 574 }, 575 }, 576 output: { 577 - encoding: 'application/json', 578 schema: { 579 - type: 'object', 580 properties: { 581 commit: { 582 - type: 'ref', 583 - ref: 'lex:com.atproto.repo.defs#commitMeta', 584 }, 585 }, 586 }, 587 }, 588 errors: [ 589 { 590 - name: 'InvalidSwap', 591 }, 592 ], 593 }, ··· 595 }, 596 ComAtprotoRepoDescribeRepo: { 597 lexicon: 1, 598 - id: 'com.atproto.repo.describeRepo', 599 defs: { 600 main: { 601 - type: 'query', 602 description: 603 - 'Get information about an account and repository, including the list of collections. Does not require auth.', 604 parameters: { 605 - type: 'params', 606 - required: ['repo'], 607 properties: { 608 repo: { 609 - type: 'string', 610 - format: 'at-identifier', 611 - description: 'The handle or DID of the repo.', 612 }, 613 }, 614 }, 615 output: { 616 - encoding: 'application/json', 617 schema: { 618 - type: 'object', 619 required: [ 620 - 'handle', 621 - 'did', 622 - 'didDoc', 623 - 'collections', 624 - 'handleIsCorrect', 625 ], 626 properties: { 627 handle: { 628 - type: 'string', 629 - format: 'handle', 630 }, 631 did: { 632 - type: 'string', 633 - format: 'did', 634 }, 635 didDoc: { 636 - type: 'unknown', 637 - description: 'The complete DID document for this account.', 638 }, 639 collections: { 640 - type: 'array', 641 description: 642 - 'List of all the collections (NSIDs) for which this repo contains at least one record.', 643 items: { 644 - type: 'string', 645 - format: 'nsid', 646 }, 647 }, 648 handleIsCorrect: { 649 - type: 'boolean', 650 description: 651 - 'Indicates if handle is currently valid (resolves bi-directionally)', 652 }, 653 }, 654 }, ··· 658 }, 659 ComAtprotoRepoGetRecord: { 660 lexicon: 1, 661 - id: 'com.atproto.repo.getRecord', 662 defs: { 663 main: { 664 - type: 'query', 665 description: 666 - 'Get a single record from a repository. Does not require auth.', 667 parameters: { 668 - type: 'params', 669 - required: ['repo', 'collection', 'rkey'], 670 properties: { 671 repo: { 672 - type: 'string', 673 - format: 'at-identifier', 674 - description: 'The handle or DID of the repo.', 675 }, 676 collection: { 677 - type: 'string', 678 - format: 'nsid', 679 - description: 'The NSID of the record collection.', 680 }, 681 rkey: { 682 - type: 'string', 683 - description: 'The Record Key.', 684 - format: 'record-key', 685 }, 686 cid: { 687 - type: 'string', 688 - format: 'cid', 689 description: 690 - 'The CID of the version of the record. If not specified, then return the most recent version.', 691 }, 692 }, 693 }, 694 output: { 695 - encoding: 'application/json', 696 schema: { 697 - type: 'object', 698 - required: ['uri', 'value'], 699 properties: { 700 uri: { 701 - type: 'string', 702 - format: 'at-uri', 703 }, 704 cid: { 705 - type: 'string', 706 - format: 'cid', 707 }, 708 value: { 709 - type: 'unknown', 710 }, 711 }, 712 }, 713 }, 714 errors: [ 715 { 716 - name: 'RecordNotFound', 717 }, 718 ], 719 }, ··· 721 }, 722 ComAtprotoRepoImportRepo: { 723 lexicon: 1, 724 - id: 'com.atproto.repo.importRepo', 725 defs: { 726 main: { 727 - type: 'procedure', 728 description: 729 - 'Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.', 730 input: { 731 - encoding: 'application/vnd.ipld.car', 732 }, 733 }, 734 }, 735 }, 736 ComAtprotoRepoListMissingBlobs: { 737 lexicon: 1, 738 - id: 'com.atproto.repo.listMissingBlobs', 739 defs: { 740 main: { 741 - type: 'query', 742 description: 743 - 'Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.', 744 parameters: { 745 - type: 'params', 746 properties: { 747 limit: { 748 - type: 'integer', 749 minimum: 1, 750 maximum: 1000, 751 default: 500, 752 }, 753 cursor: { 754 - type: 'string', 755 }, 756 }, 757 }, 758 output: { 759 - encoding: 'application/json', 760 schema: { 761 - type: 'object', 762 - required: ['blobs'], 763 properties: { 764 cursor: { 765 - type: 'string', 766 }, 767 blobs: { 768 - type: 'array', 769 items: { 770 - type: 'ref', 771 - ref: 'lex:com.atproto.repo.listMissingBlobs#recordBlob', 772 }, 773 }, 774 }, ··· 776 }, 777 }, 778 recordBlob: { 779 - type: 'object', 780 - required: ['cid', 'recordUri'], 781 properties: { 782 cid: { 783 - type: 'string', 784 - format: 'cid', 785 }, 786 recordUri: { 787 - type: 'string', 788 - format: 'at-uri', 789 }, 790 }, 791 }, ··· 793 }, 794 ComAtprotoRepoListRecords: { 795 lexicon: 1, 796 - id: 'com.atproto.repo.listRecords', 797 defs: { 798 main: { 799 - type: 'query', 800 description: 801 - 'List a range of records in a repository, matching a specific collection. Does not require auth.', 802 parameters: { 803 - type: 'params', 804 - required: ['repo', 'collection'], 805 properties: { 806 repo: { 807 - type: 'string', 808 - format: 'at-identifier', 809 - description: 'The handle or DID of the repo.', 810 }, 811 collection: { 812 - type: 'string', 813 - format: 'nsid', 814 - description: 'The NSID of the record type.', 815 }, 816 limit: { 817 - type: 'integer', 818 minimum: 1, 819 maximum: 100, 820 default: 50, 821 - description: 'The number of records to return.', 822 }, 823 cursor: { 824 - type: 'string', 825 }, 826 rkeyStart: { 827 - type: 'string', 828 description: 829 - 'DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)', 830 }, 831 rkeyEnd: { 832 - type: 'string', 833 description: 834 - 'DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)', 835 }, 836 reverse: { 837 - type: 'boolean', 838 - description: 'Flag to reverse the order of the returned records.', 839 }, 840 }, 841 }, 842 output: { 843 - encoding: 'application/json', 844 schema: { 845 - type: 'object', 846 - required: ['records'], 847 properties: { 848 cursor: { 849 - type: 'string', 850 }, 851 records: { 852 - type: 'array', 853 items: { 854 - type: 'ref', 855 - ref: 'lex:com.atproto.repo.listRecords#record', 856 }, 857 }, 858 }, ··· 860 }, 861 }, 862 record: { 863 - type: 'object', 864 - required: ['uri', 'cid', 'value'], 865 properties: { 866 uri: { 867 - type: 'string', 868 - format: 'at-uri', 869 }, 870 cid: { 871 - type: 'string', 872 - format: 'cid', 873 }, 874 value: { 875 - type: 'unknown', 876 }, 877 }, 878 }, ··· 880 }, 881 ComAtprotoRepoPutRecord: { 882 lexicon: 1, 883 - id: 'com.atproto.repo.putRecord', 884 defs: { 885 main: { 886 - type: 'procedure', 887 description: 888 - 'Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.', 889 input: { 890 - encoding: 'application/json', 891 schema: { 892 - type: 'object', 893 - required: ['repo', 'collection', 'rkey', 'record'], 894 - nullable: ['swapRecord'], 895 properties: { 896 repo: { 897 - type: 'string', 898 - format: 'at-identifier', 899 description: 900 - 'The handle or DID of the repo (aka, current account).', 901 }, 902 collection: { 903 - type: 'string', 904 - format: 'nsid', 905 - description: 'The NSID of the record collection.', 906 }, 907 rkey: { 908 - type: 'string', 909 - format: 'record-key', 910 - description: 'The Record Key.', 911 maxLength: 512, 912 }, 913 validate: { 914 - type: 'boolean', 915 description: 916 "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 917 }, 918 record: { 919 - type: 'unknown', 920 - description: 'The record to write.', 921 }, 922 swapRecord: { 923 - type: 'string', 924 - format: 'cid', 925 description: 926 - 'Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation', 927 }, 928 swapCommit: { 929 - type: 'string', 930 - format: 'cid', 931 description: 932 - 'Compare and swap with the previous commit by CID.', 933 }, 934 }, 935 }, 936 }, 937 output: { 938 - encoding: 'application/json', 939 schema: { 940 - type: 'object', 941 - required: ['uri', 'cid'], 942 properties: { 943 uri: { 944 - type: 'string', 945 - format: 'at-uri', 946 }, 947 cid: { 948 - type: 'string', 949 - format: 'cid', 950 }, 951 commit: { 952 - type: 'ref', 953 - ref: 'lex:com.atproto.repo.defs#commitMeta', 954 }, 955 validationStatus: { 956 - type: 'string', 957 - knownValues: ['valid', 'unknown'], 958 }, 959 }, 960 }, 961 }, 962 errors: [ 963 { 964 - name: 'InvalidSwap', 965 }, 966 ], 967 }, ··· 969 }, 970 ComAtprotoRepoStrongRef: { 971 lexicon: 1, 972 - id: 'com.atproto.repo.strongRef', 973 - description: 'A URI with a content-hash fingerprint.', 974 defs: { 975 main: { 976 - type: 'object', 977 - required: ['uri', 'cid'], 978 properties: { 979 uri: { 980 - type: 'string', 981 - format: 'at-uri', 982 }, 983 cid: { 984 - type: 'string', 985 - format: 'cid', 986 }, 987 }, 988 }, ··· 990 }, 991 ComAtprotoRepoUploadBlob: { 992 lexicon: 1, 993 - id: 'com.atproto.repo.uploadBlob', 994 defs: { 995 main: { 996 - type: 'procedure', 997 description: 998 - 'Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.', 999 input: { 1000 - encoding: '*/*', 1001 }, 1002 output: { 1003 - encoding: 'application/json', 1004 schema: { 1005 - type: 'object', 1006 - required: ['blob'], 1007 properties: { 1008 blob: { 1009 - type: 'blob', 1010 }, 1011 }, 1012 }, ··· 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 }, ··· 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 }, ··· 1054 }, 1055 PubLeafletBlocksButton: { 1056 lexicon: 1, 1057 - id: 'pub.leaflet.blocks.button', 1058 defs: { 1059 main: { 1060 - type: 'object', 1061 - required: ['text', 'url'], 1062 properties: { 1063 text: { 1064 - type: 'string', 1065 }, 1066 url: { 1067 - type: 'string', 1068 - format: 'uri', 1069 }, 1070 }, 1071 }, ··· 1073 }, 1074 PubLeafletBlocksCode: { 1075 lexicon: 1, 1076 - id: 'pub.leaflet.blocks.code', 1077 defs: { 1078 main: { 1079 - type: 'object', 1080 - required: ['plaintext'], 1081 properties: { 1082 plaintext: { 1083 - type: 'string', 1084 }, 1085 language: { 1086 - type: 'string', 1087 }, 1088 syntaxHighlightingTheme: { 1089 - type: 'string', 1090 }, 1091 }, 1092 }, ··· 1094 }, 1095 PubLeafletBlocksHeader: { 1096 lexicon: 1, 1097 - id: 'pub.leaflet.blocks.header', 1098 defs: { 1099 main: { 1100 - type: 'object', 1101 - required: ['plaintext'], 1102 properties: { 1103 level: { 1104 - type: 'integer', 1105 minimum: 1, 1106 maximum: 6, 1107 }, 1108 plaintext: { 1109 - type: 'string', 1110 }, 1111 facets: { 1112 - type: 'array', 1113 items: { 1114 - type: 'ref', 1115 - ref: 'lex:pub.leaflet.richtext.facet', 1116 }, 1117 }, 1118 }, ··· 1121 }, 1122 PubLeafletBlocksHorizontalRule: { 1123 lexicon: 1, 1124 - id: 'pub.leaflet.blocks.horizontalRule', 1125 defs: { 1126 main: { 1127 - type: 'object', 1128 required: [], 1129 properties: {}, 1130 }, ··· 1132 }, 1133 PubLeafletBlocksIframe: { 1134 lexicon: 1, 1135 - id: 'pub.leaflet.blocks.iframe', 1136 defs: { 1137 main: { 1138 - type: 'object', 1139 - required: ['url'], 1140 properties: { 1141 url: { 1142 - type: 'string', 1143 - format: 'uri', 1144 }, 1145 height: { 1146 - type: 'integer', 1147 minimum: 16, 1148 maximum: 1600, 1149 }, ··· 1153 }, 1154 PubLeafletBlocksImage: { 1155 lexicon: 1, 1156 - id: 'pub.leaflet.blocks.image', 1157 defs: { 1158 main: { 1159 - type: 'object', 1160 - required: ['image', 'aspectRatio'], 1161 properties: { 1162 image: { 1163 - type: 'blob', 1164 - accept: ['image/*'], 1165 maxSize: 1000000, 1166 }, 1167 alt: { 1168 - type: 'string', 1169 description: 1170 - 'Alt text description of the image, for accessibility.', 1171 }, 1172 aspectRatio: { 1173 - type: 'ref', 1174 - ref: 'lex:pub.leaflet.blocks.image#aspectRatio', 1175 }, 1176 }, 1177 }, 1178 aspectRatio: { 1179 - type: 'object', 1180 - required: ['width', 'height'], 1181 properties: { 1182 width: { 1183 - type: 'integer', 1184 }, 1185 height: { 1186 - type: 'integer', 1187 }, 1188 }, 1189 }, ··· 1191 }, 1192 PubLeafletBlocksMath: { 1193 lexicon: 1, 1194 - id: 'pub.leaflet.blocks.math', 1195 defs: { 1196 main: { 1197 - type: 'object', 1198 - required: ['tex'], 1199 properties: { 1200 tex: { 1201 - type: 'string', 1202 }, 1203 }, 1204 }, ··· 1206 }, 1207 PubLeafletBlocksPage: { 1208 lexicon: 1, 1209 - id: 'pub.leaflet.blocks.page', 1210 defs: { 1211 main: { 1212 - type: 'object', 1213 - required: ['id'], 1214 properties: { 1215 id: { 1216 - type: 'string', 1217 }, 1218 }, 1219 }, ··· 1221 }, 1222 PubLeafletBlocksPoll: { 1223 lexicon: 1, 1224 - id: 'pub.leaflet.blocks.poll', 1225 defs: { 1226 main: { 1227 - type: 'object', 1228 - required: ['pollRef'], 1229 properties: { 1230 pollRef: { 1231 - type: 'ref', 1232 - ref: 'lex:com.atproto.repo.strongRef', 1233 }, 1234 }, 1235 }, ··· 1237 }, 1238 PubLeafletBlocksText: { 1239 lexicon: 1, 1240 - id: 'pub.leaflet.blocks.text', 1241 defs: { 1242 main: { 1243 - type: 'object', 1244 - required: ['plaintext'], 1245 properties: { 1246 plaintext: { 1247 - type: 'string', 1248 }, 1249 facets: { 1250 - type: 'array', 1251 items: { 1252 - type: 'ref', 1253 - ref: 'lex:pub.leaflet.richtext.facet', 1254 }, 1255 }, 1256 }, ··· 1259 }, 1260 PubLeafletBlocksUnorderedList: { 1261 lexicon: 1, 1262 - id: 'pub.leaflet.blocks.unorderedList', 1263 defs: { 1264 main: { 1265 - type: 'object', 1266 - required: ['children'], 1267 properties: { 1268 children: { 1269 - type: 'array', 1270 items: { 1271 - type: 'ref', 1272 - ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 1273 }, 1274 }, 1275 }, 1276 }, 1277 listItem: { 1278 - type: 'object', 1279 - required: ['content'], 1280 properties: { 1281 content: { 1282 - type: 'union', 1283 refs: [ 1284 - 'lex:pub.leaflet.blocks.text', 1285 - 'lex:pub.leaflet.blocks.header', 1286 - 'lex:pub.leaflet.blocks.image', 1287 ], 1288 }, 1289 children: { 1290 - type: 'array', 1291 items: { 1292 - type: 'ref', 1293 - ref: 'lex:pub.leaflet.blocks.unorderedList#listItem', 1294 }, 1295 }, 1296 }, ··· 1299 }, 1300 PubLeafletBlocksWebsite: { 1301 lexicon: 1, 1302 - id: 'pub.leaflet.blocks.website', 1303 defs: { 1304 main: { 1305 - type: 'object', 1306 - required: ['src'], 1307 properties: { 1308 previewImage: { 1309 - type: 'blob', 1310 - accept: ['image/*'], 1311 maxSize: 1000000, 1312 }, 1313 title: { 1314 - type: 'string', 1315 }, 1316 description: { 1317 - type: 'string', 1318 }, 1319 src: { 1320 - type: 'string', 1321 - format: 'uri', 1322 }, 1323 }, 1324 }, ··· 1326 }, 1327 PubLeafletComment: { 1328 lexicon: 1, 1329 - id: 'pub.leaflet.comment', 1330 revision: 1, 1331 - description: 'A lexicon for comments on documents', 1332 defs: { 1333 main: { 1334 - type: 'record', 1335 - key: 'tid', 1336 - description: 'Record containing a comment', 1337 record: { 1338 - type: 'object', 1339 - required: ['subject', 'plaintext', 'createdAt'], 1340 properties: { 1341 subject: { 1342 - type: 'string', 1343 - format: 'at-uri', 1344 }, 1345 createdAt: { 1346 - type: 'string', 1347 - format: 'datetime', 1348 }, 1349 reply: { 1350 - type: 'ref', 1351 - ref: 'lex:pub.leaflet.comment#replyRef', 1352 }, 1353 plaintext: { 1354 - type: 'string', 1355 }, 1356 facets: { 1357 - type: 'array', 1358 items: { 1359 - type: 'ref', 1360 - ref: 'lex:pub.leaflet.richtext.facet', 1361 }, 1362 }, 1363 onPage: { 1364 - type: 'string', 1365 }, 1366 attachment: { 1367 - type: 'union', 1368 - refs: ['lex:pub.leaflet.comment#linearDocumentQuote'], 1369 }, 1370 }, 1371 }, 1372 }, 1373 linearDocumentQuote: { 1374 - type: 'object', 1375 - required: ['document', 'quote'], 1376 properties: { 1377 document: { 1378 - type: 'string', 1379 - format: 'at-uri', 1380 }, 1381 quote: { 1382 - type: 'ref', 1383 - ref: 'lex:pub.leaflet.pages.linearDocument#quote', 1384 }, 1385 }, 1386 }, 1387 replyRef: { 1388 - type: 'object', 1389 - required: ['parent'], 1390 properties: { 1391 parent: { 1392 - type: 'string', 1393 - format: 'at-uri', 1394 }, 1395 }, 1396 }, ··· 1398 }, 1399 PubLeafletDocument: { 1400 lexicon: 1, 1401 - id: 'pub.leaflet.document', 1402 revision: 1, 1403 - description: 'A lexicon for long form rich media documents', 1404 defs: { 1405 main: { 1406 - type: 'record', 1407 - key: 'tid', 1408 - description: 'Record containing a document', 1409 record: { 1410 - type: 'object', 1411 - required: ['pages', 'author', 'title'], 1412 properties: { 1413 title: { 1414 - type: 'string', 1415 maxLength: 1280, 1416 maxGraphemes: 128, 1417 }, 1418 postRef: { 1419 - type: 'ref', 1420 - ref: 'lex:com.atproto.repo.strongRef', 1421 }, 1422 description: { 1423 - type: 'string', 1424 maxLength: 3000, 1425 maxGraphemes: 300, 1426 }, 1427 publishedAt: { 1428 - type: 'string', 1429 - format: 'datetime', 1430 }, 1431 publication: { 1432 - type: 'string', 1433 - format: 'at-uri', 1434 }, 1435 author: { 1436 - type: 'string', 1437 - format: 'at-identifier', 1438 }, 1439 theme: { 1440 - type: 'ref', 1441 - ref: 'lex:pub.leaflet.publication#theme', 1442 }, 1443 tags: { 1444 - type: 'array', 1445 items: { 1446 - type: 'string', 1447 maxLength: 50, 1448 }, 1449 }, 1450 coverImage: { 1451 - type: 'blob', 1452 - accept: ['image/png', 'image/jpeg', 'image/webp'], 1453 maxSize: 1000000, 1454 }, 1455 pages: { 1456 - type: 'array', 1457 items: { 1458 - type: 'union', 1459 refs: [ 1460 - 'lex:pub.leaflet.pages.linearDocument', 1461 - 'lex:pub.leaflet.pages.canvas', 1462 ], 1463 }, 1464 }, ··· 1469 }, 1470 PubLeafletGraphSubscription: { 1471 lexicon: 1, 1472 - id: 'pub.leaflet.graph.subscription', 1473 defs: { 1474 main: { 1475 - type: 'record', 1476 - key: 'tid', 1477 - description: 'Record declaring a subscription to a publication', 1478 record: { 1479 - type: 'object', 1480 - required: ['publication'], 1481 properties: { 1482 publication: { 1483 - type: 'string', 1484 - format: 'at-uri', 1485 }, 1486 }, 1487 }, ··· 1490 }, 1491 PubLeafletPagesCanvas: { 1492 lexicon: 1, 1493 - id: 'pub.leaflet.pages.canvas', 1494 defs: { 1495 main: { 1496 - type: 'object', 1497 - required: ['blocks'], 1498 properties: { 1499 id: { 1500 - type: 'string', 1501 }, 1502 blocks: { 1503 - type: 'array', 1504 items: { 1505 - type: 'ref', 1506 - ref: 'lex:pub.leaflet.pages.canvas#block', 1507 }, 1508 }, 1509 }, 1510 }, 1511 block: { 1512 - type: 'object', 1513 - required: ['block', 'x', 'y', 'width'], 1514 properties: { 1515 block: { 1516 - type: 'union', 1517 refs: [ 1518 - 'lex:pub.leaflet.blocks.iframe', 1519 - 'lex:pub.leaflet.blocks.text', 1520 - 'lex:pub.leaflet.blocks.blockquote', 1521 - 'lex:pub.leaflet.blocks.header', 1522 - 'lex:pub.leaflet.blocks.image', 1523 - 'lex:pub.leaflet.blocks.unorderedList', 1524 - 'lex:pub.leaflet.blocks.website', 1525 - 'lex:pub.leaflet.blocks.math', 1526 - 'lex:pub.leaflet.blocks.code', 1527 - 'lex:pub.leaflet.blocks.horizontalRule', 1528 - 'lex:pub.leaflet.blocks.bskyPost', 1529 - 'lex:pub.leaflet.blocks.page', 1530 - 'lex:pub.leaflet.blocks.poll', 1531 - 'lex:pub.leaflet.blocks.button', 1532 ], 1533 }, 1534 x: { 1535 - type: 'integer', 1536 }, 1537 y: { 1538 - type: 'integer', 1539 }, 1540 width: { 1541 - type: 'integer', 1542 }, 1543 height: { 1544 - type: 'integer', 1545 }, 1546 rotation: { 1547 - type: 'integer', 1548 - description: 'The rotation of the block in degrees', 1549 }, 1550 }, 1551 }, 1552 textAlignLeft: { 1553 - type: 'token', 1554 }, 1555 textAlignCenter: { 1556 - type: 'token', 1557 }, 1558 textAlignRight: { 1559 - type: 'token', 1560 }, 1561 quote: { 1562 - type: 'object', 1563 - required: ['start', 'end'], 1564 properties: { 1565 start: { 1566 - type: 'ref', 1567 - ref: 'lex:pub.leaflet.pages.canvas#position', 1568 }, 1569 end: { 1570 - type: 'ref', 1571 - ref: 'lex:pub.leaflet.pages.canvas#position', 1572 }, 1573 }, 1574 }, 1575 position: { 1576 - type: 'object', 1577 - required: ['block', 'offset'], 1578 properties: { 1579 block: { 1580 - type: 'array', 1581 items: { 1582 - type: 'integer', 1583 }, 1584 }, 1585 offset: { 1586 - type: 'integer', 1587 }, 1588 }, 1589 }, ··· 1591 }, 1592 PubLeafletPagesLinearDocument: { 1593 lexicon: 1, 1594 - id: 'pub.leaflet.pages.linearDocument', 1595 defs: { 1596 main: { 1597 - type: 'object', 1598 - required: ['blocks'], 1599 properties: { 1600 id: { 1601 - type: 'string', 1602 }, 1603 blocks: { 1604 - type: 'array', 1605 items: { 1606 - type: 'ref', 1607 - ref: 'lex:pub.leaflet.pages.linearDocument#block', 1608 }, 1609 }, 1610 }, 1611 }, 1612 block: { 1613 - type: 'object', 1614 - required: ['block'], 1615 properties: { 1616 block: { 1617 - type: 'union', 1618 refs: [ 1619 - 'lex:pub.leaflet.blocks.iframe', 1620 - 'lex:pub.leaflet.blocks.text', 1621 - 'lex:pub.leaflet.blocks.blockquote', 1622 - 'lex:pub.leaflet.blocks.header', 1623 - 'lex:pub.leaflet.blocks.image', 1624 - 'lex:pub.leaflet.blocks.unorderedList', 1625 - 'lex:pub.leaflet.blocks.website', 1626 - 'lex:pub.leaflet.blocks.math', 1627 - 'lex:pub.leaflet.blocks.code', 1628 - 'lex:pub.leaflet.blocks.horizontalRule', 1629 - 'lex:pub.leaflet.blocks.bskyPost', 1630 - 'lex:pub.leaflet.blocks.page', 1631 - 'lex:pub.leaflet.blocks.poll', 1632 - 'lex:pub.leaflet.blocks.button', 1633 ], 1634 }, 1635 alignment: { 1636 - type: 'string', 1637 knownValues: [ 1638 - 'lex:pub.leaflet.pages.linearDocument#textAlignLeft', 1639 - 'lex:pub.leaflet.pages.linearDocument#textAlignCenter', 1640 - 'lex:pub.leaflet.pages.linearDocument#textAlignRight', 1641 - 'lex:pub.leaflet.pages.linearDocument#textAlignJustify', 1642 ], 1643 }, 1644 }, 1645 }, 1646 textAlignLeft: { 1647 - type: 'token', 1648 }, 1649 textAlignCenter: { 1650 - type: 'token', 1651 }, 1652 textAlignRight: { 1653 - type: 'token', 1654 }, 1655 textAlignJustify: { 1656 - type: 'token', 1657 }, 1658 quote: { 1659 - type: 'object', 1660 - required: ['start', 'end'], 1661 properties: { 1662 start: { 1663 - type: 'ref', 1664 - ref: 'lex:pub.leaflet.pages.linearDocument#position', 1665 }, 1666 end: { 1667 - type: 'ref', 1668 - ref: 'lex:pub.leaflet.pages.linearDocument#position', 1669 }, 1670 }, 1671 }, 1672 position: { 1673 - type: 'object', 1674 - required: ['block', 'offset'], 1675 properties: { 1676 block: { 1677 - type: 'array', 1678 items: { 1679 - type: 'integer', 1680 }, 1681 }, 1682 offset: { 1683 - type: 'integer', 1684 }, 1685 }, 1686 }, ··· 1688 }, 1689 PubLeafletPollDefinition: { 1690 lexicon: 1, 1691 - id: 'pub.leaflet.poll.definition', 1692 defs: { 1693 main: { 1694 - type: 'record', 1695 - key: 'tid', 1696 - description: 'Record declaring a poll', 1697 record: { 1698 - type: 'object', 1699 - required: ['name', 'options'], 1700 properties: { 1701 name: { 1702 - type: 'string', 1703 maxLength: 500, 1704 maxGraphemes: 100, 1705 }, 1706 options: { 1707 - type: 'array', 1708 items: { 1709 - type: 'ref', 1710 - ref: 'lex:pub.leaflet.poll.definition#option', 1711 }, 1712 }, 1713 endDate: { 1714 - type: 'string', 1715 - format: 'datetime', 1716 }, 1717 }, 1718 }, 1719 }, 1720 option: { 1721 - type: 'object', 1722 properties: { 1723 text: { 1724 - type: 'string', 1725 maxLength: 500, 1726 maxGraphemes: 50, 1727 }, ··· 1731 }, 1732 PubLeafletPollVote: { 1733 lexicon: 1, 1734 - id: 'pub.leaflet.poll.vote', 1735 defs: { 1736 main: { 1737 - type: 'record', 1738 - key: 'tid', 1739 - description: 'Record declaring a vote on a poll', 1740 record: { 1741 - type: 'object', 1742 - required: ['poll', 'option'], 1743 properties: { 1744 poll: { 1745 - type: 'ref', 1746 - ref: 'lex:com.atproto.repo.strongRef', 1747 }, 1748 option: { 1749 - type: 'array', 1750 items: { 1751 - type: 'string', 1752 }, 1753 }, 1754 }, ··· 1758 }, 1759 PubLeafletPublication: { 1760 lexicon: 1, 1761 - id: 'pub.leaflet.publication', 1762 defs: { 1763 main: { 1764 - type: 'record', 1765 - key: 'tid', 1766 - description: 'Record declaring a publication', 1767 record: { 1768 - type: 'object', 1769 - required: ['name'], 1770 properties: { 1771 name: { 1772 - type: 'string', 1773 maxLength: 2000, 1774 }, 1775 base_path: { 1776 - type: 'string', 1777 }, 1778 description: { 1779 - type: 'string', 1780 maxLength: 2000, 1781 }, 1782 icon: { 1783 - type: 'blob', 1784 - accept: ['image/*'], 1785 maxSize: 1000000, 1786 }, 1787 theme: { 1788 - type: 'ref', 1789 - ref: 'lex:pub.leaflet.publication#theme', 1790 }, 1791 preferences: { 1792 - type: 'ref', 1793 - ref: 'lex:pub.leaflet.publication#preferences', 1794 }, 1795 }, 1796 }, 1797 }, 1798 preferences: { 1799 - type: 'object', 1800 properties: { 1801 showInDiscover: { 1802 - type: 'boolean', 1803 default: true, 1804 }, 1805 showComments: { 1806 - type: 'boolean', 1807 default: true, 1808 }, 1809 }, 1810 }, 1811 theme: { 1812 - type: 'object', 1813 properties: { 1814 backgroundColor: { 1815 - type: 'union', 1816 refs: [ 1817 - 'lex:pub.leaflet.theme.color#rgba', 1818 - 'lex:pub.leaflet.theme.color#rgb', 1819 ], 1820 }, 1821 backgroundImage: { 1822 - type: 'ref', 1823 - ref: 'lex:pub.leaflet.theme.backgroundImage', 1824 }, 1825 primary: { 1826 - type: 'union', 1827 refs: [ 1828 - 'lex:pub.leaflet.theme.color#rgba', 1829 - 'lex:pub.leaflet.theme.color#rgb', 1830 ], 1831 }, 1832 pageBackground: { 1833 - type: 'union', 1834 refs: [ 1835 - 'lex:pub.leaflet.theme.color#rgba', 1836 - 'lex:pub.leaflet.theme.color#rgb', 1837 ], 1838 }, 1839 showPageBackground: { 1840 - type: 'boolean', 1841 default: false, 1842 }, 1843 accentBackground: { 1844 - type: 'union', 1845 refs: [ 1846 - 'lex:pub.leaflet.theme.color#rgba', 1847 - 'lex:pub.leaflet.theme.color#rgb', 1848 ], 1849 }, 1850 accentText: { 1851 - type: 'union', 1852 refs: [ 1853 - 'lex:pub.leaflet.theme.color#rgba', 1854 - 'lex:pub.leaflet.theme.color#rgb', 1855 ], 1856 }, 1857 }, ··· 1860 }, 1861 PubLeafletRichtextFacet: { 1862 lexicon: 1, 1863 - id: 'pub.leaflet.richtext.facet', 1864 defs: { 1865 main: { 1866 - type: 'object', 1867 - description: 'Annotation of a sub-string within rich text.', 1868 - required: ['index', 'features'], 1869 properties: { 1870 index: { 1871 - type: 'ref', 1872 - ref: 'lex:pub.leaflet.richtext.facet#byteSlice', 1873 }, 1874 features: { 1875 - type: 'array', 1876 items: { 1877 - type: 'union', 1878 refs: [ 1879 - 'lex:pub.leaflet.richtext.facet#link', 1880 - 'lex:pub.leaflet.richtext.facet#didMention', 1881 - 'lex:pub.leaflet.richtext.facet#atMention', 1882 - 'lex:pub.leaflet.richtext.facet#code', 1883 - 'lex:pub.leaflet.richtext.facet#highlight', 1884 - 'lex:pub.leaflet.richtext.facet#underline', 1885 - 'lex:pub.leaflet.richtext.facet#strikethrough', 1886 - 'lex:pub.leaflet.richtext.facet#id', 1887 - 'lex:pub.leaflet.richtext.facet#bold', 1888 - 'lex:pub.leaflet.richtext.facet#italic', 1889 ], 1890 }, 1891 }, 1892 }, 1893 }, 1894 byteSlice: { 1895 - type: 'object', 1896 description: 1897 - '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.', 1898 - required: ['byteStart', 'byteEnd'], 1899 properties: { 1900 byteStart: { 1901 - type: 'integer', 1902 minimum: 0, 1903 }, 1904 byteEnd: { 1905 - type: 'integer', 1906 minimum: 0, 1907 }, 1908 }, 1909 }, 1910 link: { 1911 - type: 'object', 1912 description: 1913 - 'Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.', 1914 - required: ['uri'], 1915 properties: { 1916 uri: { 1917 - type: 'string', 1918 }, 1919 }, 1920 }, 1921 didMention: { 1922 - type: 'object', 1923 - description: 'Facet feature for mentioning a did.', 1924 - required: ['did'], 1925 properties: { 1926 did: { 1927 - type: 'string', 1928 - format: 'did', 1929 }, 1930 }, 1931 }, 1932 atMention: { 1933 - type: 'object', 1934 - description: 'Facet feature for mentioning an AT URI.', 1935 - required: ['atURI'], 1936 properties: { 1937 atURI: { 1938 - type: 'string', 1939 - format: 'uri', 1940 }, 1941 }, 1942 }, 1943 code: { 1944 - type: 'object', 1945 - description: 'Facet feature for inline code.', 1946 required: [], 1947 properties: {}, 1948 }, 1949 highlight: { 1950 - type: 'object', 1951 - description: 'Facet feature for highlighted text.', 1952 required: [], 1953 properties: {}, 1954 }, 1955 underline: { 1956 - type: 'object', 1957 - description: 'Facet feature for underline markup', 1958 required: [], 1959 properties: {}, 1960 }, 1961 strikethrough: { 1962 - type: 'object', 1963 - description: 'Facet feature for strikethrough markup', 1964 required: [], 1965 properties: {}, 1966 }, 1967 id: { 1968 - type: 'object', 1969 description: 1970 - 'Facet feature for an identifier. Used for linking to a segment', 1971 required: [], 1972 properties: { 1973 id: { 1974 - type: 'string', 1975 }, 1976 }, 1977 }, 1978 bold: { 1979 - type: 'object', 1980 - description: 'Facet feature for bold text', 1981 required: [], 1982 properties: {}, 1983 }, 1984 italic: { 1985 - type: 'object', 1986 - description: 'Facet feature for italic text', 1987 required: [], 1988 properties: {}, 1989 }, ··· 1991 }, 1992 PubLeafletThemeBackgroundImage: { 1993 lexicon: 1, 1994 - id: 'pub.leaflet.theme.backgroundImage', 1995 defs: { 1996 main: { 1997 - type: 'object', 1998 - required: ['image'], 1999 properties: { 2000 image: { 2001 - type: 'blob', 2002 - accept: ['image/*'], 2003 maxSize: 1000000, 2004 }, 2005 width: { 2006 - type: 'integer', 2007 }, 2008 repeat: { 2009 - type: 'boolean', 2010 }, 2011 }, 2012 }, ··· 2014 }, 2015 PubLeafletThemeColor: { 2016 lexicon: 1, 2017 - id: 'pub.leaflet.theme.color', 2018 defs: { 2019 rgba: { 2020 - type: 'object', 2021 - required: ['r', 'g', 'b', 'a'], 2022 properties: { 2023 r: { 2024 - type: 'integer', 2025 maximum: 255, 2026 minimum: 0, 2027 }, 2028 g: { 2029 - type: 'integer', 2030 maximum: 255, 2031 minimum: 0, 2032 }, 2033 b: { 2034 - type: 'integer', 2035 maximum: 255, 2036 minimum: 0, 2037 }, 2038 a: { 2039 - type: 'integer', 2040 maximum: 100, 2041 minimum: 0, 2042 }, 2043 }, 2044 }, 2045 rgb: { 2046 - type: 'object', 2047 - required: ['r', 'g', 'b'], 2048 properties: { 2049 r: { 2050 - type: 'integer', 2051 maximum: 255, 2052 minimum: 0, 2053 }, 2054 g: { 2055 - type: 'integer', 2056 maximum: 255, 2057 minimum: 0, 2058 }, 2059 b: { 2060 - type: 'integer', 2061 maximum: 255, 2062 minimum: 0, 2063 }, ··· 2065 }, 2066 }, 2067 }, 2068 - } as const satisfies Record<string, LexiconDoc> 2069 - export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 2070 - export const lexicons: Lexicons = new Lexicons(schemas) 2071 2072 export function validate<T extends { $type: string }>( 2073 v: unknown, 2074 id: string, 2075 hash: string, 2076 requiredType: true, 2077 - ): ValidationResult<T> 2078 export function validate<T extends { $type?: string }>( 2079 v: unknown, 2080 id: string, 2081 hash: string, 2082 requiredType?: false, 2083 - ): ValidationResult<T> 2084 export function validate( 2085 v: unknown, 2086 id: string, ··· 2092 : { 2093 success: false, 2094 error: new ValidationError( 2095 - `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 2096 ), 2097 - } 2098 } 2099 2100 export const ids = { 2101 - AppBskyActorProfile: 'app.bsky.actor.profile', 2102 - ComAtprotoLabelDefs: 'com.atproto.label.defs', 2103 - ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites', 2104 - ComAtprotoRepoCreateRecord: 'com.atproto.repo.createRecord', 2105 - ComAtprotoRepoDefs: 'com.atproto.repo.defs', 2106 - ComAtprotoRepoDeleteRecord: 'com.atproto.repo.deleteRecord', 2107 - ComAtprotoRepoDescribeRepo: 'com.atproto.repo.describeRepo', 2108 - ComAtprotoRepoGetRecord: 'com.atproto.repo.getRecord', 2109 - ComAtprotoRepoImportRepo: 'com.atproto.repo.importRepo', 2110 - ComAtprotoRepoListMissingBlobs: 'com.atproto.repo.listMissingBlobs', 2111 - ComAtprotoRepoListRecords: 'com.atproto.repo.listRecords', 2112 - ComAtprotoRepoPutRecord: 'com.atproto.repo.putRecord', 2113 - ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', 2114 - ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob', 2115 - PubLeafletBlocksBlockquote: 'pub.leaflet.blocks.blockquote', 2116 - PubLeafletBlocksBskyPost: 'pub.leaflet.blocks.bskyPost', 2117 - PubLeafletBlocksButton: 'pub.leaflet.blocks.button', 2118 - PubLeafletBlocksCode: 'pub.leaflet.blocks.code', 2119 - PubLeafletBlocksHeader: 'pub.leaflet.blocks.header', 2120 - PubLeafletBlocksHorizontalRule: 'pub.leaflet.blocks.horizontalRule', 2121 - PubLeafletBlocksIframe: 'pub.leaflet.blocks.iframe', 2122 - PubLeafletBlocksImage: 'pub.leaflet.blocks.image', 2123 - PubLeafletBlocksMath: 'pub.leaflet.blocks.math', 2124 - PubLeafletBlocksPage: 'pub.leaflet.blocks.page', 2125 - PubLeafletBlocksPoll: 'pub.leaflet.blocks.poll', 2126 - PubLeafletBlocksText: 'pub.leaflet.blocks.text', 2127 - PubLeafletBlocksUnorderedList: 'pub.leaflet.blocks.unorderedList', 2128 - PubLeafletBlocksWebsite: 'pub.leaflet.blocks.website', 2129 - PubLeafletComment: 'pub.leaflet.comment', 2130 - PubLeafletDocument: 'pub.leaflet.document', 2131 - PubLeafletGraphSubscription: 'pub.leaflet.graph.subscription', 2132 - PubLeafletPagesCanvas: 'pub.leaflet.pages.canvas', 2133 - PubLeafletPagesLinearDocument: 'pub.leaflet.pages.linearDocument', 2134 - PubLeafletPollDefinition: 'pub.leaflet.poll.definition', 2135 - PubLeafletPollVote: 'pub.leaflet.poll.vote', 2136 - PubLeafletPublication: 'pub.leaflet.publication', 2137 - PubLeafletRichtextFacet: 'pub.leaflet.richtext.facet', 2138 - PubLeafletThemeBackgroundImage: 'pub.leaflet.theme.backgroundImage', 2139 - PubLeafletThemeColor: 'pub.leaflet.theme.color', 2140 - } as const
··· 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 }, ··· 71 }, 72 ComAtprotoLabelDefs: { 73 lexicon: 1, 74 + id: "com.atproto.label.defs", 75 defs: { 76 label: { 77 + type: "object", 78 description: 79 + "Metadata tag on an atproto resource (eg, repo or record).", 80 + required: ["src", "uri", "val", "cts"], 81 properties: { 82 ver: { 83 + type: "integer", 84 + description: "The AT Protocol version of the label object.", 85 }, 86 src: { 87 + type: "string", 88 + format: "did", 89 + description: "DID of the actor who created this label.", 90 }, 91 uri: { 92 + type: "string", 93 + format: "uri", 94 description: 95 + "AT URI of the record, repository (account), or other resource that this label applies to.", 96 }, 97 cid: { 98 + type: "string", 99 + format: "cid", 100 description: 101 "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 102 }, 103 val: { 104 + type: "string", 105 maxLength: 128, 106 description: 107 + "The short string name of the value or type of this label.", 108 }, 109 neg: { 110 + type: "boolean", 111 description: 112 + "If true, this is a negation label, overwriting a previous label.", 113 }, 114 cts: { 115 + type: "string", 116 + format: "datetime", 117 + description: "Timestamp when this label was created.", 118 }, 119 exp: { 120 + type: "string", 121 + format: "datetime", 122 description: 123 + "Timestamp at which this label expires (no longer applies).", 124 }, 125 sig: { 126 + type: "bytes", 127 + description: "Signature of dag-cbor encoded label.", 128 }, 129 }, 130 }, 131 selfLabels: { 132 + type: "object", 133 description: 134 + "Metadata tags on an atproto record, published by the author within the record.", 135 + required: ["values"], 136 properties: { 137 values: { 138 + type: "array", 139 items: { 140 + type: "ref", 141 + ref: "lex:com.atproto.label.defs#selfLabel", 142 }, 143 maxLength: 10, 144 }, 145 }, 146 }, 147 selfLabel: { 148 + type: "object", 149 description: 150 + "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.", 151 + required: ["val"], 152 properties: { 153 val: { 154 + type: "string", 155 maxLength: 128, 156 description: 157 + "The short string name of the value or type of this label.", 158 }, 159 }, 160 }, 161 labelValueDefinition: { 162 + type: "object", 163 description: 164 + "Declares a label value and its expected interpretations and behaviors.", 165 + required: ["identifier", "severity", "blurs", "locales"], 166 properties: { 167 identifier: { 168 + type: "string", 169 description: 170 "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", 171 maxLength: 100, 172 maxGraphemes: 100, 173 }, 174 severity: { 175 + type: "string", 176 description: 177 "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", 178 + knownValues: ["inform", "alert", "none"], 179 }, 180 blurs: { 181 + type: "string", 182 description: 183 "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", 184 + knownValues: ["content", "media", "none"], 185 }, 186 defaultSetting: { 187 + type: "string", 188 + description: "The default setting for this label.", 189 + knownValues: ["ignore", "warn", "hide"], 190 + default: "warn", 191 }, 192 adultOnly: { 193 + type: "boolean", 194 description: 195 + "Does the user need to have adult content enabled in order to configure this label?", 196 }, 197 locales: { 198 + type: "array", 199 items: { 200 + type: "ref", 201 + ref: "lex:com.atproto.label.defs#labelValueDefinitionStrings", 202 }, 203 }, 204 }, 205 }, 206 labelValueDefinitionStrings: { 207 + type: "object", 208 description: 209 + "Strings which describe the label in the UI, localized into a specific language.", 210 + required: ["lang", "name", "description"], 211 properties: { 212 lang: { 213 + type: "string", 214 description: 215 + "The code of the language these strings are written in.", 216 + format: "language", 217 }, 218 name: { 219 + type: "string", 220 + description: "A short human-readable name for the label.", 221 maxGraphemes: 64, 222 maxLength: 640, 223 }, 224 description: { 225 + type: "string", 226 description: 227 + "A longer description of what the label means and why it might be applied.", 228 maxGraphemes: 10000, 229 maxLength: 100000, 230 }, 231 }, 232 }, 233 labelValue: { 234 + type: "string", 235 knownValues: [ 236 + "!hide", 237 + "!no-promote", 238 + "!warn", 239 + "!no-unauthenticated", 240 + "dmca-violation", 241 + "doxxing", 242 + "porn", 243 + "sexual", 244 + "nudity", 245 + "nsfl", 246 + "gore", 247 ], 248 }, 249 }, 250 }, 251 ComAtprotoRepoApplyWrites: { 252 lexicon: 1, 253 + id: "com.atproto.repo.applyWrites", 254 defs: { 255 main: { 256 + type: "procedure", 257 description: 258 + "Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.", 259 input: { 260 + encoding: "application/json", 261 schema: { 262 + type: "object", 263 + required: ["repo", "writes"], 264 properties: { 265 repo: { 266 + type: "string", 267 + format: "at-identifier", 268 description: 269 + "The handle or DID of the repo (aka, current account).", 270 }, 271 validate: { 272 + type: "boolean", 273 description: 274 "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.", 275 }, 276 writes: { 277 + type: "array", 278 items: { 279 + type: "union", 280 refs: [ 281 + "lex:com.atproto.repo.applyWrites#create", 282 + "lex:com.atproto.repo.applyWrites#update", 283 + "lex:com.atproto.repo.applyWrites#delete", 284 ], 285 closed: true, 286 }, 287 }, 288 swapCommit: { 289 + type: "string", 290 description: 291 + "If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.", 292 + format: "cid", 293 }, 294 }, 295 }, 296 }, 297 output: { 298 + encoding: "application/json", 299 schema: { 300 + type: "object", 301 required: [], 302 properties: { 303 commit: { 304 + type: "ref", 305 + ref: "lex:com.atproto.repo.defs#commitMeta", 306 }, 307 results: { 308 + type: "array", 309 items: { 310 + type: "union", 311 refs: [ 312 + "lex:com.atproto.repo.applyWrites#createResult", 313 + "lex:com.atproto.repo.applyWrites#updateResult", 314 + "lex:com.atproto.repo.applyWrites#deleteResult", 315 ], 316 closed: true, 317 }, ··· 321 }, 322 errors: [ 323 { 324 + name: "InvalidSwap", 325 description: 326 "Indicates that the 'swapCommit' parameter did not match current commit.", 327 }, 328 ], 329 }, 330 create: { 331 + type: "object", 332 + description: "Operation which creates a new record.", 333 + required: ["collection", "value"], 334 properties: { 335 collection: { 336 + type: "string", 337 + format: "nsid", 338 }, 339 rkey: { 340 + type: "string", 341 maxLength: 512, 342 + format: "record-key", 343 description: 344 + "NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.", 345 }, 346 value: { 347 + type: "unknown", 348 }, 349 }, 350 }, 351 update: { 352 + type: "object", 353 + description: "Operation which updates an existing record.", 354 + required: ["collection", "rkey", "value"], 355 properties: { 356 collection: { 357 + type: "string", 358 + format: "nsid", 359 }, 360 rkey: { 361 + type: "string", 362 + format: "record-key", 363 }, 364 value: { 365 + type: "unknown", 366 }, 367 }, 368 }, 369 delete: { 370 + type: "object", 371 + description: "Operation which deletes an existing record.", 372 + required: ["collection", "rkey"], 373 properties: { 374 collection: { 375 + type: "string", 376 + format: "nsid", 377 }, 378 rkey: { 379 + type: "string", 380 + format: "record-key", 381 }, 382 }, 383 }, 384 createResult: { 385 + type: "object", 386 + required: ["uri", "cid"], 387 properties: { 388 uri: { 389 + type: "string", 390 + format: "at-uri", 391 }, 392 cid: { 393 + type: "string", 394 + format: "cid", 395 }, 396 validationStatus: { 397 + type: "string", 398 + knownValues: ["valid", "unknown"], 399 }, 400 }, 401 }, 402 updateResult: { 403 + type: "object", 404 + required: ["uri", "cid"], 405 properties: { 406 uri: { 407 + type: "string", 408 + format: "at-uri", 409 }, 410 cid: { 411 + type: "string", 412 + format: "cid", 413 }, 414 validationStatus: { 415 + type: "string", 416 + knownValues: ["valid", "unknown"], 417 }, 418 }, 419 }, 420 deleteResult: { 421 + type: "object", 422 required: [], 423 properties: {}, 424 }, ··· 426 }, 427 ComAtprotoRepoCreateRecord: { 428 lexicon: 1, 429 + id: "com.atproto.repo.createRecord", 430 defs: { 431 main: { 432 + type: "procedure", 433 description: 434 + "Create a single new repository record. Requires auth, implemented by PDS.", 435 input: { 436 + encoding: "application/json", 437 schema: { 438 + type: "object", 439 + required: ["repo", "collection", "record"], 440 properties: { 441 repo: { 442 + type: "string", 443 + format: "at-identifier", 444 description: 445 + "The handle or DID of the repo (aka, current account).", 446 }, 447 collection: { 448 + type: "string", 449 + format: "nsid", 450 + description: "The NSID of the record collection.", 451 }, 452 rkey: { 453 + type: "string", 454 + format: "record-key", 455 + description: "The Record Key.", 456 maxLength: 512, 457 }, 458 validate: { 459 + type: "boolean", 460 description: 461 "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 462 }, 463 record: { 464 + type: "unknown", 465 + description: "The record itself. Must contain a $type field.", 466 }, 467 swapCommit: { 468 + type: "string", 469 + format: "cid", 470 description: 471 + "Compare and swap with the previous commit by CID.", 472 }, 473 }, 474 }, 475 }, 476 output: { 477 + encoding: "application/json", 478 schema: { 479 + type: "object", 480 + required: ["uri", "cid"], 481 properties: { 482 uri: { 483 + type: "string", 484 + format: "at-uri", 485 }, 486 cid: { 487 + type: "string", 488 + format: "cid", 489 }, 490 commit: { 491 + type: "ref", 492 + ref: "lex:com.atproto.repo.defs#commitMeta", 493 }, 494 validationStatus: { 495 + type: "string", 496 + knownValues: ["valid", "unknown"], 497 }, 498 }, 499 }, 500 }, 501 errors: [ 502 { 503 + name: "InvalidSwap", 504 description: 505 "Indicates that 'swapCommit' didn't match current repo commit.", 506 }, ··· 510 }, 511 ComAtprotoRepoDefs: { 512 lexicon: 1, 513 + id: "com.atproto.repo.defs", 514 defs: { 515 commitMeta: { 516 + type: "object", 517 + required: ["cid", "rev"], 518 properties: { 519 cid: { 520 + type: "string", 521 + format: "cid", 522 }, 523 rev: { 524 + type: "string", 525 + format: "tid", 526 }, 527 }, 528 }, ··· 530 }, 531 ComAtprotoRepoDeleteRecord: { 532 lexicon: 1, 533 + id: "com.atproto.repo.deleteRecord", 534 defs: { 535 main: { 536 + type: "procedure", 537 description: 538 "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.", 539 input: { 540 + encoding: "application/json", 541 schema: { 542 + type: "object", 543 + required: ["repo", "collection", "rkey"], 544 properties: { 545 repo: { 546 + type: "string", 547 + format: "at-identifier", 548 description: 549 + "The handle or DID of the repo (aka, current account).", 550 }, 551 collection: { 552 + type: "string", 553 + format: "nsid", 554 + description: "The NSID of the record collection.", 555 }, 556 rkey: { 557 + type: "string", 558 + format: "record-key", 559 + description: "The Record Key.", 560 }, 561 swapRecord: { 562 + type: "string", 563 + format: "cid", 564 description: 565 + "Compare and swap with the previous record by CID.", 566 }, 567 swapCommit: { 568 + type: "string", 569 + format: "cid", 570 description: 571 + "Compare and swap with the previous commit by CID.", 572 }, 573 }, 574 }, 575 }, 576 output: { 577 + encoding: "application/json", 578 schema: { 579 + type: "object", 580 properties: { 581 commit: { 582 + type: "ref", 583 + ref: "lex:com.atproto.repo.defs#commitMeta", 584 }, 585 }, 586 }, 587 }, 588 errors: [ 589 { 590 + name: "InvalidSwap", 591 }, 592 ], 593 }, ··· 595 }, 596 ComAtprotoRepoDescribeRepo: { 597 lexicon: 1, 598 + id: "com.atproto.repo.describeRepo", 599 defs: { 600 main: { 601 + type: "query", 602 description: 603 + "Get information about an account and repository, including the list of collections. Does not require auth.", 604 parameters: { 605 + type: "params", 606 + required: ["repo"], 607 properties: { 608 repo: { 609 + type: "string", 610 + format: "at-identifier", 611 + description: "The handle or DID of the repo.", 612 }, 613 }, 614 }, 615 output: { 616 + encoding: "application/json", 617 schema: { 618 + type: "object", 619 required: [ 620 + "handle", 621 + "did", 622 + "didDoc", 623 + "collections", 624 + "handleIsCorrect", 625 ], 626 properties: { 627 handle: { 628 + type: "string", 629 + format: "handle", 630 }, 631 did: { 632 + type: "string", 633 + format: "did", 634 }, 635 didDoc: { 636 + type: "unknown", 637 + description: "The complete DID document for this account.", 638 }, 639 collections: { 640 + type: "array", 641 description: 642 + "List of all the collections (NSIDs) for which this repo contains at least one record.", 643 items: { 644 + type: "string", 645 + format: "nsid", 646 }, 647 }, 648 handleIsCorrect: { 649 + type: "boolean", 650 description: 651 + "Indicates if handle is currently valid (resolves bi-directionally)", 652 }, 653 }, 654 }, ··· 658 }, 659 ComAtprotoRepoGetRecord: { 660 lexicon: 1, 661 + id: "com.atproto.repo.getRecord", 662 defs: { 663 main: { 664 + type: "query", 665 description: 666 + "Get a single record from a repository. Does not require auth.", 667 parameters: { 668 + type: "params", 669 + required: ["repo", "collection", "rkey"], 670 properties: { 671 repo: { 672 + type: "string", 673 + format: "at-identifier", 674 + description: "The handle or DID of the repo.", 675 }, 676 collection: { 677 + type: "string", 678 + format: "nsid", 679 + description: "The NSID of the record collection.", 680 }, 681 rkey: { 682 + type: "string", 683 + description: "The Record Key.", 684 + format: "record-key", 685 }, 686 cid: { 687 + type: "string", 688 + format: "cid", 689 description: 690 + "The CID of the version of the record. If not specified, then return the most recent version.", 691 }, 692 }, 693 }, 694 output: { 695 + encoding: "application/json", 696 schema: { 697 + type: "object", 698 + required: ["uri", "value"], 699 properties: { 700 uri: { 701 + type: "string", 702 + format: "at-uri", 703 }, 704 cid: { 705 + type: "string", 706 + format: "cid", 707 }, 708 value: { 709 + type: "unknown", 710 }, 711 }, 712 }, 713 }, 714 errors: [ 715 { 716 + name: "RecordNotFound", 717 }, 718 ], 719 }, ··· 721 }, 722 ComAtprotoRepoImportRepo: { 723 lexicon: 1, 724 + id: "com.atproto.repo.importRepo", 725 defs: { 726 main: { 727 + type: "procedure", 728 description: 729 + "Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.", 730 input: { 731 + encoding: "application/vnd.ipld.car", 732 }, 733 }, 734 }, 735 }, 736 ComAtprotoRepoListMissingBlobs: { 737 lexicon: 1, 738 + id: "com.atproto.repo.listMissingBlobs", 739 defs: { 740 main: { 741 + type: "query", 742 description: 743 + "Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.", 744 parameters: { 745 + type: "params", 746 properties: { 747 limit: { 748 + type: "integer", 749 minimum: 1, 750 maximum: 1000, 751 default: 500, 752 }, 753 cursor: { 754 + type: "string", 755 }, 756 }, 757 }, 758 output: { 759 + encoding: "application/json", 760 schema: { 761 + type: "object", 762 + required: ["blobs"], 763 properties: { 764 cursor: { 765 + type: "string", 766 }, 767 blobs: { 768 + type: "array", 769 items: { 770 + type: "ref", 771 + ref: "lex:com.atproto.repo.listMissingBlobs#recordBlob", 772 }, 773 }, 774 }, ··· 776 }, 777 }, 778 recordBlob: { 779 + type: "object", 780 + required: ["cid", "recordUri"], 781 properties: { 782 cid: { 783 + type: "string", 784 + format: "cid", 785 }, 786 recordUri: { 787 + type: "string", 788 + format: "at-uri", 789 }, 790 }, 791 }, ··· 793 }, 794 ComAtprotoRepoListRecords: { 795 lexicon: 1, 796 + id: "com.atproto.repo.listRecords", 797 defs: { 798 main: { 799 + type: "query", 800 description: 801 + "List a range of records in a repository, matching a specific collection. Does not require auth.", 802 parameters: { 803 + type: "params", 804 + required: ["repo", "collection"], 805 properties: { 806 repo: { 807 + type: "string", 808 + format: "at-identifier", 809 + description: "The handle or DID of the repo.", 810 }, 811 collection: { 812 + type: "string", 813 + format: "nsid", 814 + description: "The NSID of the record type.", 815 }, 816 limit: { 817 + type: "integer", 818 minimum: 1, 819 maximum: 100, 820 default: 50, 821 + description: "The number of records to return.", 822 }, 823 cursor: { 824 + type: "string", 825 }, 826 rkeyStart: { 827 + type: "string", 828 description: 829 + "DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)", 830 }, 831 rkeyEnd: { 832 + type: "string", 833 description: 834 + "DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)", 835 }, 836 reverse: { 837 + type: "boolean", 838 + description: "Flag to reverse the order of the returned records.", 839 }, 840 }, 841 }, 842 output: { 843 + encoding: "application/json", 844 schema: { 845 + type: "object", 846 + required: ["records"], 847 properties: { 848 cursor: { 849 + type: "string", 850 }, 851 records: { 852 + type: "array", 853 items: { 854 + type: "ref", 855 + ref: "lex:com.atproto.repo.listRecords#record", 856 }, 857 }, 858 }, ··· 860 }, 861 }, 862 record: { 863 + type: "object", 864 + required: ["uri", "cid", "value"], 865 properties: { 866 uri: { 867 + type: "string", 868 + format: "at-uri", 869 }, 870 cid: { 871 + type: "string", 872 + format: "cid", 873 }, 874 value: { 875 + type: "unknown", 876 }, 877 }, 878 }, ··· 880 }, 881 ComAtprotoRepoPutRecord: { 882 lexicon: 1, 883 + id: "com.atproto.repo.putRecord", 884 defs: { 885 main: { 886 + type: "procedure", 887 description: 888 + "Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.", 889 input: { 890 + encoding: "application/json", 891 schema: { 892 + type: "object", 893 + required: ["repo", "collection", "rkey", "record"], 894 + nullable: ["swapRecord"], 895 properties: { 896 repo: { 897 + type: "string", 898 + format: "at-identifier", 899 description: 900 + "The handle or DID of the repo (aka, current account).", 901 }, 902 collection: { 903 + type: "string", 904 + format: "nsid", 905 + description: "The NSID of the record collection.", 906 }, 907 rkey: { 908 + type: "string", 909 + format: "record-key", 910 + description: "The Record Key.", 911 maxLength: 512, 912 }, 913 validate: { 914 + type: "boolean", 915 description: 916 "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 917 }, 918 record: { 919 + type: "unknown", 920 + description: "The record to write.", 921 }, 922 swapRecord: { 923 + type: "string", 924 + format: "cid", 925 description: 926 + "Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation", 927 }, 928 swapCommit: { 929 + type: "string", 930 + format: "cid", 931 description: 932 + "Compare and swap with the previous commit by CID.", 933 }, 934 }, 935 }, 936 }, 937 output: { 938 + encoding: "application/json", 939 schema: { 940 + type: "object", 941 + required: ["uri", "cid"], 942 properties: { 943 uri: { 944 + type: "string", 945 + format: "at-uri", 946 }, 947 cid: { 948 + type: "string", 949 + format: "cid", 950 }, 951 commit: { 952 + type: "ref", 953 + ref: "lex:com.atproto.repo.defs#commitMeta", 954 }, 955 validationStatus: { 956 + type: "string", 957 + knownValues: ["valid", "unknown"], 958 }, 959 }, 960 }, 961 }, 962 errors: [ 963 { 964 + name: "InvalidSwap", 965 }, 966 ], 967 }, ··· 969 }, 970 ComAtprotoRepoStrongRef: { 971 lexicon: 1, 972 + id: "com.atproto.repo.strongRef", 973 + description: "A URI with a content-hash fingerprint.", 974 defs: { 975 main: { 976 + type: "object", 977 + required: ["uri", "cid"], 978 properties: { 979 uri: { 980 + type: "string", 981 + format: "at-uri", 982 }, 983 cid: { 984 + type: "string", 985 + format: "cid", 986 }, 987 }, 988 }, ··· 990 }, 991 ComAtprotoRepoUploadBlob: { 992 lexicon: 1, 993 + id: "com.atproto.repo.uploadBlob", 994 defs: { 995 main: { 996 + type: "procedure", 997 description: 998 + "Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.", 999 input: { 1000 + encoding: "*/*", 1001 }, 1002 output: { 1003 + encoding: "application/json", 1004 schema: { 1005 + type: "object", 1006 + required: ["blob"], 1007 properties: { 1008 blob: { 1009 + type: "blob", 1010 }, 1011 }, 1012 }, ··· 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 }, ··· 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 }, ··· 1054 }, 1055 PubLeafletBlocksButton: { 1056 lexicon: 1, 1057 + id: "pub.leaflet.blocks.button", 1058 defs: { 1059 main: { 1060 + type: "object", 1061 + required: ["text", "url"], 1062 properties: { 1063 text: { 1064 + type: "string", 1065 }, 1066 url: { 1067 + type: "string", 1068 + format: "uri", 1069 }, 1070 }, 1071 }, ··· 1073 }, 1074 PubLeafletBlocksCode: { 1075 lexicon: 1, 1076 + id: "pub.leaflet.blocks.code", 1077 defs: { 1078 main: { 1079 + type: "object", 1080 + required: ["plaintext"], 1081 properties: { 1082 plaintext: { 1083 + type: "string", 1084 }, 1085 language: { 1086 + type: "string", 1087 }, 1088 syntaxHighlightingTheme: { 1089 + type: "string", 1090 }, 1091 }, 1092 }, ··· 1094 }, 1095 PubLeafletBlocksHeader: { 1096 lexicon: 1, 1097 + id: "pub.leaflet.blocks.header", 1098 defs: { 1099 main: { 1100 + type: "object", 1101 + required: ["plaintext"], 1102 properties: { 1103 level: { 1104 + type: "integer", 1105 minimum: 1, 1106 maximum: 6, 1107 }, 1108 plaintext: { 1109 + type: "string", 1110 }, 1111 facets: { 1112 + type: "array", 1113 items: { 1114 + type: "ref", 1115 + ref: "lex:pub.leaflet.richtext.facet", 1116 }, 1117 }, 1118 }, ··· 1121 }, 1122 PubLeafletBlocksHorizontalRule: { 1123 lexicon: 1, 1124 + id: "pub.leaflet.blocks.horizontalRule", 1125 defs: { 1126 main: { 1127 + type: "object", 1128 required: [], 1129 properties: {}, 1130 }, ··· 1132 }, 1133 PubLeafletBlocksIframe: { 1134 lexicon: 1, 1135 + id: "pub.leaflet.blocks.iframe", 1136 defs: { 1137 main: { 1138 + type: "object", 1139 + required: ["url"], 1140 properties: { 1141 url: { 1142 + type: "string", 1143 + format: "uri", 1144 }, 1145 height: { 1146 + type: "integer", 1147 minimum: 16, 1148 maximum: 1600, 1149 }, ··· 1153 }, 1154 PubLeafletBlocksImage: { 1155 lexicon: 1, 1156 + id: "pub.leaflet.blocks.image", 1157 defs: { 1158 main: { 1159 + type: "object", 1160 + required: ["image", "aspectRatio"], 1161 properties: { 1162 image: { 1163 + type: "blob", 1164 + accept: ["image/*"], 1165 maxSize: 1000000, 1166 }, 1167 alt: { 1168 + type: "string", 1169 description: 1170 + "Alt text description of the image, for accessibility.", 1171 }, 1172 aspectRatio: { 1173 + type: "ref", 1174 + ref: "lex:pub.leaflet.blocks.image#aspectRatio", 1175 }, 1176 }, 1177 }, 1178 aspectRatio: { 1179 + type: "object", 1180 + required: ["width", "height"], 1181 properties: { 1182 width: { 1183 + type: "integer", 1184 }, 1185 height: { 1186 + type: "integer", 1187 }, 1188 }, 1189 }, ··· 1191 }, 1192 PubLeafletBlocksMath: { 1193 lexicon: 1, 1194 + id: "pub.leaflet.blocks.math", 1195 defs: { 1196 main: { 1197 + type: "object", 1198 + required: ["tex"], 1199 properties: { 1200 tex: { 1201 + type: "string", 1202 }, 1203 }, 1204 }, ··· 1206 }, 1207 PubLeafletBlocksPage: { 1208 lexicon: 1, 1209 + id: "pub.leaflet.blocks.page", 1210 defs: { 1211 main: { 1212 + type: "object", 1213 + required: ["id"], 1214 properties: { 1215 id: { 1216 + type: "string", 1217 }, 1218 }, 1219 }, ··· 1221 }, 1222 PubLeafletBlocksPoll: { 1223 lexicon: 1, 1224 + id: "pub.leaflet.blocks.poll", 1225 defs: { 1226 main: { 1227 + type: "object", 1228 + required: ["pollRef"], 1229 properties: { 1230 pollRef: { 1231 + type: "ref", 1232 + ref: "lex:com.atproto.repo.strongRef", 1233 }, 1234 }, 1235 }, ··· 1237 }, 1238 PubLeafletBlocksText: { 1239 lexicon: 1, 1240 + id: "pub.leaflet.blocks.text", 1241 defs: { 1242 main: { 1243 + type: "object", 1244 + required: ["plaintext"], 1245 properties: { 1246 plaintext: { 1247 + type: "string", 1248 }, 1249 facets: { 1250 + type: "array", 1251 items: { 1252 + type: "ref", 1253 + ref: "lex:pub.leaflet.richtext.facet", 1254 }, 1255 }, 1256 }, ··· 1259 }, 1260 PubLeafletBlocksUnorderedList: { 1261 lexicon: 1, 1262 + id: "pub.leaflet.blocks.unorderedList", 1263 defs: { 1264 main: { 1265 + type: "object", 1266 + required: ["children"], 1267 properties: { 1268 children: { 1269 + type: "array", 1270 items: { 1271 + type: "ref", 1272 + ref: "lex:pub.leaflet.blocks.unorderedList#listItem", 1273 }, 1274 }, 1275 }, 1276 }, 1277 listItem: { 1278 + type: "object", 1279 + required: ["content"], 1280 properties: { 1281 content: { 1282 + type: "union", 1283 refs: [ 1284 + "lex:pub.leaflet.blocks.text", 1285 + "lex:pub.leaflet.blocks.header", 1286 + "lex:pub.leaflet.blocks.image", 1287 ], 1288 }, 1289 children: { 1290 + type: "array", 1291 items: { 1292 + type: "ref", 1293 + ref: "lex:pub.leaflet.blocks.unorderedList#listItem", 1294 }, 1295 }, 1296 }, ··· 1299 }, 1300 PubLeafletBlocksWebsite: { 1301 lexicon: 1, 1302 + id: "pub.leaflet.blocks.website", 1303 defs: { 1304 main: { 1305 + type: "object", 1306 + required: ["src"], 1307 properties: { 1308 previewImage: { 1309 + type: "blob", 1310 + accept: ["image/*"], 1311 maxSize: 1000000, 1312 }, 1313 title: { 1314 + type: "string", 1315 }, 1316 description: { 1317 + type: "string", 1318 }, 1319 src: { 1320 + type: "string", 1321 + format: "uri", 1322 }, 1323 }, 1324 }, ··· 1326 }, 1327 PubLeafletComment: { 1328 lexicon: 1, 1329 + id: "pub.leaflet.comment", 1330 revision: 1, 1331 + description: "A lexicon for comments on documents", 1332 defs: { 1333 main: { 1334 + type: "record", 1335 + key: "tid", 1336 + description: "Record containing a comment", 1337 record: { 1338 + type: "object", 1339 + required: ["subject", "plaintext", "createdAt"], 1340 properties: { 1341 subject: { 1342 + type: "string", 1343 + format: "at-uri", 1344 }, 1345 createdAt: { 1346 + type: "string", 1347 + format: "datetime", 1348 }, 1349 reply: { 1350 + type: "ref", 1351 + ref: "lex:pub.leaflet.comment#replyRef", 1352 }, 1353 plaintext: { 1354 + type: "string", 1355 }, 1356 facets: { 1357 + type: "array", 1358 items: { 1359 + type: "ref", 1360 + ref: "lex:pub.leaflet.richtext.facet", 1361 }, 1362 }, 1363 onPage: { 1364 + type: "string", 1365 }, 1366 attachment: { 1367 + type: "union", 1368 + refs: ["lex:pub.leaflet.comment#linearDocumentQuote"], 1369 }, 1370 }, 1371 }, 1372 }, 1373 linearDocumentQuote: { 1374 + type: "object", 1375 + required: ["document", "quote"], 1376 properties: { 1377 document: { 1378 + type: "string", 1379 + format: "at-uri", 1380 }, 1381 quote: { 1382 + type: "ref", 1383 + ref: "lex:pub.leaflet.pages.linearDocument#quote", 1384 }, 1385 }, 1386 }, 1387 replyRef: { 1388 + type: "object", 1389 + required: ["parent"], 1390 properties: { 1391 parent: { 1392 + type: "string", 1393 + format: "at-uri", 1394 }, 1395 }, 1396 }, ··· 1398 }, 1399 PubLeafletDocument: { 1400 lexicon: 1, 1401 + id: "pub.leaflet.document", 1402 revision: 1, 1403 + description: "A lexicon for long form rich media documents", 1404 defs: { 1405 main: { 1406 + type: "record", 1407 + key: "tid", 1408 + description: "Record containing a document", 1409 record: { 1410 + type: "object", 1411 + required: ["pages", "author", "title"], 1412 properties: { 1413 title: { 1414 + type: "string", 1415 maxLength: 1280, 1416 maxGraphemes: 128, 1417 }, 1418 postRef: { 1419 + type: "ref", 1420 + ref: "lex:com.atproto.repo.strongRef", 1421 }, 1422 description: { 1423 + type: "string", 1424 maxLength: 3000, 1425 maxGraphemes: 300, 1426 }, 1427 publishedAt: { 1428 + type: "string", 1429 + format: "datetime", 1430 }, 1431 publication: { 1432 + type: "string", 1433 + format: "at-uri", 1434 }, 1435 author: { 1436 + type: "string", 1437 + format: "at-identifier", 1438 }, 1439 theme: { 1440 + type: "ref", 1441 + ref: "lex:pub.leaflet.publication#theme", 1442 }, 1443 tags: { 1444 + type: "array", 1445 items: { 1446 + type: "string", 1447 maxLength: 50, 1448 }, 1449 }, 1450 coverImage: { 1451 + type: "blob", 1452 + accept: ["image/png", "image/jpeg", "image/webp"], 1453 maxSize: 1000000, 1454 }, 1455 pages: { 1456 + type: "array", 1457 items: { 1458 + type: "union", 1459 refs: [ 1460 + "lex:pub.leaflet.pages.linearDocument", 1461 + "lex:pub.leaflet.pages.canvas", 1462 ], 1463 }, 1464 }, ··· 1469 }, 1470 PubLeafletGraphSubscription: { 1471 lexicon: 1, 1472 + id: "pub.leaflet.graph.subscription", 1473 defs: { 1474 main: { 1475 + type: "record", 1476 + key: "tid", 1477 + description: "Record declaring a subscription to a publication", 1478 record: { 1479 + type: "object", 1480 + required: ["publication"], 1481 properties: { 1482 publication: { 1483 + type: "string", 1484 + format: "at-uri", 1485 }, 1486 }, 1487 }, ··· 1490 }, 1491 PubLeafletPagesCanvas: { 1492 lexicon: 1, 1493 + id: "pub.leaflet.pages.canvas", 1494 defs: { 1495 main: { 1496 + type: "object", 1497 + required: ["blocks"], 1498 properties: { 1499 id: { 1500 + type: "string", 1501 }, 1502 blocks: { 1503 + type: "array", 1504 items: { 1505 + type: "ref", 1506 + ref: "lex:pub.leaflet.pages.canvas#block", 1507 }, 1508 }, 1509 }, 1510 }, 1511 block: { 1512 + type: "object", 1513 + required: ["block", "x", "y", "width"], 1514 properties: { 1515 block: { 1516 + type: "union", 1517 refs: [ 1518 + "lex:pub.leaflet.blocks.iframe", 1519 + "lex:pub.leaflet.blocks.text", 1520 + "lex:pub.leaflet.blocks.blockquote", 1521 + "lex:pub.leaflet.blocks.header", 1522 + "lex:pub.leaflet.blocks.image", 1523 + "lex:pub.leaflet.blocks.unorderedList", 1524 + "lex:pub.leaflet.blocks.website", 1525 + "lex:pub.leaflet.blocks.math", 1526 + "lex:pub.leaflet.blocks.code", 1527 + "lex:pub.leaflet.blocks.horizontalRule", 1528 + "lex:pub.leaflet.blocks.bskyPost", 1529 + "lex:pub.leaflet.blocks.page", 1530 + "lex:pub.leaflet.blocks.poll", 1531 + "lex:pub.leaflet.blocks.button", 1532 ], 1533 }, 1534 x: { 1535 + type: "integer", 1536 }, 1537 y: { 1538 + type: "integer", 1539 }, 1540 width: { 1541 + type: "integer", 1542 }, 1543 height: { 1544 + type: "integer", 1545 }, 1546 rotation: { 1547 + type: "integer", 1548 + description: "The rotation of the block in degrees", 1549 }, 1550 }, 1551 }, 1552 textAlignLeft: { 1553 + type: "token", 1554 }, 1555 textAlignCenter: { 1556 + type: "token", 1557 }, 1558 textAlignRight: { 1559 + type: "token", 1560 }, 1561 quote: { 1562 + type: "object", 1563 + required: ["start", "end"], 1564 properties: { 1565 start: { 1566 + type: "ref", 1567 + ref: "lex:pub.leaflet.pages.canvas#position", 1568 }, 1569 end: { 1570 + type: "ref", 1571 + ref: "lex:pub.leaflet.pages.canvas#position", 1572 }, 1573 }, 1574 }, 1575 position: { 1576 + type: "object", 1577 + required: ["block", "offset"], 1578 properties: { 1579 block: { 1580 + type: "array", 1581 items: { 1582 + type: "integer", 1583 }, 1584 }, 1585 offset: { 1586 + type: "integer", 1587 }, 1588 }, 1589 }, ··· 1591 }, 1592 PubLeafletPagesLinearDocument: { 1593 lexicon: 1, 1594 + id: "pub.leaflet.pages.linearDocument", 1595 defs: { 1596 main: { 1597 + type: "object", 1598 + required: ["blocks"], 1599 properties: { 1600 id: { 1601 + type: "string", 1602 }, 1603 blocks: { 1604 + type: "array", 1605 items: { 1606 + type: "ref", 1607 + ref: "lex:pub.leaflet.pages.linearDocument#block", 1608 }, 1609 }, 1610 }, 1611 }, 1612 block: { 1613 + type: "object", 1614 + required: ["block"], 1615 properties: { 1616 block: { 1617 + type: "union", 1618 refs: [ 1619 + "lex:pub.leaflet.blocks.iframe", 1620 + "lex:pub.leaflet.blocks.text", 1621 + "lex:pub.leaflet.blocks.blockquote", 1622 + "lex:pub.leaflet.blocks.header", 1623 + "lex:pub.leaflet.blocks.image", 1624 + "lex:pub.leaflet.blocks.unorderedList", 1625 + "lex:pub.leaflet.blocks.website", 1626 + "lex:pub.leaflet.blocks.math", 1627 + "lex:pub.leaflet.blocks.code", 1628 + "lex:pub.leaflet.blocks.horizontalRule", 1629 + "lex:pub.leaflet.blocks.bskyPost", 1630 + "lex:pub.leaflet.blocks.page", 1631 + "lex:pub.leaflet.blocks.poll", 1632 + "lex:pub.leaflet.blocks.button", 1633 ], 1634 }, 1635 alignment: { 1636 + type: "string", 1637 knownValues: [ 1638 + "lex:pub.leaflet.pages.linearDocument#textAlignLeft", 1639 + "lex:pub.leaflet.pages.linearDocument#textAlignCenter", 1640 + "lex:pub.leaflet.pages.linearDocument#textAlignRight", 1641 + "lex:pub.leaflet.pages.linearDocument#textAlignJustify", 1642 ], 1643 }, 1644 }, 1645 }, 1646 textAlignLeft: { 1647 + type: "token", 1648 }, 1649 textAlignCenter: { 1650 + type: "token", 1651 }, 1652 textAlignRight: { 1653 + type: "token", 1654 }, 1655 textAlignJustify: { 1656 + type: "token", 1657 }, 1658 quote: { 1659 + type: "object", 1660 + required: ["start", "end"], 1661 properties: { 1662 start: { 1663 + type: "ref", 1664 + ref: "lex:pub.leaflet.pages.linearDocument#position", 1665 }, 1666 end: { 1667 + type: "ref", 1668 + ref: "lex:pub.leaflet.pages.linearDocument#position", 1669 }, 1670 }, 1671 }, 1672 position: { 1673 + type: "object", 1674 + required: ["block", "offset"], 1675 properties: { 1676 block: { 1677 + type: "array", 1678 items: { 1679 + type: "integer", 1680 }, 1681 }, 1682 offset: { 1683 + type: "integer", 1684 }, 1685 }, 1686 }, ··· 1688 }, 1689 PubLeafletPollDefinition: { 1690 lexicon: 1, 1691 + id: "pub.leaflet.poll.definition", 1692 defs: { 1693 main: { 1694 + type: "record", 1695 + key: "tid", 1696 + description: "Record declaring a poll", 1697 record: { 1698 + type: "object", 1699 + required: ["name", "options"], 1700 properties: { 1701 name: { 1702 + type: "string", 1703 maxLength: 500, 1704 maxGraphemes: 100, 1705 }, 1706 options: { 1707 + type: "array", 1708 items: { 1709 + type: "ref", 1710 + ref: "lex:pub.leaflet.poll.definition#option", 1711 }, 1712 }, 1713 endDate: { 1714 + type: "string", 1715 + format: "datetime", 1716 }, 1717 }, 1718 }, 1719 }, 1720 option: { 1721 + type: "object", 1722 properties: { 1723 text: { 1724 + type: "string", 1725 maxLength: 500, 1726 maxGraphemes: 50, 1727 }, ··· 1731 }, 1732 PubLeafletPollVote: { 1733 lexicon: 1, 1734 + id: "pub.leaflet.poll.vote", 1735 defs: { 1736 main: { 1737 + type: "record", 1738 + key: "tid", 1739 + description: "Record declaring a vote on a poll", 1740 record: { 1741 + type: "object", 1742 + required: ["poll", "option"], 1743 properties: { 1744 poll: { 1745 + type: "ref", 1746 + ref: "lex:com.atproto.repo.strongRef", 1747 }, 1748 option: { 1749 + type: "array", 1750 items: { 1751 + type: "string", 1752 }, 1753 }, 1754 }, ··· 1758 }, 1759 PubLeafletPublication: { 1760 lexicon: 1, 1761 + id: "pub.leaflet.publication", 1762 defs: { 1763 main: { 1764 + type: "record", 1765 + key: "tid", 1766 + description: "Record declaring a publication", 1767 record: { 1768 + type: "object", 1769 + required: ["name"], 1770 properties: { 1771 name: { 1772 + type: "string", 1773 maxLength: 2000, 1774 }, 1775 base_path: { 1776 + type: "string", 1777 }, 1778 description: { 1779 + type: "string", 1780 maxLength: 2000, 1781 }, 1782 icon: { 1783 + type: "blob", 1784 + accept: ["image/*"], 1785 maxSize: 1000000, 1786 }, 1787 theme: { 1788 + type: "ref", 1789 + ref: "lex:pub.leaflet.publication#theme", 1790 }, 1791 preferences: { 1792 + type: "ref", 1793 + ref: "lex:pub.leaflet.publication#preferences", 1794 }, 1795 }, 1796 }, 1797 }, 1798 preferences: { 1799 + type: "object", 1800 properties: { 1801 showInDiscover: { 1802 + type: "boolean", 1803 default: true, 1804 }, 1805 showComments: { 1806 + type: "boolean", 1807 default: true, 1808 }, 1809 }, 1810 }, 1811 theme: { 1812 + type: "object", 1813 properties: { 1814 backgroundColor: { 1815 + type: "union", 1816 refs: [ 1817 + "lex:pub.leaflet.theme.color#rgba", 1818 + "lex:pub.leaflet.theme.color#rgb", 1819 ], 1820 }, 1821 backgroundImage: { 1822 + type: "ref", 1823 + ref: "lex:pub.leaflet.theme.backgroundImage", 1824 + }, 1825 + pageWidth: { 1826 + type: "integer", 1827 + minimum: 320, 1828 + maximum: 1200, 1829 }, 1830 primary: { 1831 + type: "union", 1832 refs: [ 1833 + "lex:pub.leaflet.theme.color#rgba", 1834 + "lex:pub.leaflet.theme.color#rgb", 1835 ], 1836 }, 1837 pageBackground: { 1838 + type: "union", 1839 refs: [ 1840 + "lex:pub.leaflet.theme.color#rgba", 1841 + "lex:pub.leaflet.theme.color#rgb", 1842 ], 1843 }, 1844 showPageBackground: { 1845 + type: "boolean", 1846 default: false, 1847 }, 1848 accentBackground: { 1849 + type: "union", 1850 refs: [ 1851 + "lex:pub.leaflet.theme.color#rgba", 1852 + "lex:pub.leaflet.theme.color#rgb", 1853 ], 1854 }, 1855 accentText: { 1856 + type: "union", 1857 refs: [ 1858 + "lex:pub.leaflet.theme.color#rgba", 1859 + "lex:pub.leaflet.theme.color#rgb", 1860 ], 1861 }, 1862 }, ··· 1865 }, 1866 PubLeafletRichtextFacet: { 1867 lexicon: 1, 1868 + id: "pub.leaflet.richtext.facet", 1869 defs: { 1870 main: { 1871 + type: "object", 1872 + description: "Annotation of a sub-string within rich text.", 1873 + required: ["index", "features"], 1874 properties: { 1875 index: { 1876 + type: "ref", 1877 + ref: "lex:pub.leaflet.richtext.facet#byteSlice", 1878 }, 1879 features: { 1880 + type: "array", 1881 items: { 1882 + type: "union", 1883 refs: [ 1884 + "lex:pub.leaflet.richtext.facet#link", 1885 + "lex:pub.leaflet.richtext.facet#didMention", 1886 + "lex:pub.leaflet.richtext.facet#atMention", 1887 + "lex:pub.leaflet.richtext.facet#code", 1888 + "lex:pub.leaflet.richtext.facet#highlight", 1889 + "lex:pub.leaflet.richtext.facet#underline", 1890 + "lex:pub.leaflet.richtext.facet#strikethrough", 1891 + "lex:pub.leaflet.richtext.facet#id", 1892 + "lex:pub.leaflet.richtext.facet#bold", 1893 + "lex:pub.leaflet.richtext.facet#italic", 1894 ], 1895 }, 1896 }, 1897 }, 1898 }, 1899 byteSlice: { 1900 + type: "object", 1901 description: 1902 + "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.", 1903 + required: ["byteStart", "byteEnd"], 1904 properties: { 1905 byteStart: { 1906 + type: "integer", 1907 minimum: 0, 1908 }, 1909 byteEnd: { 1910 + type: "integer", 1911 minimum: 0, 1912 }, 1913 }, 1914 }, 1915 link: { 1916 + type: "object", 1917 description: 1918 + "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.", 1919 + required: ["uri"], 1920 properties: { 1921 uri: { 1922 + type: "string", 1923 }, 1924 }, 1925 }, 1926 didMention: { 1927 + type: "object", 1928 + description: "Facet feature for mentioning a did.", 1929 + required: ["did"], 1930 properties: { 1931 did: { 1932 + type: "string", 1933 + format: "did", 1934 }, 1935 }, 1936 }, 1937 atMention: { 1938 + type: "object", 1939 + description: "Facet feature for mentioning an AT URI.", 1940 + required: ["atURI"], 1941 properties: { 1942 atURI: { 1943 + type: "string", 1944 + format: "uri", 1945 }, 1946 }, 1947 }, 1948 code: { 1949 + type: "object", 1950 + description: "Facet feature for inline code.", 1951 required: [], 1952 properties: {}, 1953 }, 1954 highlight: { 1955 + type: "object", 1956 + description: "Facet feature for highlighted text.", 1957 required: [], 1958 properties: {}, 1959 }, 1960 underline: { 1961 + type: "object", 1962 + description: "Facet feature for underline markup", 1963 required: [], 1964 properties: {}, 1965 }, 1966 strikethrough: { 1967 + type: "object", 1968 + description: "Facet feature for strikethrough markup", 1969 required: [], 1970 properties: {}, 1971 }, 1972 id: { 1973 + type: "object", 1974 description: 1975 + "Facet feature for an identifier. Used for linking to a segment", 1976 required: [], 1977 properties: { 1978 id: { 1979 + type: "string", 1980 }, 1981 }, 1982 }, 1983 bold: { 1984 + type: "object", 1985 + description: "Facet feature for bold text", 1986 required: [], 1987 properties: {}, 1988 }, 1989 italic: { 1990 + type: "object", 1991 + description: "Facet feature for italic text", 1992 required: [], 1993 properties: {}, 1994 }, ··· 1996 }, 1997 PubLeafletThemeBackgroundImage: { 1998 lexicon: 1, 1999 + id: "pub.leaflet.theme.backgroundImage", 2000 defs: { 2001 main: { 2002 + type: "object", 2003 + required: ["image"], 2004 properties: { 2005 image: { 2006 + type: "blob", 2007 + accept: ["image/*"], 2008 maxSize: 1000000, 2009 }, 2010 width: { 2011 + type: "integer", 2012 }, 2013 repeat: { 2014 + type: "boolean", 2015 }, 2016 }, 2017 }, ··· 2019 }, 2020 PubLeafletThemeColor: { 2021 lexicon: 1, 2022 + id: "pub.leaflet.theme.color", 2023 defs: { 2024 rgba: { 2025 + type: "object", 2026 + required: ["r", "g", "b", "a"], 2027 properties: { 2028 r: { 2029 + type: "integer", 2030 maximum: 255, 2031 minimum: 0, 2032 }, 2033 g: { 2034 + type: "integer", 2035 maximum: 255, 2036 minimum: 0, 2037 }, 2038 b: { 2039 + type: "integer", 2040 maximum: 255, 2041 minimum: 0, 2042 }, 2043 a: { 2044 + type: "integer", 2045 maximum: 100, 2046 minimum: 0, 2047 }, 2048 }, 2049 }, 2050 rgb: { 2051 + type: "object", 2052 + required: ["r", "g", "b"], 2053 properties: { 2054 r: { 2055 + type: "integer", 2056 maximum: 255, 2057 minimum: 0, 2058 }, 2059 g: { 2060 + type: "integer", 2061 maximum: 255, 2062 minimum: 0, 2063 }, 2064 b: { 2065 + type: "integer", 2066 maximum: 255, 2067 minimum: 0, 2068 }, ··· 2070 }, 2071 }, 2072 }, 2073 + } as const satisfies Record<string, LexiconDoc>; 2074 + export const schemas = Object.values(schemaDict) satisfies LexiconDoc[]; 2075 + export const lexicons: Lexicons = new Lexicons(schemas); 2076 2077 export function validate<T extends { $type: string }>( 2078 v: unknown, 2079 id: string, 2080 hash: string, 2081 requiredType: true, 2082 + ): ValidationResult<T>; 2083 export function validate<T extends { $type?: string }>( 2084 v: unknown, 2085 id: string, 2086 hash: string, 2087 requiredType?: false, 2088 + ): ValidationResult<T>; 2089 export function validate( 2090 v: unknown, 2091 id: string, ··· 2097 : { 2098 success: false, 2099 error: new ValidationError( 2100 + `Must be an object with "${hash === "main" ? id : `${id}#${hash}`}" $type property`, 2101 ), 2102 + }; 2103 } 2104 2105 export const ids = { 2106 + AppBskyActorProfile: "app.bsky.actor.profile", 2107 + ComAtprotoLabelDefs: "com.atproto.label.defs", 2108 + ComAtprotoRepoApplyWrites: "com.atproto.repo.applyWrites", 2109 + ComAtprotoRepoCreateRecord: "com.atproto.repo.createRecord", 2110 + ComAtprotoRepoDefs: "com.atproto.repo.defs", 2111 + ComAtprotoRepoDeleteRecord: "com.atproto.repo.deleteRecord", 2112 + ComAtprotoRepoDescribeRepo: "com.atproto.repo.describeRepo", 2113 + ComAtprotoRepoGetRecord: "com.atproto.repo.getRecord", 2114 + ComAtprotoRepoImportRepo: "com.atproto.repo.importRepo", 2115 + ComAtprotoRepoListMissingBlobs: "com.atproto.repo.listMissingBlobs", 2116 + ComAtprotoRepoListRecords: "com.atproto.repo.listRecords", 2117 + ComAtprotoRepoPutRecord: "com.atproto.repo.putRecord", 2118 + ComAtprotoRepoStrongRef: "com.atproto.repo.strongRef", 2119 + ComAtprotoRepoUploadBlob: "com.atproto.repo.uploadBlob", 2120 + PubLeafletBlocksBlockquote: "pub.leaflet.blocks.blockquote", 2121 + PubLeafletBlocksBskyPost: "pub.leaflet.blocks.bskyPost", 2122 + PubLeafletBlocksButton: "pub.leaflet.blocks.button", 2123 + PubLeafletBlocksCode: "pub.leaflet.blocks.code", 2124 + PubLeafletBlocksHeader: "pub.leaflet.blocks.header", 2125 + PubLeafletBlocksHorizontalRule: "pub.leaflet.blocks.horizontalRule", 2126 + PubLeafletBlocksIframe: "pub.leaflet.blocks.iframe", 2127 + PubLeafletBlocksImage: "pub.leaflet.blocks.image", 2128 + PubLeafletBlocksMath: "pub.leaflet.blocks.math", 2129 + PubLeafletBlocksPage: "pub.leaflet.blocks.page", 2130 + PubLeafletBlocksPoll: "pub.leaflet.blocks.poll", 2131 + PubLeafletBlocksText: "pub.leaflet.blocks.text", 2132 + PubLeafletBlocksUnorderedList: "pub.leaflet.blocks.unorderedList", 2133 + PubLeafletBlocksWebsite: "pub.leaflet.blocks.website", 2134 + PubLeafletComment: "pub.leaflet.comment", 2135 + PubLeafletDocument: "pub.leaflet.document", 2136 + PubLeafletGraphSubscription: "pub.leaflet.graph.subscription", 2137 + PubLeafletPagesCanvas: "pub.leaflet.pages.canvas", 2138 + PubLeafletPagesLinearDocument: "pub.leaflet.pages.linearDocument", 2139 + PubLeafletPollDefinition: "pub.leaflet.poll.definition", 2140 + PubLeafletPollVote: "pub.leaflet.poll.vote", 2141 + PubLeafletPublication: "pub.leaflet.publication", 2142 + PubLeafletRichtextFacet: "pub.leaflet.richtext.facet", 2143 + PubLeafletThemeBackgroundImage: "pub.leaflet.theme.backgroundImage", 2144 + PubLeafletThemeColor: "pub.leaflet.theme.color", 2145 + } as const;
+41 -36
lexicons/api/types/pub/leaflet/publication.ts
··· 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 11 const is$typed = _is$typed, 12 - validate = _validate 13 - const id = 'pub.leaflet.publication' 14 15 export interface Record { 16 - $type: 'pub.leaflet.publication' 17 - name: string 18 - base_path?: string 19 - description?: string 20 - icon?: BlobRef 21 - theme?: Theme 22 - preferences?: Preferences 23 - [k: string]: unknown 24 } 25 26 - const hashRecord = 'main' 27 28 export function isRecord<V>(v: V) { 29 - return is$typed(v, id, hashRecord) 30 } 31 32 export function validateRecord<V>(v: V) { 33 - return validate<Record & V>(v, id, hashRecord, true) 34 } 35 36 export interface Preferences { 37 - $type?: 'pub.leaflet.publication#preferences' 38 - showInDiscover: boolean 39 - showComments: boolean 40 } 41 42 - const hashPreferences = 'preferences' 43 44 export function isPreferences<V>(v: V) { 45 - return is$typed(v, id, hashPreferences) 46 } 47 48 export function validatePreferences<V>(v: V) { 49 - return validate<Preferences & V>(v, id, hashPreferences) 50 } 51 52 export interface Theme { 53 - $type?: 'pub.leaflet.publication#theme' 54 backgroundColor?: 55 | $Typed<PubLeafletThemeColor.Rgba> 56 | $Typed<PubLeafletThemeColor.Rgb> 57 - | { $type: string } 58 - backgroundImage?: PubLeafletThemeBackgroundImage.Main 59 primary?: 60 | $Typed<PubLeafletThemeColor.Rgba> 61 | $Typed<PubLeafletThemeColor.Rgb> 62 - | { $type: string } 63 pageBackground?: 64 | $Typed<PubLeafletThemeColor.Rgba> 65 | $Typed<PubLeafletThemeColor.Rgb> 66 - | { $type: string } 67 - showPageBackground: boolean 68 accentBackground?: 69 | $Typed<PubLeafletThemeColor.Rgba> 70 | $Typed<PubLeafletThemeColor.Rgb> 71 - | { $type: string } 72 accentText?: 73 | $Typed<PubLeafletThemeColor.Rgba> 74 | $Typed<PubLeafletThemeColor.Rgb> 75 - | { $type: string } 76 } 77 78 - const hashTheme = 'theme' 79 80 export function isTheme<V>(v: V) { 81 - return is$typed(v, id, hashTheme) 82 } 83 84 export function validateTheme<V>(v: V) { 85 - return validate<Theme & V>(v, id, hashTheme) 86 }
··· 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 PubLeafletThemeColor from "./theme/color"; 13 + import type * as PubLeafletThemeBackgroundImage from "./theme/backgroundImage"; 14 15 const is$typed = _is$typed, 16 + validate = _validate; 17 + const id = "pub.leaflet.publication"; 18 19 export interface Record { 20 + $type: "pub.leaflet.publication"; 21 + name: string; 22 + base_path?: string; 23 + description?: string; 24 + icon?: BlobRef; 25 + theme?: Theme; 26 + preferences?: Preferences; 27 + [k: string]: unknown; 28 } 29 30 + const hashRecord = "main"; 31 32 export function isRecord<V>(v: V) { 33 + return is$typed(v, id, hashRecord); 34 } 35 36 export function validateRecord<V>(v: V) { 37 + return validate<Record & V>(v, id, hashRecord, true); 38 } 39 40 export interface Preferences { 41 + $type?: "pub.leaflet.publication#preferences"; 42 + showInDiscover: boolean; 43 + showComments: boolean; 44 } 45 46 + const hashPreferences = "preferences"; 47 48 export function isPreferences<V>(v: V) { 49 + return is$typed(v, id, hashPreferences); 50 } 51 52 export function validatePreferences<V>(v: V) { 53 + return validate<Preferences & V>(v, id, hashPreferences); 54 } 55 56 export interface Theme { 57 + $type?: "pub.leaflet.publication#theme"; 58 backgroundColor?: 59 | $Typed<PubLeafletThemeColor.Rgba> 60 | $Typed<PubLeafletThemeColor.Rgb> 61 + | { $type: string }; 62 + backgroundImage?: PubLeafletThemeBackgroundImage.Main; 63 + pageWidth?: number; 64 primary?: 65 | $Typed<PubLeafletThemeColor.Rgba> 66 | $Typed<PubLeafletThemeColor.Rgb> 67 + | { $type: string }; 68 pageBackground?: 69 | $Typed<PubLeafletThemeColor.Rgba> 70 | $Typed<PubLeafletThemeColor.Rgb> 71 + | { $type: string }; 72 + showPageBackground: boolean; 73 accentBackground?: 74 | $Typed<PubLeafletThemeColor.Rgba> 75 | $Typed<PubLeafletThemeColor.Rgb> 76 + | { $type: string }; 77 accentText?: 78 | $Typed<PubLeafletThemeColor.Rgba> 79 | $Typed<PubLeafletThemeColor.Rgb> 80 + | { $type: string }; 81 } 82 83 + const hashTheme = "theme"; 84 85 export function isTheme<V>(v: V) { 86 + return is$typed(v, id, hashTheme); 87 } 88 89 export function validateTheme<V>(v: V) { 90 + return validate<Theme & V>(v, id, hashTheme); 91 }
+44
lexicons/fix-extensions.ts
···
··· 1 + import * as fs from "fs"; 2 + import * as path from "path"; 3 + 4 + /** 5 + * Recursively processes all files in a directory and removes .js extensions from imports 6 + */ 7 + function fixExtensionsInDirectory(dir: string): void { 8 + const entries = fs.readdirSync(dir, { withFileTypes: true }); 9 + 10 + for (const entry of entries) { 11 + const fullPath = path.join(dir, entry.name); 12 + 13 + if (entry.isDirectory()) { 14 + fixExtensionsInDirectory(fullPath); 15 + } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))) { 16 + fixExtensionsInFile(fullPath); 17 + } 18 + } 19 + } 20 + 21 + /** 22 + * Removes .js extensions from import/export statements in a file 23 + */ 24 + function fixExtensionsInFile(filePath: string): void { 25 + const content = fs.readFileSync(filePath, "utf-8"); 26 + const fixedContent = content.replace(/\.js'/g, "'"); 27 + 28 + if (content !== fixedContent) { 29 + fs.writeFileSync(filePath, fixedContent, "utf-8"); 30 + console.log(`Fixed: ${filePath}`); 31 + } 32 + } 33 + 34 + // Get the directory to process from command line arguments 35 + const targetDir = process.argv[2] || "./lexicons/api"; 36 + 37 + if (!fs.existsSync(targetDir)) { 38 + console.error(`Directory not found: ${targetDir}`); 39 + process.exit(1); 40 + } 41 + 42 + console.log(`Fixing extensions in: ${targetDir}`); 43 + fixExtensionsInDirectory(targetDir); 44 + console.log("Done!");
+8 -7
lexicons/pub/leaflet/publication.json
··· 8 "description": "Record declaring a publication", 9 "record": { 10 "type": "object", 11 - "required": [ 12 - "name" 13 - ], 14 "properties": { 15 "name": { 16 "type": "string", ··· 25 }, 26 "icon": { 27 "type": "blob", 28 - "accept": [ 29 - "image/*" 30 - ], 31 "maxSize": 1000000 32 }, 33 "theme": { ··· 68 "type": "ref", 69 "ref": "pub.leaflet.theme.backgroundImage" 70 }, 71 "primary": { 72 "type": "union", 73 "refs": [ ··· 103 } 104 } 105 } 106 - }
··· 8 "description": "Record declaring a publication", 9 "record": { 10 "type": "object", 11 + "required": ["name"], 12 "properties": { 13 "name": { 14 "type": "string", ··· 23 }, 24 "icon": { 25 "type": "blob", 26 + "accept": ["image/*"], 27 "maxSize": 1000000 28 }, 29 "theme": { ··· 64 "type": "ref", 65 "ref": "pub.leaflet.theme.backgroundImage" 66 }, 67 + "pageWidth": { 68 + "type": "integer", 69 + "minimum": 0, 70 + "maximum": 1600 71 + }, 72 "primary": { 73 "type": "union", 74 "refs": [ ··· 104 } 105 } 106 } 107 + }
+5
lexicons/src/publication.ts
··· 37 type: "ref", 38 ref: PubLeafletThemeBackgroundImage.id, 39 }, 40 primary: ColorUnion, 41 pageBackground: ColorUnion, 42 showPageBackground: { type: "boolean", default: false },
··· 37 type: "ref", 38 ref: PubLeafletThemeBackgroundImage.id, 39 }, 40 + pageWidth: { 41 + type: "integer", 42 + minimum: 0, 43 + maximum: 1600, 44 + }, 45 primary: ColorUnion, 46 pageBackground: ColorUnion, 47 showPageBackground: { type: "boolean", default: false },
+1 -1
package.json
··· 7 "dev": "TZ=UTC next dev --turbo", 8 "publish-lexicons": "tsx lexicons/publish.ts", 9 "generate-db-types": "supabase gen types --local > supabase/database.types.ts && drizzle-kit introspect && rm -rf ./drizzle/*.sql ./drizzle/meta", 10 - "lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/document.json ./lexicons/pub/leaflet/comment.json ./lexicons/pub/leaflet/publication.json ./lexicons/pub/leaflet/*/* ./lexicons/com/atproto/*/* ./lexicons/app/bsky/*/* --yes && find './lexicons/api' -type f -exec sed -i 's/\\.js'/'/g' {} \\;", 11 "wrangler-dev": "wrangler dev", 12 "build-appview": "esbuild appview/index.ts --outfile=appview/dist/index.js --bundle --platform=node", 13 "build-feed-service": "esbuild feeds/index.ts --outfile=feeds/dist/index.js --bundle --platform=node",
··· 7 "dev": "TZ=UTC next dev --turbo", 8 "publish-lexicons": "tsx lexicons/publish.ts", 9 "generate-db-types": "supabase gen types --local > supabase/database.types.ts && drizzle-kit introspect && rm -rf ./drizzle/*.sql ./drizzle/meta", 10 + "lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/document.json ./lexicons/pub/leaflet/comment.json ./lexicons/pub/leaflet/publication.json ./lexicons/pub/leaflet/*/* ./lexicons/com/atproto/*/* ./lexicons/app/bsky/*/* --yes && tsx ./lexicons/fix-extensions.ts ./lexicons/api", 11 "wrangler-dev": "wrangler dev", 12 "build-appview": "esbuild appview/index.ts --outfile=appview/dist/index.js --bundle --platform=node", 13 "build-feed-service": "esbuild feeds/index.ts --outfile=feeds/dist/index.js --bundle --platform=node",
+4
src/replicache/attributes.ts
··· 195 type: "boolean", 196 cardinality: "one", 197 }, 198 "theme/page-background": { 199 type: "color", 200 cardinality: "one",
··· 195 type: "boolean", 196 cardinality: "one", 197 }, 198 + "theme/page-width": { 199 + type: "number", 200 + cardinality: "one", 201 + }, 202 "theme/page-background": { 203 type: "color", 204 cardinality: "one",