import { Nsid } from "@atcute/lexicons"; import { useLocation, useNavigate } from "@solidjs/router"; import { createEffect, For, Show } from "solid-js"; import { resolveLexiconAuthority } from "../utils/api.js"; interface LexiconSchema { lexicon: number; id: string; description?: string; defs: { [key: string]: LexiconDef; }; } interface LexiconDef { type: string; description?: string; key?: string; record?: LexiconObject; parameters?: LexiconObject; input?: { encoding: string; schema?: LexiconObject }; output?: { encoding: string; schema?: LexiconObject }; errors?: Array<{ name: string; description?: string }>; properties?: { [key: string]: LexiconProperty }; required?: string[]; nullable?: string[]; maxLength?: number; minLength?: number; maxGraphemes?: number; minGraphemes?: number; items?: LexiconProperty; refs?: string[]; closed?: boolean; enum?: string[]; const?: string; default?: any; minimum?: number; maximum?: number; accept?: string[]; maxSize?: number; knownValues?: string[]; format?: string; } interface LexiconObject { type: string; description?: string; ref?: string; refs?: string[]; closed?: boolean; properties?: { [key: string]: LexiconProperty }; required?: string[]; nullable?: string[]; } interface LexiconProperty { type: string; description?: string; ref?: string; refs?: string[]; closed?: boolean; format?: string; items?: LexiconProperty; minLength?: number; maxLength?: number; maxGraphemes?: number; minGraphemes?: number; minimum?: number; maximum?: number; enum?: string[]; const?: string | boolean | number; default?: any; knownValues?: string[]; accept?: string[]; maxSize?: number; } const TypeBadge = (props: { type: string; format?: string; refType?: string }) => { const navigate = useNavigate(); const displayType = props.refType ? props.refType.replace(/^#/, "") : props.format ? `${props.type}:${props.format}` : props.type; const isLocalRef = () => props.refType?.startsWith("#"); const isExternalRef = () => props.refType && !props.refType.startsWith("#"); const handleClick = async () => { if (isLocalRef()) { const defName = props.refType!.slice(1); window.history.replaceState(null, "", `#schema:${defName}`); const element = document.getElementById(`def-${defName}`); if (element) { element.scrollIntoView({ behavior: "instant", block: "start" }); } } else if (isExternalRef()) { try { const [nsid, anchor] = props.refType!.split("#"); const authority = await resolveLexiconAuthority(nsid as Nsid); const hash = anchor ? `#schema:${anchor}` : "#schema"; navigate(`/at://${authority}/com.atproto.lexicon.schema/${nsid}${hash}`); } catch (err) { console.error("Failed to resolve lexicon authority:", err); } } }; return ( <> {displayType} ); }; const UnionBadges = (props: { refs: string[] }) => (
{(refType) => }
); const ConstraintsList = (props: { property: LexiconProperty }) => (
minLength: {props.property.minLength} maxLength: {props.property.maxLength} maxGraphemes: {props.property.maxGraphemes} minGraphemes: {props.property.minGraphemes} min: {props.property.minimum} max: {props.property.maximum} maxSize: {props.property.maxSize} accept: [{props.property.accept!.join(", ")}] enum: [{props.property.enum!.join(", ")}] const: {props.property.const?.toString()} default: {JSON.stringify(props.property.default)} knownValues: [{props.property.knownValues!.join(", ")}] closed: true
); const PropertyRow = (props: { name: string; property: LexiconProperty; required?: boolean; hideNameType?: boolean; }) => { const hasConstraints = (property: LexiconProperty) => property.minLength !== undefined || property.maxLength !== undefined || property.maxGraphemes !== undefined || property.minGraphemes !== undefined || property.minimum !== undefined || property.maximum !== undefined || property.maxSize !== undefined || property.accept || property.enum || property.const || property.default !== undefined || property.knownValues || property.closed; return (
{props.name} union required
items: union

{props.property.description}

); }; const DefSection = (props: { name: string; def: LexiconDef }) => { const defTypeColor = () => { switch (props.def.type) { case "record": return "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300"; case "query": return "bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300"; case "procedure": return "bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300"; case "subscription": return "bg-pink-100 text-pink-800 dark:bg-pink-900/30 dark:text-pink-300"; case "object": return "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"; case "token": return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"; default: return "bg-neutral-200 text-neutral-800 dark:bg-neutral-700 dark:text-neutral-300"; } }; const hasDefContent = () => props.def.refs || props.def.minLength !== undefined || props.def.maxLength !== undefined || props.def.maxGraphemes !== undefined || props.def.minGraphemes !== undefined || props.def.minimum !== undefined || props.def.maximum !== undefined || props.def.maxSize !== undefined || props.def.accept || props.def.enum || props.def.const || props.def.default !== undefined || props.def.closed || props.def.items || props.def.knownValues; return (
{props.name === "main" ? "Main Definition" : props.name} {props.def.type}

{props.def.description}

{/* Record key */}
Record Key: {props.def.key}
{/* Properties (for record/object types) */} 0} >

Properties

{([name, property]) => ( )}
{/* Parameters (for query/procedure) */} 0 } >

Parameters

{([name, property]) => ( )}
{/* Input */}

Input

Encoding: {props.def.input!.encoding}
Schema:
Schema (union):
0 } >
{([name, property]) => ( )}
{/* Output */}

Output

Encoding: {props.def.output!.encoding}
Schema:
Schema (union):
0 } >
{([name, property]) => ( )}
{/* Errors */} 0}>

Errors

{(error) => (
{error.name}

{error.description}

)}
{/* Other Definitions */}
); }; export const LexiconSchemaView = (props: { schema: LexiconSchema }) => { const location = useLocation(); // Handle scrolling to a definition when hash is like #schema:definitionName createEffect(() => { const hash = location.hash; if (hash.startsWith("#schema:")) { const defName = hash.slice(8); requestAnimationFrame(() => { const element = document.getElementById(`def-${defName}`); if (element) element.scrollIntoView({ behavior: "instant", block: "start" }); }); } }); return (
{/* Header */}

{props.schema.id}

Lexicon version: {props.schema.lexicon}

{props.schema.description}

{/* Definitions */}
{([name, def]) => }
); };