Skip to content

RhysSullivan/typlit

Repository files navigation

Typlit

A TypeScript/JSX library for building Tidbyt apps. Replaces Pixlet's Starlark syntax with familiar JSX components.

Warning: This project is experimental. The README and codebase are AI-generated. Expect breaking changes as the API and developer experience are still being figured out.

Note: Tidbyt devices are no longer being sold. If you're looking for hardware, Tronbyt appears to be a promising self-hosted alternative (I have purchased one and once I get it I will update this README with my experience):

Features

  • JSX Syntax - Write Tidbyt apps using React-like components
  • Type-Safe - Full TypeScript support with typed props and configs
  • 1:1 API Compatibility - All Pixlet widgets available as JSX components
  • Schema System - Type-safe configuration with Zod validation
  • Animation Support - Marquee, Animation, Sequence, and frame-based rendering
  • Built-in Fonts - BDF font rendering with all standard Tidbyt fonts
  • Dev Server - Hot-reloading preview server with WebGL rendering

Installation

bun add typlit

Requires Bun runtime.

Quick Start

import { Root, Box, Column, Text } from "typlit";

export default function HelloWorld() {
  return (
    <Root delay={500}>
      <Box color="#111">
        <Column mainAlign="center" crossAlign="center" expanded>
          <Text content="Hello" color="#0ff" />
          <Text content="World!" color="#f80" />
        </Column>
      </Box>
    </Root>
  );
}

Running Locally

# Start the dev server with hot reload
bun run dev

# Run tests
bun test

The dev server runs at http://localhost:3456 and provides a WebGL preview of your app.

Widgets

All Pixlet render widgets are available:

Widget Description
Root Root container (64x32), sets frame delay
Box Container with background color, centers children
Row Horizontal layout
Column Vertical layout
Stack Z-axis stacking (overlapping layers)
Text Single-line text with font support
WrappedText Multi-line wrapped text
Image PNG/GIF image rendering
Marquee Scrolling text/content animation
Animation Frame-based animation from children
Padding Add padding around content
Circle Draw circles
Plot Line/scatter plots
Sequence Multi-page presentations with transitions
PixelCanvas Direct pixel manipulation

Layout Props

Row and Column support alignment:

<Row mainAlign="center" crossAlign="center" expanded>
  {/* mainAlign: start, center, end, space_between, space_around, space_evenly */}
  {/* crossAlign: start, center, end */}
</Row>

Animation Example

import { Root, Animation, Text } from "typlit";

export default function BlinkingText() {
  return (
    <Root delay={500}>
      <Animation>
        <Text content="ON" color="#0f0" />
        <Text content="OFF" color="#f00" />
      </Animation>
    </Root>
  );
}

Marquee Example

import { Root, Marquee, Text } from "typlit";

export default function ScrollingText() {
  return (
    <Root>
      <Marquee width={64} speed={0.5}>
        <Text content="This text scrolls across the screen!" color="#fff" />
      </Marquee>
    </Root>
  );
}

Schema System

Define type-safe configuration for your apps:

import { Root, Text, schema, text, toggle, dropdown, color, z } from "typlit";

// Define the schema
export const configSchema = schema({
  message: text({ name: "Message", default: "Hello!" }),
  showIcon: toggle({ name: "Show Icon", default: true }),
  theme: dropdown({
    name: "Theme",
    options: [
      { display: "Light", value: "light" },
      { display: "Dark", value: "dark" },
    ] as const,
    default: "dark",
  }),
  textColor: color({ name: "Text Color", default: "#FFFFFF" }),
});

// Infer the config type
type Config = z.infer<typeof configSchema>;

export function MyApp({ config }: { config: Config }) {
  return (
    <Root>
      <Text content={config.message} color={config.textColor} />
    </Root>
  );
}

Available schema fields:

  • text() - Text input
  • toggle() - Boolean toggle
  • dropdown() - Select from options
  • color() - Color picker with optional palette
  • datetime() - Date/time picker
  • location() - Location picker (returns lat/lng/timezone)
  • photo() - Image upload (base64)
  • duration() - Duration picker (e.g., "2s", "500ms")

Fonts

Built-in fonts from Tidbyt:

  • tb-8 - Default 8px font
  • tom-thumb - Tiny 4px font
  • 6x13 - Larger readable font
  • 5x8 - Compact font
  • Dina_r400-6 - Clean sans-serif
<Text content="Hello" font="tom-thumb" color="#fff" />

Load fonts before rendering:

import { loadBuiltinFonts, render } from "typlit";

await loadBuiltinFonts();
const frames = await render(<MyApp />);

Rendering

To Image Data

import { render, loadBuiltinFonts } from "typlit";

await loadBuiltinFonts();
const frames = await render(<MyApp />);
// frames is an array of PixelBuffer objects

ASCII Debug Output

import { render, printBuffer, loadBuiltinFonts } from "typlit";

await loadBuiltinFonts();
const frames = await render(<MyApp />);
printBuffer(frames[0], { title: "My App" });

Examples

See the examples/ directory for complete apps:

  • hello-world.tsx - Basic layout example
  • github-stargazer.tsx - API integration with marquee
  • configurable-clock.tsx - Schema system demo
  • beat-time.tsx - Swatch Internet Time display
  • game-of-life.tsx - Conway's Game of Life
  • tetris-clock.tsx - Animated tetris-style clock
  • plasma-wave.tsx - PixelCanvas animation demo

Project Structure

src/
├── core/
│   ├── PixelBuffer.ts    # RGBA pixel buffer
│   ├── render.ts         # JSX tree resolution
│   ├── color.ts          # Color parsing
│   └── types.ts          # Shared types
├── fonts/
│   ├── parser.ts         # BDF font parser
│   └── builtin.ts        # Built-in font loading
├── widgets/              # All widget implementations
├── server/               # Dev server with API
├── preview/              # Browser preview renderer
├── schema.ts             # Configuration schema system
└── index.ts              # Main exports

Porting from Pixlet

Typlit aims for 1:1 API compatibility with Pixlet. Here's a conversion example:

Pixlet (Starlark):

load("render.star", "render")

def main():
    return render.Root(
        child = render.Box(
            render.Row(
                children = [
                    render.Text("Hello"),
                    render.Text("World"),
                ],
            ),
        ),
    )

Typlit (JSX):

import { Root, Box, Row, Text } from "typlit";

export default function Main() {
  return (
    <Root>
      <Box>
        <Row>
          <Text content="Hello" />
          <Text content="World" />
        </Row>
      </Box>
    </Root>
  );
}

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published