@elyx-design/cli
v0.0.1-nightly.14809.5c7efbe47
Published
Command-line tools for Elyx
Readme
@elyx-design/cli
The command-line interface for Elyx.
Install it globally and run elyx from your terminal:
npm install --global @elyx-design/cli
elyx --helpOpen a project folder in the matching Elyx app release (development, nightly, preview, or public):
elyx .
elyx /path/to/projectThis package provides the JavaScript launcher. The native executable is installed through an optional package selected for the current operating system and architecture.
Linux support
The Linux CLI currently supports x86-64 systems using glibc. Ubuntu 24.04 is the tested environment, and libc++ and Fontconfig must be installed on the system. Alpine and other musl-based distributions are not currently supported.
Development
cli.ts is the npm launcher. src/ contains the native entry point, commands,
and their tests and fixtures. CMakeLists.txt builds the native elyx executable
using Mosaic's shared language and rendering code. cmake/ contains embedded
resource generators. cpp-vendors.yml declares CLI11, libgit2, and the demo
project used by --create.
Run from the repository root:
mise run vendors:elyx
mise run elyx:build
mise run elyx:test:cpp
mise run cli:buildThe native debug executable is written to out/native/debug/elyx within this
directory. The npm launcher builds to dist/cli.js. The published npm package
includes only the launcher, README, and license; platform packages supply the
native executable.
The daemon also compiles rendering, inspection, lint, and scene code from
src/. Mosaic uses its native font-loading code.
The Figma reader and its CLI adapter live in src/fig/. Its miniz
and Zstandard dependencies are also listed in cpp-vendors.yml and are only
configured by the native CLI build. The daemon also builds the reader and
converter using its own pinned dependencies for sidebar .fig imports.
Building an Elyx model
For .fig decoding and the elyx fig inspect and elyx fig convert commands,
see the Figma reader and converter.
ElyxLang::ElyxModelBuilder builds objects from Model.h without creating an
MSWorld. It uses elyxSchema() to find fields, check units, select the existing
value codec, and order properties. build() transfers the declarations into a
ModelValue containing a ModelBoard.
Pass an ElyxModelBuilder::Layer tree to addLayer(). Children keep their input
order and are declared inside their parent, alongside the children: references.
Names must be unique among siblings; separate parents can reuse a child name.
The schema determines which layer types accept children.
Properties currently support literal lengths, such as frame width and height,
and a single solid fill supplied as an MSColor. Color channels, including
alpha, must be finite values between 0 and 1. The builder validates the fill
through the schema's shorthand expansion, paint branch, and color codec, then
keeps the typed color for formatting. Paint lists and paint objects are not yet
supported.
Unknown properties, invalid or duplicate symbols, invalid lengths, unsupported
property structures, and nesting beyond 512 child levels return an error. A
failed addLayer() leaves the entire document unchanged, even when the error is
in a descendant. Accepted values are copied; the builder does not omit defaults.
The model carries a distinct namespace for each layer's declarations. The formatter uses those namespaces to render local child references without an external namespace resolver.
The schema must outlive the builder. The process-wide elyxSchema() does.
Create a frame with colored children and write it to disk
#include <fstream>
#include <stdexcept>
#include <utility>
#include "ElyxLang/ElyxFormatter.h"
#include "ElyxLang/ElyxModelBuilder.h"
#include "ElyxLang/ElyxSchema.h"
#include "primitives/MSColor.h"
int main() {
using namespace ElyxLang;
ElyxModelBuilder document{Schema::elyxSchema()};
ElyxModelBuilder::Layer card{
.kind = ModelBuiltinBase::Frame,
.name = "card",
};
card.properties.add("width", ModelValue{ast::Length::Pixel(320)});
card.properties.add("height", ModelValue{ast::Length::Pixel(200)});
card.properties.add("fill", ModelValue{MSColor::White()});
ElyxModelBuilder::Layer rectangle{
.kind = ModelBuiltinBase::Frame,
.name = "rectangle",
};
rectangle.properties.add("left", ModelValue{ast::Length::Pixel(16)});
rectangle.properties.add("top", ModelValue{ast::Length::Pixel(24)});
rectangle.properties.add("width", ModelValue{ast::Length::Pixel(80)});
rectangle.properties.add("height", ModelValue{ast::Length::Pixel(40)});
rectangle.properties.add("fill", ModelValue{MSColor::Red()});
card.children.push_back(std::move(rectangle));
ElyxModelBuilder::Layer translucent{
.kind = ModelBuiltinBase::Frame,
.name = "translucent",
};
translucent.properties.add("left", ModelValue{ast::Length::Pixel(128)});
translucent.properties.add("top", ModelValue{ast::Length::Pixel(24)});
translucent.properties.add("width", ModelValue{ast::Length::Pixel(80)});
translucent.properties.add("height", ModelValue{ast::Length::Pixel(40)});
translucent.properties.add("fill", ModelValue{MSColor::Blue().withAlpha(0.5F)});
card.children.push_back(std::move(translucent));
if (auto result = document.addLayer(card); !result) {
throw std::runtime_error(result.error());
}
auto model = std::move(document).build();
pp::DocumentBuilder output;
pp::PrettyPrinter printer;
pp::DefaultLayout layout;
const auto &formatted = printer.pp(output, model);
std::ofstream file;
file.exceptions(std::ios::failbit | std::ios::badbit);
file.open("card.elyx");
file << printer.render(formatted, layout);
file.close();
}Additional paint types and references to other scopes are later steps. The Figma converter calls this shared language API; the CLI handles arguments and output files.
Layer targets
Inspection returns a target on component nodes and bounds, hit, and rectangle
results. It contains filePath, sourceRef, and overridePath; a null target
means the runtime layer has no reusable source address. Other fields such as
symbol, ref, and path describe the layer, rather than identifying an exact
instance occurrence.
Use the target's file path as the CLI input, sourceRef as inspect's --ref or
render's --symbol, and overridePath as --override-path. For example:
elyx inspect component screen.elyx --ref=musicPlayer.track.save
elyx inspect bounds screen.elyx --ref=card --override-path="'ui.Body.Label"
elyx render screen.elyx --symbol=card --override-path="'ui.Body.Label" -o label.pngCopy override paths from inspection; their namespace qualifiers distinguish
imported descendants. Ambiguous source references fail instead of choosing an
instance. In MCP, pass the returned object as query.target to inspect
bounds/component, target to render, or an entry in focus's targets array.
Spatial inspect queries use query.filePath. To render a whole document, pass
target: {filePath: "screen.elyx"}.
