@inlay/core#
Build element trees for Inlay, a UI component system for the AT Protocol.
Install#
npm install @inlay/core
Creating elements#
The $ function creates elements. Pass a string NSID as the type:
import { $ } from "@inlay/core";
const el = $("org.atsui.Stack", { gap: "small" },
$("org.atsui.Text", {}, "Hello"),
$("org.atsui.Text", {}, "world")
);
You can then turn this tree into JSON with serializeTree(el):
{
"$": "$",
"type": "org.atsui.Stack",
"props": {
"gap": "small",
"children": [
{
"$": "$",
"type": "org.atsui.Text",
"props": { "children": ["Hello"] },
"key": "0"
},
{
"$": "$",
"type": "org.atsui.Text",
"props": { "children": ["world"] },
"key": "1"
}
]
}
}
If the components you use are published lexicons, you can use the typed $ overload:
import { $ } from "@inlay/core";
import { Stack, Text } from "./generated/org/atsui";
$(Stack, { gap: "small" },
$(Text, {}, "Hello"),
$(Text, {}, "world")
);
Then their props will be checked.
JSX#
Set @inlay/core as the JSX import source in your tsconfig:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@inlay/core"
}
}
Then use Lexicon modules as tags:
import { jslex } from "@inlay/core";
import * as atsui from "./generated/org/atsui";
const { Stack, Text } = jslex(atsui);
<Stack gap="medium">
<Text>Hello world</Text>
</Stack>
HTTP imports#
In environments that support URL imports (Val Town, Deno), you can import any published Lexicons like this:
/* @jsxImportSource npm:@inlay/core */
import { Stack, Text } from "https://lex.val.run/org.atsui.*.ts";
<Stack gap="medium">
<Text>Hello world</Text>
</Stack>
Lexicons#
Components are defined by Lexicons — JSON schemas that declare props. For example, org.atsui.Avatar:
{
"lexicon": 1,
"id": "org.atsui.Avatar",
"defs": {
"main": {
"type": "procedure",
"description": "Circular profile image at a fixed size.",
"input": {
"encoding": "application/json",
"schema": {
"type": "object",
"required": ["src"],
"properties": {
"src": {
"type": "unknown",
"description": "Blob ref for the image."
},
"did": {
"type": "string",
"format": "did",
"description": "DID of the blob owner. Used to resolve blob URLs."
},
"size": {
"type": "string",
"maxLength": 32,
"description": "Size token.",
"knownValues": ["xsmall", "small", "medium", "large"],
"default": "medium"
}
}
}
},
"output": {
"encoding": "application/json",
"schema": {
"type": "ref",
"ref": "at.inlay.defs#response"
}
}
}
}
}
Use @atproto/lex to generate TypeScript from Lexicon files. For published lexicons, install from the network:
npm install @atproto/lex
npx lex install org.atsui.Stack org.atsui.Text
npx lex build --out generated
For your own lexicons, put .json files in a lexicons/ directory (e.g. lexicons/com/example/MyComponent.json) and point lex build at them:
npx lex build --lexicons lexicons --out generated
Serialization#
Convert element trees to/from JSON-safe representations:
import { serializeTree, deserializeTree } from "@inlay/core";
const json = serializeTree(elementTree);
const restored = deserializeTree(json);
API#
| Function | Description |
|---|---|
$(type, props?, ...children) |
Create an element from a Lexicon module or NSID string |
jslex(namespace) |
Cast Lexicon modules for use as JSX components (no-op at runtime) |
isValidElement(value) |
Type guard for Element |
serializeTree(tree, visitor?) |
Convert element tree to JSON-serializable form |
deserializeTree(tree, visitor?) |
Inverse of serializeTree |
walkTree(tree, mapObject) |
Recursive tree walker for plain objects |
resolveBindings(tree, resolve) |
Replace at.inlay.Binding elements using a resolver callback |
Types#
Element—{ type: string, key?: string, props?: Record<string, unknown> }LexiconComponent— Object with a$nsidfieldComponent<M>— A Lexicon module usable as a JSX componentElementVisitor—(element: Element) => unknown