trmnlp Development Tool

repository·main·Indexed 42 days ago

https://github.com/usetrmnl/trmnlp

A development tool and local web server for building, previewing, and deploying TRMNL plugins. It supports Liquid templates, the TRMNL Design System, and serverless transforms in Python, Ruby, PHP, and Node.js. Features include local rendering to HTML and PNG, OAuth2 flow simulation, and CLI commands for scaffolding, linting, and syncing plugins with the TRMNL server.

Tokens
11.1K
Snippets
42
Records
59
Agent score
54%

What's inside trmnlp

  1. Use Serverless Transforms to process API responses

    main

    A transform script allows you to run code (python, ruby, php, or node) against a polled API response before it reaches your Liquid templates. This matches the hosted plugin service's behavior.

    Implementation

    1. Create a file at src/transform.{py,rb,php,js}.
    2. Define a run(input) function. The function receives the polled response as JSON via stdin and returns the new data object.

    Configuration

    • Enabled by default: Transforms run automatically if the file exists.
    • Disable transforms: Set transform_runtime: disabled in .trmnlp.yml.
    • Remote Daemon: To test against a real microVM daemon, set serverless_daemon_url: in .trmnlp.yml and provide the token via the $TRMNL_SERVERLESS_DAEMON_API_KEY environment variable.

    Language Detection

    The language is determined by the file extension:

    • src/transform.py -> python
    • src/transform.rb -> ruby
    • src/transform.js -> node
    • src/transform.php -> php
    # src/transform.py
    def run(input):
        return {"items": [x["title"] for x in input["data"]]}
    # .trmnlp.yml
    transform_runtime: enabled
    serverless_daemon_url: https://transforms.your-team.example
    export TRMNL_SERVERLESS_DAEMON_API_KEY=...
    trmnlp serve
  2. Understand the trmnlp plugin project structure

    main

    A plugin project follows a specific directory structure. Key files include:

    • .trmnlp.yml: Local development server configuration (not uploaded to TRMNL).
    • src/settings.yml: Plugin configuration (uploaded to TRMNL). Note: This file must contain an id to update an existing plugin instead of creating a new one on every push.
    • src/full.liquid: Markup for the full screen.
    • src/half_horizontal.liquid: Top or bottom half of a stacked mashup.
    • src/half_vertical.liquid: Left or right half of a side-by-side mashup.
    • src/quadrant.liquid: One quarter of a 2x2 mashup.
    • src/shared.liquid: Reusable markup included by other templates.
    • .github/workflows/trmnl.yml: GitHub Actions workflow for linting and deployment.
  3. Configure transform sandboxing and isolation

    main

    trmnlp provides two levels of isolation for executing plugin transforms:

    1. Standard Isolation: By default, trmnlp executes your transform via a Ruby subprocess inside the Docker container. The container itself acts as the primary isolation boundary.
    2. MicroVM Isolation (Stronger Sandboxing): For running untrusted plugin code, you can offload execution to a remote transform daemon. To do this, add the serverless_daemon_url key to your .trmnlp.yml file, pointing to your remote service URL.

    The wire format remains identical, so your transform code (e.g., transform.py) does not need to change when switching between local subprocess execution and a remote daemon.

  4. Configure OAuth2 for private plugins

    main

    If your plugin fetches data from a third-party API requiring authorization, you can run the OAuth2 authorization code flow locally using trmnlp serve. This allows you to preview the plugin with a real token. The token is injected into polling requests exactly as it is in the hosted service.

    1. Configure the provider

    Add the following keys to src/settings.yml. These settings are synced with the hosted service via trmnlp push and trmnlp pull.

    2. Set credentials locally

    OAuth app credentials must stay local and should be set as environment variables:

    3. Register the redirect URI

    In your OAuth provider's dashboard, register the following redirect URI: http://localhost:4567/oauth/callback (Note: match the port if running trmnlp serve on a different port).

    4. Connect

    Run trmnlp serve and open the preview. If not yet connected, click the Connect account banner to authorize in your browser. Tokens are stored in the local cache directory and refreshed automatically.

    # src/settings.yml
    oauth_enabled: "true"
    oauth_authorize_url: https://github.com/login/oauth/authorize
    oauth_token_url: https://github.com/login/oauth/access_token
    oauth_scopes: "read:user user:email"
    oauth_pkce_enabled: "true"
    # oauth_scope_separator: " "
    # oauth_refresh_url: https://...
    export TRMNL_OAUTH_CLIENT_ID=your-oauth-app-client-id
    export TRMNL_OAUTH_CLIENT_SECRET=your-oauth-app-client-secret
    http://localhost:4567/oauth/callback
  5. Run trmnlp via Docker

    main

    If you prefer not to install Ruby locally, you can run trmnlp using Docker.

    Standard execution

    Run the server by mounting your current directory to /plugin:

    docker run \
        --pull always \
        --publish 4567:4567 \
        --volume "$(pwd):/plugin" \
        trmnl/trmnlp serve \
        --bind 0.0.0.0

    Note: --bind 0.0.0.0 is required inside Docker to make the preview reachable from your host browser.

    Interactive Mode

    To run multiple commands (like login and clone) within a container, start an interactive shell and mount your local config directory to persist your API key:

    docker run -it \
        --pull always \
        --publish 4567:4567 \
        --volume "$HOME/.config/trmnlp:/root/.config/trmnlp" \
        --volume "$(pwd):/plugin" \
        --entrypoint /bin/bash \
        trmnl/trmnlp

    Docker Compose

    For a pre-configured setup, use a docker-compose.yml:

    services:
      trmnlp:
        image: trmnl/trmnlp
        pull_policy: always
        command: ["serve"]
        ports:
          - "4567:4567"
        volumes:
          - .:/plugin
  6. Run the Hacker News Top Stories example via Docker

    main

    To run the complete Hacker News Top Stories plugin locally, use Docker Compose. This will start the trmnlp environment, execute the polling strategy, and serve the rendered views.

    1. Navigate to the example directory:
      cd examples/hn-stories
    2. Start the services:
      docker compose up
    3. Access the full-screen view in your browser at: http://localhost:4567/full

    Note: The initial poll may take several seconds as it fetches the top story IDs and then performs individual HTTP requests to enrich each story's data.

    cd examples/hn-stories
    docker compose up
  7. Implement a serverless Python transform

    main

    trmnlp supports serverless transforms using Python. This allows you to enrich raw polled data (e.g., an array of IDs) with additional data from external APIs before it reaches your Liquid templates.

    The Transform Contract

    Your Python script must define a run(input) function. This function is the same code shape used in production.

    1. Input: The function receives an input dictionary containing the polled data under the input["data"] key.
    2. Logic: You can use standard libraries like urllib.request to make additional HTTPS calls to fetch detailed records.
    3. Output: The function must return a dictionary containing the cleaned, enriched data structure that your Liquid templates will iterate over.

    Configuration

    To use a Python transform, ensure your src/settings.yml includes:

    • serverless_language: python
    • strategy: polling (or your chosen strategy)

    To enable running these transforms in-process during local development, your .trmnlp.yml must have:

    • transform_runtime: enabled
  8. Install and Quick Start trmnlp

    main

    trmnlp is a self-hosted web server for developing and sharing TRMNL plugins. It renders Liquid templates using the TRMNL Design System as either HTML or PNG.

    Prerequisites

    • Ruby 4.0 or newer (check with ruby -v).
    • For PNG rendering: Firefox and ImageMagick must be installed.

    Installation via RubyGems

    gem install trmnl_preview

    Quick Start Workflow

    1. Scaffold a new project: trmnlp init my_plugin
    2. Enter the directory: cd my_plugin
    3. Start the preview server: trmnlp serve (available at http://localhost:4567)

    If you do not have Ruby installed, you can use Docker instead.

    gem install trmnl_preview
    trmnlp init my_plugin
    cd my_plugin
    trmnlp serve
  9. Create and manage TRMNL plugins

    main

    Starting a new plugin

    Use trmnlp init [NAME] to scaffold a new project, then use trmnlp serve to develop locally and trmnlp push to upload to the TRMNL server.

    Working with existing plugins

    If you created a plugin via the web editor, you can bring it to your local environment:

    1. Authenticate: trmnlp login
    2. Download: trmnlp clone [NAME] [ID]
    3. Develop: trmnlp serve
    4. Upload changes: trmnlp push

    Alternatively, you can use trmnlp pull to download the latest settings from the TRMNL server.

    # New plugin
    trmnlp init my_plugin
    cd my_plugin
    trmnlp serve
    trmnlp login
    trmnlp push
    
    # Existing plugin
    trmnlp login
    trmnlp clone my_plugin 12345
    cd my_plugin
    trmnlp serve
    trmnlp push
  10. Run trmnlp from source

    main

    To run trmnlp from a local checkout (for contributing or testing unreleased changes):

    1. Clone the repository.
    2. Install dependencies using bundle install.
    3. Run the CLI using bundle exec bin/trmnlp serve.
    git clone https://github.com/usetrmnl/trmnlp.git
    cd trmnlp
    bundle install
    bundle exec bin/trmnlp serve