Skip to content

Contributing

Thank you for your interest in actively participating in the project's development! Please read the Contributor Covenant Code of Conduct and the Contributor License Agreement first.

The project is written in ClojureScript - a compiler for Clojure that targets JavaScript, and is based on re-frame - a framework for building Modern Web Apps in ClojureScript. You should probably take a look at their exceptional documentation first.

Style Guide

We try to follow the Clojure Style Guide as much as possible.

An additional resource about how to name Clojure functions by Stuart Sierra.

In addition to the idiomatic names, we use the following conventions

e           -> event
el, els     -> element, elements
attr, attrs -> attribute, attributes
prop, props -> property, properties
w, h        -> width, height
t           -> time
h, m, s, ms -> hours, minutes, seconds, milliseconds

We also use the following namespace aliases

v  -> views
e  -> events
h  -> handlers
s  -> subs
fx -> effects

If the namespace belongs to a dedicated module, we use module.v.

App structure

Main structure

src\
├── renderer\     <--- Renderer Process
├── electron\     <--- Main Process & Preload script
├── lang\         <--- Translation files
└── worker\       <--- Web Workers

We are trying to split our code under renderer into relatively independent modules, following re-frame's app structure suggestions with some minor additions.

module\
├── core.cljs      <--- entry point
├── db.cljs        <--- schema, validation
├── views.cljs     <--- reagent views
├── events.cljs    <--- event handlers
├── subs.cljs      <--- subscription handlers
├── handlers.cljs  <--- helper functions for db transformations
├── effects.cljs   <--- effect handlers
├── hierarchy.cljs <--- multimethods and hierarchies
├── styles.css     <--- styles
└── README.md      <--- documentation

Re-frame recommendations

Avoid chaining events to create new ones. Always prefer composing pure functions that directly transform the db. That is the whole purpose of handlers namespace.

Use interceptors sparingly. Although they look (and probably are) ingenious, it is hard to write and reason with them. Doing things explicitly, is usually easier to grasp and maintain.

Always use auto-qualified keywords (e.g. ::copy) for subscriptions, events and effects. You can use as-alias to require those namespaces without evaluating the registrations multiple times.

Spec

We use malli to describe the shape of our app db and selectively validate incoming data (e.g. file loading). We also use this spec to generate default values. You can optionally enable full db validation on dev mode (see renderer.dev namespace).

Function inline schemas are used selectively on critical namespaces, like renderer.utils.bounds. Instrumentation is enabled by default on dev environment only.

Useful development shortcuts

Ctrl+Shift+I Toggle devtools
Ctrl+Shift+X Toggle 10x
Ctrl+R Reload app