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
localhostby 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
.appattribute
# 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 listsfrom_file(name, path)- Load syntax definition from a JavaScript file
Attributes:
name- Display name for the syntaxdefinition- 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 fullhttp://host:portaddress
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 UIGET /syntax-def.js- CodeMirror syntax definitionPOST /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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file heydsl-0.1.0.tar.gz.
File metadata
- Download URL: heydsl-0.1.0.tar.gz
- Upload date:
- Size: 29.8 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67331b181ccb955a315e187db80f944826ba3ff40f8a2964e5066ba223e1ca1e
|
|
| MD5 |
107913a646ca1229d4e0c8164e0e387b
|
|
| BLAKE2b-256 |
6d33adc6c8223682a84396963f7423cd4832128821847262de009db69ea7cafe
|
File details
Details for the file heydsl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: heydsl-0.1.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df2a291b331c9323b05b0f05ac3d437165b900e8128d3ebbd514a922dd297484
|
|
| MD5 |
85522f050e026afeab580decb18b4bf1
|
|
| BLAKE2b-256 |
9e72c59d74b4cc283398462b4bfa2582929d007059ac76497b8b1ed93ff124de
|