# scholatex

**Print-ready teaching worksheets, without writing LaTeX.** — A small, consistent tag language for the documents a teacher actually makes.

[![CTAN](https://img.shields.io/badge/CTAN-scholatex-d35400?style=flat-square)](https://ctan.org/pkg/scholatex)
[![Version](https://img.shields.io/badge/version-2.0-2980b9?style=flat-square)](https://ctan.org/pkg/scholatex)
[![Engine](https://img.shields.io/badge/engine-LuaLaTeX-1F3A5F?style=flat-square)](https://www.luatex.org/)
[![TeX Live](https://img.shields.io/badge/TeX_Live-included-27ae60?style=flat-square)](https://tug.org/texlive/)
[![License](https://img.shields.io/badge/license-GPL_v3+-8e44ad?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)

## Why scholatex?

Write a single `.tex` file with a tiny, readable syntax and get a clean, print-ready worksheet — framed exercises, a results table, simple formulas, an image — without `\begin{tabular}{|c|c|}`, without counting ampersands, without remembering which package draws a coloured box.

```latex
% !TeX program = lualatex
\documentclass[margins=20, size=12]{scholatex}
\begin{document}

<navy b 18pt c>My first scholatex document

This is a normal paragraph. <b>{Bold}, <i>{italic}, <red>{red}.

<box line:Navy fill:AliceBlue radius:3 title:{A framed note}>{
Boxes, tables, images and maths all use the same tag syntax.
}

\end{document}
```

Compile with `lualatex myfile.tex` — the `scholatex` class reads the body between `\begin{document}` and `\end{document}`, transpiles it to LaTeX, and typesets it. The output has full LaTeX quality; the syntax is meant to be read and edited in minutes by someone who does not know LaTeX. scholatex is a **closed language**: the backslash is an ordinary character, so raw LaTeX commands in the body are typeset literally rather than executed. Everything a worksheet needs is expressed through tags, which keeps documents consistent and safe to share.

## Quick start

```bash
tlmgr install scholatex      # add sudo on macOS/Linux; see Installation below
```

```latex
\documentclass[margins=20, size=12, lang=en]{scholatex}
\begin{document}
<navy b section>First topic
A paragraph with <b>{bold} and <red>{colour}.
\end{document}
```

## Key features

- **🏷️ One tag syntax** — `<attributes>{content}` covers text, tables, images, maths, boxes, lists and full-page layouts
- **🎨 151 colours** — the full CSS / svgnames palette in CamelCase, 22 of them with a lowercase shortcut
- **📐 A maths mini-language** — fractions, roots, sums, products, integrals, derivatives, trigonometry, matrices and systems, all in `$...$`
- **📦 Framed boxes & grids** — coloured `<box>` panels and CSS-Grid-style named-area page layouts
- **📊 Tables without pain** — two-letter cell placement, spans, headers, borders, no ampersand counting
- **♻️ Aliases & macros** — factor a style once, name it everywhere; one edit restyles the whole document
- **🔁 Control flow** — loops, conditionals and `#{...}` interpolation in the document body
- **🔒 Sandbox option** — `untrusted=true` runs document Lua in a restricted environment

---

## The one rule

Everything is a **tag**: `<attributes>` followed either by `{content}` (inline) or by a line that ends in `{` (a block). Attribute words follow a single case convention:

| Form | Meaning | Examples |
|------|---------|----------|
| `lowercase` | a short keyword or base colour | `b`, `i`, `c`, `red`, `2tab` |
| `CamelCase` | an extended CSS colour (151 of them) | `SteelBlue`, `Crimson` |
| `UPPERCASE` | a font name | `DEJAVU SANS` |

The order of words inside a tag never matters — emission is always normalised (page break, then vertical skips, then alignment, then tabs, then styles).

---

## Installation

`scholatex` is on CTAN and ships with TeX Live, so on a current, fully updated TeX Live or MacTeX it is already there — compile with `lualatex` and nothing more is needed. If your installation predates the package, install it once with the TeX Live manager; the same two commands work on **macOS, Linux and Windows**:

```bash
tlmgr update --self
tlmgr install scholatex
```

On macOS (MacTeX) and most Linux setups `tlmgr` needs administrator rights, so prefix both with `sudo`. On Windows, open the *TeX Live Command-Line* (or a terminal as Administrator) and run them without `sudo`. MiKTeX users install through the MiKTeX Console (*Packages* → search `scholatex` → *Install*), or with `mpm --install=scholatex`.

`tlmgr update --self` comes first because the manager refuses to install packages when its own version is older than the repository's. After installing, check the class is found:

```bash
kpsewhich scholatex.cls
```

It should print a path under your TeX tree. From then on `\documentclass{scholatex}` works from any folder.

If `tlmgr install scholatex` reports the package is *not present in the repository*, your mirror has not yet synced a recent release; either wait a day or switch to the main CTAN mirror and retry:

```bash
sudo tlmgr option repository https://mirror.ctan.org/systems/texlive/tlnet
sudo tlmgr update --self
sudo tlmgr install scholatex
```

### Manual install (any OS, no waiting)

To use the package immediately without `tlmgr`, drop the files into your personal TeX tree, mirroring the CTAN layout, then refresh the filename database. The home tree is `~/Library/texmf` on macOS, `~/texmf` on Linux, and `%USERPROFILE%\texmf` on Windows:

```bash
mkdir -p ~/texmf/tex/luatex/latex/scholatex
cp scholatex.cls scholatex.lua scholatex-*.lua ~/texmf/tex/luatex/latex/scholatex/
mktexlsr ~/texmf
```

A LuaLaTeX engine is required (`lualatex`); the package does **not** compile with `pdflatex` or `xelatex`.

---
## Class options

Set in `\documentclass[...]{scholatex}`:

| Option | Default | Meaning |
|--------|---------|---------|
| `margins` | `20` | `N` (all sides) or `{top,right,bottom,left}` in mm |
| `font` | `Latin Modern Roman` | main text font |
| `mathfont` | `Latin Modern Math` | math font |
| `size` | `11` | base font size in pt |
| `imgdir` | `img` | folder(s) searched for bare image names; comma-separated list, e.g. `{IMG, IMAGES/PNG}` |
| `tabwidth` | `8` | width of one tab, in mm (a large Seyès square) |
| `lineheight` | `8` | height of one explicit line skip (`<line>`, `<Nlines>`), in mm |
| `linespread` | `1.0` | overall line spacing (1.5 = one-and-a-half); tall maths never touches even at 1.0 |
| `scriptscale` | `100` | scale (%) of `up`/`down` scripts |
| `padding` | `2` | inner padding (mm) between a box/grid frame and its content; override locally with `sep:N` |
| `lang` | `fr` | decimal separator: `fr` (comma) or `en` (point); affects typed and interpolated decimals alike |
| `untrusted` | `false` | run document Lua in a restricted sandbox — see [Security](#security) |

Headings carry no extra vertical space or built-in colour: to style one, fold a heading keyword into an alias, e.g. `let h = <line navy b section>` then `<h>My title`.

---

## Text attributes

**Inline styles** — `b` bold, `i` italic, `u` underline, `emph`, `sf` sans-serif, `sc` small caps. They combine in one tag: `<b i red>{bold italic red}`.

**Colours** — short keywords (`red`, `blue`, `green`, `navy`, `orange`, `purple`, `teal`, `brown`, `gray`, `pink`, …) or any of the 151 CSS colours in CamelCase (`Tomato`, `SteelBlue`, `ForestGreen`, …). See the [colour reference](#colour-reference).

**Fonts and sizes** — a font name in CAPITALS (`<DEJAVU SANS>{…}`); sizes as `Npt` or `Npx` (`<14pt>{…}`).

**Alignment** — `l` left, `c` centre, `r` right, `j` justified.

**Tabs and skips** (the number is always a prefix) — `Ntab` indents the first line by N tabs; vertical skips obey singular/plural agreement: `line` or `1line` skips one, `2lines`, `3lines`, … skip several. Bare `tab` = `1tab`.

**Scripts** — `upN` raises text by N mm, `downN` lowers it: `x<up4>{2}`, `H<down2>{2}O`.

**Page break** — `nextpage`.

**Section headings** — `section`, `subsection`, `subsubsection` give the three levels, numbered automatically:

```
<section>First topic
<subsection>A detail
<subsubsection>A finer point
```

renders as `1 First topic`, `1.1 A detail`, `1.1.1 A finer point`. A **table of contents** is printed with `<tableofcontents>`; give it a title in braces: `<tableofcontents>{Table of contents}`.

---

## Aliases and macros — the factoring tool

Define a style **once**, at the top of the document, then name it everywhere instead of repeating its attributes. One edit at the definition restyles the whole document.

```
let title = <navy b 18pt c>          % style alias
let h1    = <navy b section>         % a heading style, reusable
let p     = <tab>                    % a standard indented paragraph
<title>My heading
<h1>First topic
<p>{ A paragraph, indented and justified, named not described. }

let n = 7                            % value, usable in #{...}
Seven squared is #{n*n}.

let greet{name} = Hello #name!       % text macro with parameters
```

Change `let h1 = <navy b section>` to `<ForestGreen b section>` once and **every** first-level heading follows — the single point of control that keeps a long worksheet consistent.

---

## Tables

Columns are declared in brackets, **one two-letter placement code per column**. The first letter is vertical (`t`/`m`/`b`), the second horizontal (`l`/`c`/`r`): `mc` is middle-centre, `br` bottom-right. This is the only placement syntax scholatex uses — in tables as in boxes and grids.

```
<table [mc, ml, mc, mc] borders header fill:AliceBlue line:Navy headerfill:Navy headertext:White>{
<colspan:4 mc>{Term report}
Day | Subject | Mark | Coef.
<rowspan:2 mc>{Monday} | Maths | 15 | 4
. | French | 12 | 3
}
```

One row per line, `|` between cells. `<colspan:N>` and `<rowspan:N>` span cells; `.` marks a cell covered by a span above. `N:` before a placement code fixes a column width in mm.

---

## Images

```
<img 30>{photo.png}        % 30 mm wide
<img 40x25>{photo.png}     % 40 mm × 25 mm
<img>{photo.png}           % full available width, never enlarged
```

A bare name is searched in each folder of `imgdir` in turn, then at the project root. An explicit path always works (`<img 20>{IMG/PNG/chat.png}`).

---
## Maths

Wrap maths in `$…$`. A small mini-language keeps it light:

| You write | You get |
|-----------|---------|
| `*` | × |
| `+-` | ± |
| `<=` `>=` `!=` | ≤ ≥ ≠ |
| `a/b` | fraction (chained `a/b/c` reads as `(a/b)/c`) |
| `x^2` `x_i` | power / index (bind tighter than `/`, so `x^2/y` is `\frac{x^2}{y}`) |
| `sqrt(2)` | √2 |
| `sum(i=1, n) i` | ∑ with bounds, display style |
| `prod(k=1, n) k` | ∏ with bounds, display style |
| `int(x) f(x)` | ∫ f(x) dx (primitive; the differential is added) |
| `int(x=a, b) f(x)` | ∫ from a to b, f(x) dx |
| `contourint(C) f(z)` | ∮ contour integral |
| `pvint(x=a, b) f(x)` | p.v. ∫ Cauchy principal value |
| `meanint(x=a, b) f(x)` | ⨍ average (normalised) integral |
| `dy/dx`, `df/dx` | derivative, upright differential d (ISO) |
| `sin(x)` `cos(x)` `ln(x)` … | upright function names |
| `abs(x)` | \|x\| |
| `norm(v)` | ‖v‖ |
| `vec(AB)` | →AB (over-arrow vector) |
| `lim(x->0) f(x)` | limit, `->` becomes the arrow, target under the word |
| `partial`, `nabla` | ∂, ∇ (use `(partial f)/(partial x)` for ∂f/∂x) |
| `pi`, `alpha`, … | Greek letters |
| `inf` | ∞ |

The helpers nest, so the secondary-school staples come for free: `norm(vec(AB))` is the norm of a vector, `vec(AB) + vec(BC) = vec(AC)` is Chasles' relation.

### Operators with an index: sum, prod, lim

`sum`, `prod` and `lim` carry their index in `(...)` and set their whole expression in display style, so a fraction in the body keeps full size and a limit's target sits **under** the word, as on a blackboard. The body runs to the end of the formula or to the first `=`:

```
$sum(i=1, n) i = n(n+1)/2$
$prod(k=1, n) k$
$lim(x->0) sin(x)/x = 1$
$lim(x->+inf) 1/x$
```

### Functions and trigonometry

Function names are set upright automatically — no backslashes. The set covers `sin cos tan cot sec csc`, the inverses `arcsin arccos arctan`, the hyperbolics `sinh cosh tanh coth`, and `ln log exp det dim gcd deg ker arg max min sup`. A name glued to `(...)` takes its argument as one atom, so fractions and powers behave:

```
$sin(x)^2 + cos(x)^2 = 1$
$cos(a+b) = cos(a)cos(b) - sin(a)sin(b)$
$tan(x) = sin(x)/cos(x)$
```

### Integrals

The integral family writes its variable and bounds in the head `(...)` and captures the integrand as its body; the differential `\,dx` is appended automatically. No bounds gives a primitive, a comma-separated pair a definite integral:

```
$int(x) f(x)$              ∫ f(x) dx
$int(x=a, b) f(x)$         ∫ from a to b of f(x) dx
```

**Multiple integrals** — separate several domains with `;`. The count of domains chooses ∫, ∬ or ∭; the differentials come out in reverse order (Fubini):

```
$int(x=a, b ; y=c, d) f(x,y)$            ∬ … dy dx
$int(x=a, b ; y=c, d ; z=e, g) f$        ∭ … dz dy dx
```

A single named domain is a region integral: `int(D) f` gives ∬_D f dω. **Named integrals**: `contourint(C) f(z)` is a contour integral ∮, `pvint(x=a, b) f(x)` a Cauchy principal value, `meanint(x=a, b) f(x)` the average integral ⨍.

### Derivatives

A Leibniz derivative is written as the fraction it is, and the differential `d` is set upright (ISO 80000-2), matching the `d` of the integrals — **only** when both sides of the fraction carry it, so a variable named `d` is never disturbed (`d/2` stays the fraction d over 2):

```
$dy/dx$                 dy/dx, upright d
$(d^2 y)/(dx^2)$        second derivative
$dy/dx + y = 0$         a differential equation
```

Partial derivatives use `partial` (∂); parenthesise each side so the fraction groups correctly:

```
$(partial f)/(partial x)$
$(partial u)/(partial t) = (partial^2 u)/(partial x^2)$
```

`nabla` (∇) is available for gradients and divergences.

### Maths blocks

Matrices and systems are blocks: **one line is one row**, and inside a matrix `;` separates the entries. Every cell still goes through the mini-language.

```
<matrix>{
1 ; 2 ; 3
4 ; 5 ; 6
}
```

`<matrix>` draws parentheses, `<det>` the bars of a determinant, `<bmatrix>` square brackets. A single `|` inside a row draws the bar of an **augmented matrix** (allowed on `matrix` and `bmatrix`, never on `det`). A `<system>` stacks equations under a brace, aligned on the first relational operator:

```
<system>{
2x + 3y = 7
x - y = 1
}
```

Inject a computed value with `#{expr}` (or `#name`), including inside maths: `$#k^2$`. Decimal numbers follow the `lang` option.

---

## Boxes

```
<box line:Crimson fill:MistyRose radius:4 title:{A note}>{
Content here.
}
```

Options: `line:` frame colour, `fill:` background, `text:` text colour, `radius:N` rounded corners (mm), `width:N` or `width:N%`, `boxrule:N`, `boxsep:N`, `break:yes`, `title:{…}`, `titlefill:`, `titletext:`. A line containing only `---` splits a box into two regions.

`<row gap:N>{ … }` lays its child boxes side by side, equal widths and equalised heights. A box also takes a two-letter placement code (`tl`…`br`, default `tl`); the vertical part needs a `height:` to act.

---

## Grid (named-area layout)

For full-page layouts — a worksheet header with a logo, a title bar, info fields, and a body — `<grid>` borrows CSS Grid's named-area idea. A `template:[ … ]` of quoted rows draws the layout; each word names a cell. A name repeated horizontally spans columns, vertically spans rows; a dot `.` is empty.

```
<grid template:[
  "title  title  logo"
  "intro  info   logo"
  "body   body   body"
] gap:4>{
  <area title>{ <red b 16pt>Maths assessment }
  <area logo >{ <img>{blason.png} }
  <area intro>{ Instructions: no calculator. }
  <area info >{ Name: \\ First name: }
  <area body >{ <s1>Exercise 1
    Solve the equation... }
}
```

An area can be framed like a box (`line:`, `fill:`, `radius:`, `title:`). The grid takes `width:` and `height:`; an area or the grid takes a two-letter placement code for content position within cells.

---

## Lists

A list is `<list:STYLE>` — the style follows the name, one item per line, no item tag:

```
<list:decimal>{
Read the instructions
Underline the key words
Write your answer
}
```

Styles — bullets: `none` `disc` `circle` `square`; numbered: `decimal` `alpha` `ALPHA` `roman` `ROMAN` (the case of the keyword sets the case of the letters); checkboxes: `check`. A list written under an item becomes its sub-list, nested as deep as you like. Text attributes on the tag wrap the whole list: `<list:ROMAN TIMES NEW ROMAN 12pt i>{ … }`.

---

## Block aliases

Define a reusable component once; `#param` placeholders are filled at the call site, and the call-site body becomes the block content — so it may contain sub-blocks of its own.

```
let card{title, frame} = <box title:{#title} line:#frame radius:2>

<card First, Crimson>{ Called with two arguments. }
<card Second, Navy>{ Same component, different look. }
```

---

## Control flow

```
for n in 1..3 {
<c navy b>Sheet #n
}

for f in [chat.png, chien.png] {
<img 16>{#f}
}

if score >= 10 {
<green>Passed.
} else {
<red>Try again.
}
```

Loops and conditions work in the document body, inside boxes, and inside table bodies. The loop variable interpolates everywhere via `#`.

---

## Escapes

To print a character that scholatex treats specially, **double it**: `<<` `>>` `{{` `}}` `##` give a literal `<` `>` `{` `}` `#`. The backslash is an ordinary character — a path like `C:\Users\Leo` or a regex `\d+\s*\w` prints verbatim, nothing to escape. The characters `_ & % ~` are escaped automatically. A line break inside a paragraph is the tag `<nextline>`. A line whose first non-space character is `%` is a comment. A bare `#` not followed by a name or `{…}` is a literal `#`.

Braces carry structure, so a **literal brace must be balanced**: write the pair `{{…}}` to print `{…}`. A lone, unmatched `{{` or `}}` is reported as an unbalanced brace naming its line, rather than silently corrupting the surrounding block — so set-builder notation like `{{ x : x > 0 }}` is written as a pair. Angle brackets and hashes need no such balancing; only braces do.

---
## Colour reference

Short lowercase keywords cover the everyday set: `red` `blue` `green` `navy` `orange` `purple` `teal` `brown` `gray`/`grey` `pink` `yellow` `black` `white` `violet` `cyan` `magenta` `lime` `olive` `aqua` `silver` `maroon`.

For the full palette, write any of the **151 CSS / svgnames colours in CamelCase** (`<SteelBlue>{…}`, `fill:MistyRose`, `line:DarkOrange`). They are grouped below by family for browsing; all are valid anywhere a colour is expected (`line:` `fill:` `text:` and inline tags).

**Reds & pinks** (14) — `Brown`, `Crimson`, `DarkRed`, `FireBrick`, `IndianRed`, `LightCoral`, `LightPink`, `Maroon`, `MistyRose`, `Pink`, `Red`, `RosyBrown`, `Salmon`, `Tomato`

**Oranges & browns** (23) — `AntiqueWhite`, `Bisque`, `BlanchedAlmond`, `BurlyWood`, `Chocolate`, `Coral`, `DarkGoldenrod`, `DarkOrange`, `DarkSalmon`, `Goldenrod`, `LightSalmon`, `Moccasin`, `NavajoWhite`, `Orange`, `OrangeRed`, `PapayaWhip`, `PeachPuff`, `Peru`, `SaddleBrown`, `SandyBrown`, `Sienna`, `Tan`, `Wheat`

**Yellows & golds** (11) — `Cornsilk`, `DarkKhaki`, `Gold`, `Khaki`, `LemonChiffon`, `LightGoldenrod`, `LightGoldenrodYellow`, `LightYellow`, `Olive`, `PaleGoldenrod`, `Yellow`

**Greens** (20) — `Aquamarine`, `Chartreuse`, `DarkGreen`, `DarkOliveGreen`, `DarkSeaGreen`, `ForestGreen`, `Green`, `GreenYellow`, `LawnGreen`, `LightGreen`, `Lime`, `LimeGreen`, `MediumAquamarine`, `MediumSeaGreen`, `MediumSpringGreen`, `OliveDrab`, `PaleGreen`, `SeaGreen`, `SpringGreen`, `YellowGreen`

**Cyans & teals** (17) — `Aqua`, `CadetBlue`, `Cyan`, `DarkCyan`, `DarkSlateGray`, `DarkSlateGrey`, `DarkTurquoise`, `DeepSkyBlue`, `LightBlue`, `LightCyan`, `LightSeaGreen`, `MediumTurquoise`, `PaleTurquoise`, `PowderBlue`, `SkyBlue`, `Teal`, `Turquoise`

**Blues** (21) — `Blue`, `CornflowerBlue`, `DarkBlue`, `DarkSlateBlue`, `DodgerBlue`, `LightSkyBlue`, `LightSlateBlue`, `LightSlateGray`, `LightSlateGrey`, `LightSteelBlue`, `MediumBlue`, `MediumPurple`, `MediumSlateBlue`, `MidnightBlue`, `Navy`, `NavyBlue`, `RoyalBlue`, `SlateBlue`, `SlateGray`, `SlateGrey`, `SteelBlue`

**Purples & violets** (17) — `BlueViolet`, `DarkMagenta`, `DarkOrchid`, `DarkViolet`, `DeepPink`, `Fuchsia`, `HotPink`, `Indigo`, `Magenta`, `MediumOrchid`, `MediumVioletRed`, `Orchid`, `PaleVioletRed`, `Plum`, `Purple`, `Violet`, `VioletRed`

**Grays** (10) — `DarkGray`, `DarkGrey`, `DimGray`, `DimGrey`, `Gray`, `Grey`, `LightGray`, `LightGrey`, `Silver`, `Thistle`

**Whites & off-whites** (17) — `AliceBlue`, `Azure`, `Beige`, `FloralWhite`, `Gainsboro`, `GhostWhite`, `Honeydew`, `Ivory`, `Lavender`, `LavenderBlush`, `Linen`, `MintCream`, `OldLace`, `Seashell`, `Snow`, `White`, `WhiteSmoke`

**Black** (1) — `Black`

These are exactly the colours xcolor provides under its `svgnames` option (the 147 CSS Color Module names plus the four X11 extras `LightGoldenrod`, `LightSlateBlue`, `NavyBlue` and `VioletRed`). Names are case-sensitive: write `Seashell`, not `SeaShell`.

---
## Security

`scholatex` evaluates `let name = expr`, `#{expr}` and the conditions of `for`/`if`/`while` as Lua at compile time, so by default a document can run arbitrary code — exactly like `\directlua`.

Setting `untrusted=true` in `\documentclass[...]{scholatex}` runs that Lua in a restricted environment: only pure, side-effect-free names are visible; `os`, `io`, `package`, `require`, `load`, `debug` and other escape vectors are absent. A blocked access stops the compile with a clear message; a runaway loop is aborted by an instruction-count ceiling; `string.rep`/`string.format` are capped.

```latex
\documentclass[untrusted=true]{scholatex}
```

`untrusted` hardens **the scholatex expression layer only**. It does not sandbox LuaLaTeX as a whole — a hostile `.tex` can still call `\directlua`, `\write18`, `\input`. Use it when the scholatex **body** comes from a semi-trusted source while the surrounding `.tex` is your own; for a whole untrusted `.tex`, run `lualatex` without `--shell-escape`, ideally in a container.

---

## Examples

The `examples/` folder contains three self-contained, fully commented documents that together exercise every feature:

| File | Covers |
|------|--------|
| `01-text-style.tex` | the case rule, styles, colours, fonts, sizes, alignment, tabs, skips, scripts; **factoring styles into aliases**; a table of contents from the heading keywords |
| `02-containers.tex` | tables, boxes and the named-area grid, each built up from its simplest form to a full worksheet header |
| `03-math.tex` | the inline mini-language, operators with an index, the integral family (`int` `contourint` `pvint` `meanint`), trigonometry, derivatives, and the matrix / determinant / augmented-matrix / system blocks |

Compile any of them with `lualatex <file>.tex` from the `examples/` folder.

---

## Project layout

```
scholatex.cls            LaTeX class: options, packages, reads & injects the body
scholatex.lua            transpiler core: tags, text, control flow, aliases
scholatex-style.lua      attribute resolution (colours, styles, sizes, alignment…)
scholatex-math.lua       the $…$ math mini-language (operators, integrals, functions, trig)
scholatex-util.lua       parsing primitives (groups, brace balance, comma split)
scholatex-table.lua      the <table> block
scholatex-img.lua        the <img> tag
scholatex-box.lua        the <box> and <row> blocks
scholatex-grid.lua       the <grid> named-area layout block
scholatex-section.lua    the <section>/<subsection>/<subsubsection> blocks
scholatex-list.lua       the <list:STYLE> block
scholatex-matrix.lua     the <matrix>/<det>/<bmatrix> and <system> blocks
scholatex-toc.lua        the <tableofcontents> tag
examples/                three commented showcase documents
```

New tags register themselves via `scholatex.register_tag` / `scholatex.register_block`; a name clash raises an error rather than silently overwriting, so modules stay independent.

---

## Diagnostics

Errors point at the source line, e.g. `scholatex: line 12: unknown tag attribute: 'xyz'`. Defining an alias whose name is a built-in (`let section = …`) prints a warning: the built-in always wins, so the alias would be silently dead — pick a different name.

---

## What's new

### 2.0

- **New escape rules** *(breaking change)* — to print a special character, double it: `<<` `>>` `{{` `}}` `##` give a literal `<` `>` `{` `}` `#`. A backslash is now an ordinary character (so `C:\Users` just works); it is no longer an escape. The old `\<` `\>` `\{` `\}` `\#` forms no longer apply.
- **Line break is a tag** *(breaking change)* — use `<nextline>` (in text and table cells) instead of the old `\\`. In running text a blank line already starts a new paragraph, so `<nextline>` is only for a break without a paragraph change.

### 1.2

- **Script/fraction precedence fix** — `^` and `_` now bind tighter than `/`, so `x^2/y` renders as the fraction of `x^2` over `y` (and `1/i^2` as `1` over `i^2`), matching ordinary mathematical reading
- **Automatic spacing for tall lines** — consecutive lines containing fractions or integrals no longer touch; TeX inserts just enough air where needed, leaving ordinary text unchanged

### 1.1

- **Maths expansion** — full integral family (`int`, multiple integrals with Fubini ordering, `contourint`, `pvint`, `meanint`), upright functions and trigonometry, Leibniz and partial derivatives with ISO 80000-2 upright differentials
- **Operators in display style** — `sum`, `prod`, `lim` keep fractions full-size and place limits under the word
- **Full colour parity** — all 151 xcolor svgnames colours recognised, grouped by family in the [reference](#colour-reference)
- **Cross-platform install** — documented `tlmgr` and manual paths for macOS, Linux and Windows

---

## Acknowledgments

`scholatex` is built on excellent LaTeX packages:

- [tcolorbox](https://ctan.org/pkg/tcolorbox) — framed boxes and posters
- [tabularray](https://ctan.org/pkg/tabularray) — reliable 2-D table layout
- [unicode-math](https://ctan.org/pkg/unicode-math) — OpenType maths
- [fontspec](https://ctan.org/pkg/fontspec) — system fonts in LuaLaTeX
- [xcolor](https://ctan.org/pkg/xcolor) — the svgnames colour palette

---

## License

Copyright © 2026 Gérard Dubard.

`scholatex` is free software: you can redistribute it and/or modify it under the terms of the **GNU General Public License version 3** (or later) as published by the Free Software Foundation. See the [`LICENSE`](LICENSE) file for the full text.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
