Skip to main content

A lightweight local web UI for editing custom DSLs with live HTML preview

Project description

HeyDSL

HeyDSL is a lightweight local web UI for editing custom DSLs with live HTML preview.

If you've built a domain-specific language that produces visual output (diagrams, formatted text, HTML, or graphics), HeyDSL gives you an interactive editor to develop and test it.

Give HeyDSL a preview function, a compile function, and syntax highlighting rules, and it provides:

  • Live preview as you type
  • Syntax highlighting for your DSL
  • Compile & save output to disk

It's designed for local development and can be deployed in your own applications.

Installation

pip install heydsl

Quick Start

from heydsl import DSLDefinition, HeyDSLApp, Syntax, LineCommentStyle

def preview_fn(code: str) -> str:
    """Convert DSL to HTML for preview"""
    return f"<pre>{code}</pre>"

def compile_fn(code: str) -> bytes:
    """Compile DSL to output format"""
    return code.encode()

app = HeyDSLApp(
    DSLDefinition(
        syntax=Syntax.from_lists(
            name="MyDSL",
            keywords=["if", "else", "function"],
            line_comment_style=LineCommentStyle.HASH,
        ),
        preview_fn=preview_fn,
        compile_fn=compile_fn,
        sample_code="# Hello World",
    )
)
app.run()

Visit http://127.0.0.1:5000 in your browser to start editing.

Features

  • Live preview: See output as you edit with no refresh needed
  • Custom syntax highlighting: Built-in support for CodeMirror 5; define your syntax highlighting rules via keyword lists or custom JavaScript
  • Compile & save: Built-in file dialogs for saving output, or bring your own handlers
  • Zero setup: Single Python app instance is the entire server
  • Customizable UI: Swap editor themes, header text, and assets
  • Local-first: Runs on localhost by default; deploy with your app if needed

Configuration

Defining Syntax

Create syntax highlighting in two ways:

From keyword/type/operator lists:

syntax = Syntax.from_lists(
    name="MyDSL",
    keywords=["if", "else", "for", "return"],
    types=["int", "string", "bool"],
    operators=["+", "-", "*", "/", "=", "=="],
    line_comment_style=LineCommentStyle.HASH,
)

From a custom CodeMirror 5 mode file:

syntax = Syntax.from_file("MyDSL", Path("my_mode.js"))

If creating from a file, make sure the name matches the mode name within the file.

Customizing the UI

from heydsl import UIConfig, curated_cm5_themes

ui = UIConfig(
    header_text="My DSL Editor",
    code_themes=curated_cm5_themes(),  # or provide your own
)

app = HeyDSLApp(dsl_definition, ui_config=ui)

Save Handlers

By default, saving opens a file dialog. You can customize this:

from heydsl import save_file, save_compiled
from datetime import datetime

def custom_save(code: str) -> str:
    """Custom save handler with timestamp."""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    # Use the built-in helper
    return save_file(code, default_name=f"my_dsl_{timestamp}.dsl")

def custom_compile_save(compiled: bytes) -> str:
    """Custom compiled save handler with timestamp."""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    # Use the built-in helper
    return save_compiled(compiled, default_name=f"output_{timestamp}.bin")

app = HeyDSLApp(
    DSLDefinition(
        syntax=my_syntax,
        preview_fn=my_preview_fn,
        compile_fn=my_compile_fn,
        save_fn=custom_save,
        save_compiled_fn=custom_compile_save,
    )
)

Or skip the dialogs entirely:

def save_to_disk(code: str) -> str:
    path = Path("/tmp/dsl_output.txt")
    path.write_text(code)
    return str(path)

app = HeyDSLApp(
    DSLDefinition(
        syntax=my_syntax,
        preview_fn=my_preview_fn,
        compile_fn=my_compile_fn,
        save_fn=save_to_disk,
    )
)

HTML Preview & Security

The clean_preview setting controls how preview HTML is handled.

When clean_preview=True (default):

  • Preview HTML is sanitized: only safe tags and attributes are allowed
  • Content Security Policy is restrictive: no scripts, forms, or external requests
  • Useful as a safety check while developing

When clean_preview=False:

  • Preview HTML is rendered as-is
  • CSP is permissive: scripts and forms can run
  • It is your responsibility to ensure preview output is safe

For local development tools with trusted input, disabling is normal:

app = HeyDSLApp(
    DSLDefinition(
        syntax=my_syntax,
        preview_fn=my_preview_fn,
        compile_fn=my_compile_fn,
        clean_preview=False,  # You control the preview output
    )
)

If you're running in a shared environment or accepting untrusted DSL code without sanitising it, keep it enabled.

Deploying HeyDSL

HeyDSL runs as a Flask app. You can:

  • Use the built-in run() method for development
  • Integrate with any WSGI server (Gunicorn, uWSGI) for production
  • Embed in a larger Flask/FastAPI application via the .app attribute
# Get the Flask app for WSGI deployment
flask_app = heydsl_app.app

API Reference

Core Classes

DSLDefinition

Configuration for your DSL. All fields except noted optional are required.

Field Type Description
syntax Syntax Syntax definition for editor highlighting
preview_fn Callable[[str], str] Function that converts code to HTML preview
compile_fn Callable[[str], bytes] Function that compiles code to output
save_fn Callable[[str], str] (Optional) Custom save handler; defaults to file dialog
save_compiled_fn Callable[[bytes], str] (Optional) Custom compiled save handler; defaults to file dialog
initial_file Path | None (Optional) File to load on startup
sample_code str (Optional) Code shown when no file is loaded
clean_preview bool (Optional) Enable HTML sanitization and strict CSP; default True

HeyDSLApp

Main application class.

HeyDSLApp(
    dsl_definition: DSLDefinition,
    ui_config: UIConfig = UIConfig(),
    server_config: ServerConfig = ServerConfig(),
    cm5_assets: list[ExternalAsset] = default_cm5_assets(),
)

Methods:

  • run(open_browser: bool = True) - Start the server and optionally open in browser

Attributes:

  • app - The underlying Flask application object

Syntax

Defines syntax highlighting rules.

Class Methods:

  • from_lists(name, line_comment_style, keywords, types=[], operators=[]) - Create from keyword/type/operator lists
  • from_file(name, path) - Load syntax definition from a JavaScript file

Attributes:

  • name - Display name for the syntax
  • definition - CodeMirror 5 mode JavaScript code

LineCommentStyle

Enum for comment syntax. Values: SLASH_SLASH (//), HASH (#), SEMI (;), PERCENT (%), NONE (disabled)

ServerConfig

Server settings.

ServerConfig(
    host: str = "127.0.0.1",
    port: int = 5000,
)

Methods:

  • address() - Return the full http://host:port address

UIConfig

UI customization.

UIConfig(
    header_text: str = "HeyDSL Editor",
    code_themes: dict[str, ExternalAsset] = curated_cm5_themes(),
)

ExternalAsset

External stylesheet or script to load.

ExternalAsset(
    type: AssetType,  # AssetType.STYLESHEET or AssetType.SCRIPT
    url: str,
    integrity: str | None = None,  # SRI hash
)

AssetType

Enum for asset types: STYLESHEET, SCRIPT

Utilities

save_file(code, default_name="code.txt", initial_dir=None, filetypes=None) -> str

Opens a save dialog and writes code to the selected file. Returns the file path. Use within custom save_fn handlers.

save_compiled(compiled_bytes, default_name="output.bin", initial_dir=None, filetypes=None) -> str

Opens a save dialog and writes compiled bytes to the selected file. Returns the file path. Use within custom save_compiled_fn handlers.

curated_cm5_themes() -> dict[str, ExternalAsset]

Returns popular CodeMirror 5 themes: cobalt, dracula, eclipse, material, monokai, solarized.

default_cm5_assets() -> list[ExternalAsset]

Returns CodeMirror 5 core and simple mode assets.

HTTP API

The Flask app exposes these endpoints (for advanced use):

  • GET / - Main editor UI
  • GET /syntax-def.js - CodeMirror syntax definition
  • POST /api/preview - Generate preview (JSON request: {"code": "..."})
  • POST /api/compile - Compile and save (JSON request: {"code": "..."})
  • POST /api/save-as - Save code (JSON request: {"code": "..."})

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

heydsl-1.0.0.tar.gz (30.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

heydsl-1.0.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file heydsl-1.0.0.tar.gz.

File metadata

  • Download URL: heydsl-1.0.0.tar.gz
  • Upload date:
  • Size: 30.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for heydsl-1.0.0.tar.gz
Algorithm Hash digest
SHA256 5311ed7298b1f36b95abbcc54eb688c2e3593a1038f05a4e9f72b2539d6c9cae
MD5 2d5f5e08b4ff171267ef8c143e48359d
BLAKE2b-256 52121a24c78546620b8c9e50dbe47bc364c35dd060facc83885160d81c357741

See more details on using hashes here.

File details

Details for the file heydsl-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: heydsl-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 16.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for heydsl-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 41443f1c01ae32a4dc5127b7ebea4d79dfe7e6a7b1d37a72ed33532e79d48922
MD5 ea399a2a0788c904884b2aa118b5466a
BLAKE2b-256 3d5918d53aebab9afe9aab292098d7f60a799572f562be34d2f7d786d389e10b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page