@regmisatyam/retex 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,11 @@ LaTeX without the toolchain. You write a small, readable markup language; ReTeX
11
11
  tokenizes, parses, validates, and renders it to **HTML**, **React**, **JSON**, or
12
12
  **PDF** — all from a single, dependency-free TypeScript library.
13
13
 
14
+ ReTeX is **blank by default** — a plain, unstyled document, "just simple
15
+ editing". Styling is opt-in: import a **library** with `\usepackage{...}`
16
+ (LaTeX-style) for colors, fonts, shapes, or a full themed look. Nothing is
17
+ imposed; you add exactly what you want.
18
+
14
19
  ```latex
15
20
  \name{Ada Lovelace}
16
21
  \title{Software Engineer}
@@ -27,10 +32,11 @@ tokenizes, parses, validates, and renders it to **HTML**, **React**, **JSON**, o
27
32
 
28
33
  ## Preview
29
34
 
30
- The three [examples](examples/) rendered to HTML (default, modern, and classic
31
- themes). Regenerate with `npx tsx examples/build.ts`.
35
+ The three [examples](examples/) rendered to HTML. Each opts into a theme pack
36
+ with a single `\usepackage{...}` line (`modern`, `compact`, `classic`).
37
+ Regenerate with `npx tsx examples/build.ts`.
32
38
 
33
- | Single column (`default`) | Two column (`modern`) | Academic CV (`classic`) |
39
+ | Single column (`modern`) | Two column (`compact`) | Academic CV (`classic`) |
34
40
  | --- | --- | --- |
35
41
  | [![software engineer](examples/rendered/software-engineer.png)](examples/software-engineer.retex) | [![two column](examples/rendered/two-column.png)](examples/two-column.retex) | [![research paper](examples/rendered/research-paper.png)](examples/research-paper.retex) |
36
42
 
@@ -44,18 +50,29 @@ set of commands (`\name`, `\job`, `\education`, `\skills`, `\section`, …), str
44
50
  theming, and four production-ready renderers.
45
51
 
46
52
  ReTeX **is not** a full LaTeX implementation. There is no math mode, no macro
47
- programming, no package ecosystem, no `\def`. The syntax is *LaTeX-inspired* —
48
- familiar braces and backslash commands — but the language is deliberately small,
49
- safe, and predictable. (Notably, `%` is a literal percent sign, not a comment;
53
+ programming, no `\def`. The syntax is *LaTeX-inspired* — familiar braces and
54
+ backslash commands — but the language is deliberately small, safe, and
55
+ predictable. (Notably, `%` is a literal percent sign, not a comment;
50
56
  see [Syntax](docs/SYNTAX.md).)
51
57
 
58
+ It does, however, borrow LaTeX's best idea: **packages**. The core is blank, and
59
+ you `\usepackage{...}` exactly the styling you need — `colors`, `fonts`,
60
+ `shapes`, or a complete theme pack like `modern`. See [Libraries](#libraries).
61
+
52
62
  ---
53
63
 
54
64
  ## Features
55
65
 
56
- - **Typography** — `\textbf`, `\textit`, `\emph`, `\underline`, `\sout`,
57
- `\textcolor`, `\fontsize`, `\fontfamily`, and scoped switches (`\small`,
58
- `\large`, `\Large`, `\Huge`, `\bfseries`, `\itshape`, `\ttfamily`).
66
+ - **Blank by default** — the core ships structure + basic emphasis; everything
67
+ decorative is opt-in. A document with no imports renders as a clean,
68
+ browser-default page.
69
+ - **Core typography** — `\textbf`, `\textit`, `\emph`, `\underline`, `\sout`,
70
+ `\bfseries`, `\itshape`.
71
+ - **Libraries (`\usepackage`)** — opt into styling, LaTeX-style: `colors`
72
+ (`\textcolor`, `\themecolor`), `fonts` (`\fontfamily`, `\fontsize`,
73
+ `\small`…`\Huge`, `\ttfamily`), `shapes` (`\hrule`, `\dashrule`, …, `\vspace`,
74
+ `\hspace`), and theme packs `modern` / `classic` / `compact`. Activate from the
75
+ document **or** from code (`engine.use(...)`).
59
76
  - **Hyperlinks** — `\href{url}{label}` and `\url{url}`, with URL sanitization.
60
77
  - **Sections** — `\section` and `\subsection`.
61
78
  - **Résumé components** — `\name`, `\title`, `\email`, `\phone`, `\location`,
@@ -64,8 +81,8 @@ see [Syntax](docs/SYNTAX.md).)
64
81
  environment.
65
82
  - **Icons** — inline SVG icon set (`\icon{github}`, `\icon{linkedin}`, …),
66
83
  extensible at runtime.
67
- - **Theming** — four presets (`default`, `modern`, `classic`, `compact`) plus a
68
- fully data-driven, deep-mergeable custom theme model.
84
+ - **Theming** — a blank default plus opt-in presets (`modern`, `classic`,
85
+ `compact`) and a fully data-driven, deep-mergeable custom theme model.
69
86
  - **Plugins** — register new commands, environments, icons, renderers, or theme
70
87
  patches on a per-engine basis. Two example plugins ship in the box
71
88
  (`badgePlugin`, `ratingPlugin`).
@@ -73,6 +90,9 @@ see [Syntax](docs/SYNTAX.md).)
73
90
  React element tree, a structured JSON AST, and print-ready HTML/PDF.
74
91
  - **Editor tooling** — completion, hover docs, semantic tokens, diagnostics,
75
92
  formatting, and AST inspection via `EditorService`.
93
+ - **Two-way preview** — opt-in `sourceMap` HTML stamps elements with source
94
+ offsets so a live editor can sync both ways (click preview ↔ highlight code),
95
+ Overleaf-style. See [`examples/playground.html`](examples/playground.html).
76
96
  - **Incremental parsing** — block-level caching for fast live preview.
77
97
  - **Security** — every text node and attribute is escaped; every URL is vetted.
78
98
  No `eval`, no `Function`, no code execution of source.
@@ -84,7 +104,7 @@ see [Syntax](docs/SYNTAX.md).)
84
104
  ## Install
85
105
 
86
106
  ```bash
87
- npm install retex
107
+ npm i @regmisatyam/retex
88
108
  ```
89
109
 
90
110
  React is an **optional peer dependency** — install it only if you use the React
@@ -98,22 +118,26 @@ PDF export lazily imports `puppeteer` if present; otherwise you can supply your
98
118
  own headless-browser launcher (Playwright, a pooled Chromium, etc.) or print the
99
119
  generated HTML to PDF in the browser.
100
120
 
101
- The package exposes two entry points:
121
+ The package exposes three entry points:
102
122
 
103
- | Import path | Contents |
104
- | --------------- | ----------------------------------------------------- |
105
- | `retex` | The engine, pipeline, AST utilities, all renderers\*, theming, plugins, security, icons, editor, incremental compiler. |
106
- | `retex/react` | The React renderer (`ReactRenderer`, `renderReact`). |
123
+ | Import path | Contents |
124
+ | ---------------------------- | ----------------------------------------------------- |
125
+ | `@regmisatyam/retex` | The engine, pipeline, AST utilities, all renderers\*, theming, libraries\*\*, plugins, security, icons, editor, incremental compiler. |
126
+ | `@regmisatyam/retex/react` | The React renderer (`ReactRenderer`, `renderReact`). |
127
+ | `@regmisatyam/retex/library` | Just the libraries (`colorsLibrary`, `fontsLibrary`, …) and helpers. |
107
128
 
108
- \* The React renderer is also re-exported from `retex`, but lives behind
109
- `retex/react` so the core bundle never imports React.
129
+ \* The React renderer is also re-exported from `@regmisatyam/retex`, but lives behind
130
+ `@regmisatyam/retex/react` so the core bundle never imports React.
131
+
132
+ \*\* The libraries are also re-exported from `@regmisatyam/retex`; the
133
+ `/library` subpath just lets you import them on their own.
110
134
 
111
135
  ---
112
136
 
113
137
  ## Quick start
114
138
 
115
139
  ```ts
116
- import { ReTeXEngine } from "retex";
140
+ import { ReTeXEngine } from "@regmisatyam/retex";
117
141
 
118
142
  const engine = new ReTeXEngine();
119
143
 
@@ -141,11 +165,30 @@ const css = engine.styles();
141
165
  `engine.toHtml` accepts either a source string or a parsed `DocumentNode`, so you
142
166
  can parse once and render to several targets.
143
167
 
168
+ That document renders as a clean, **unstyled** page. To add styling, import a
169
+ library — from the source:
170
+
171
+ ```latex
172
+ \usepackage{modern} %% a full themed look + the color/font/shape commands
173
+ \textcolor{#7c3aed}{Now in color}
174
+ ```
175
+
176
+ …or from code:
177
+
178
+ ```ts
179
+ import { ReTeXEngine, colorsLibrary, fontsLibrary } from "@regmisatyam/retex";
180
+ const engine = new ReTeXEngine().use(colorsLibrary).use(fontsLibrary);
181
+ ```
182
+
183
+ See [Libraries](#libraries) for the full catalog.
184
+
144
185
  ---
145
186
 
146
187
  ## A realistic résumé
147
188
 
148
189
  ```latex
190
+ \usepackage{modern} %% styled look + color/font/shape commands (e.g. \hspace below)
191
+
149
192
  %% --- header ---
150
193
  \name{Grace Hopper}
151
194
  \title{Distinguished Engineer \& Compiler Pioneer}
@@ -252,7 +295,7 @@ engine.styles(); // the theme's CSS (for the fragmen
252
295
  Or use the stage functions directly:
253
296
 
254
297
  ```ts
255
- import { tokenize, parse, validate, renderHtml } from "retex";
298
+ import { tokenize, parse, validate, renderHtml } from "@regmisatyam/retex";
256
299
 
257
300
  const { ast } = parse(tokenize(source).tokens);
258
301
  const diagnostics = validate(ast);
@@ -266,7 +309,7 @@ factory yourself, so it works with React, Preact, or any compatible runtime.
266
309
 
267
310
  ```tsx
268
311
  import React from "react";
269
- import { ReTeXEngine } from "retex";
312
+ import { ReTeXEngine } from "@regmisatyam/retex";
270
313
 
271
314
  function Resume({ source }: { source: string }) {
272
315
  const engine = React.useMemo(() => new ReTeXEngine(), []);
@@ -287,8 +330,8 @@ Standalone, via the dedicated subpath:
287
330
 
288
331
  ```tsx
289
332
  import React from "react";
290
- import { renderReact } from "retex/react";
291
- import { parse, tokenize } from "retex";
333
+ import { renderReact } from "@regmisatyam/retex/react";
334
+ import { parse, tokenize } from "@regmisatyam/retex";
292
335
 
293
336
  const { ast } = parse(tokenize(source).tokens);
294
337
  const element = renderReact(ast, {
@@ -321,18 +364,60 @@ const pdf2 = await engine.toPdf(source, {
321
364
 
322
365
  ---
323
366
 
367
+ ## Libraries
368
+
369
+ ReTeX is blank by default; **libraries** add styling, opt-in. Activate them two
370
+ ways — and you can mix both.
371
+
372
+ **From the document** (LaTeX-style, in the preamble):
373
+
374
+ ```latex
375
+ \usepackage{colors} %% \textcolor, \themecolor
376
+ \usepackage{fonts, shapes} %% comma-separate to import several at once
377
+ \textcolor{#2563eb}{Now available} — \hrule
378
+ ```
379
+
380
+ `\use{...}` is an alias. If you use a gated command without its library, ReTeX
381
+ tells you exactly which `\usepackage{...}` to add.
382
+
383
+ **From code** (great for app integrations):
384
+
385
+ ```ts
386
+ import { ReTeXEngine, colorsLibrary, shapesLibrary } from "@regmisatyam/retex";
387
+
388
+ const engine = new ReTeXEngine({ plugins: [colorsLibrary, shapesLibrary] });
389
+ // or: new ReTeXEngine().use(colorsLibrary).use(shapesLibrary)
390
+ engine.toHtml("\\textcolor{#2563eb}{Hi} \\hrule");
391
+ ```
392
+
393
+ ### Built-in libraries
394
+
395
+ | Library | `\usepackage{…}` | Provides |
396
+ | -------------------------------- | ---------------- | ------------------------------------------------------------------------- |
397
+ | `colorsLibrary` | `colors` | `\textcolor`, `\themecolor`, plus a `palettes` catalog |
398
+ | `fontsLibrary` | `fonts` | `\fontfamily`, `\fontsize`, `\small`…`\Huge`, `\normalsize`, `\ttfamily`, plus `fontStacks` / `fontTheme` |
399
+ | `shapesLibrary` | `shapes` | `\hrule`, `\divider`, `\dashrule`, `\dotrule`, `\thickrule`, `\doublerule`, `\vspace`, `\hspace` |
400
+ | `modern` / `classic` / `compact` | same name | A complete styled look **plus** all three toolkits above |
401
+
402
+ Theme packs are the quickest path to a polished document: `\usepackage{modern}`
403
+ gives you the full look *and* every styling command. Bring your own library with
404
+ `engine.provideLibrary(myLib)` (addressable by `\usepackage`) or `engine.use`.
405
+
406
+ ---
407
+
324
408
  ## Theming
325
409
 
326
- Themes are plain data, deep-merged over the default. Pass a full `Theme` or a
327
- partial patch.
410
+ Themes are plain data, deep-merged over the **blank** default so every field is
411
+ optional, and what you don't set stays unstyled. Pass a full `Theme` or a partial
412
+ patch.
328
413
 
329
414
  ```ts
330
- import { ReTeXEngine, modernTheme } from "retex";
415
+ import { ReTeXEngine, modernTheme } from "@regmisatyam/retex";
331
416
 
332
417
  // Use a preset:
333
418
  const a = new ReTeXEngine({ theme: modernTheme });
334
419
 
335
- // Or a partial override (deep-merged over the default):
420
+ // Or a partial override (deep-merged over blank):
336
421
  const b = new ReTeXEngine({
337
422
  theme: {
338
423
  name: "brand",
@@ -342,14 +427,16 @@ const b = new ReTeXEngine({
342
427
  },
343
428
  });
344
429
 
345
- // Swap at runtime:
430
+ // Swap at runtime (an anonymous patch merges over the current theme):
346
431
  b.setTheme({ colors: { primary: "#059669" } });
347
432
  ```
348
433
 
349
- Presets: `defaultTheme`, `modernTheme`, `classicTheme`, `compactTheme` (also
350
- available by name via `getTheme("modern")` or the `themes` map). Every theme
351
- color is exposed as a CSS variable (`--retex-color-<token>`), usable from
352
- `\themecolor{token}{…}`.
434
+ Presets: `blankTheme` (the default), `modernTheme`, `classicTheme`,
435
+ `compactTheme` (also available by name via `getTheme("modern")` or the `themes`
436
+ map). Every theme color is exposed as a CSS variable (`--retex-color-<token>`),
437
+ usable from `\themecolor{token}{…}`. A blank theme still renders a readable
438
+ document — the structural CSS is always emitted; only decoration is left to a
439
+ theme/library.
353
440
 
354
441
  ---
355
442
 
@@ -360,7 +447,7 @@ theme patches. The simplest case — a custom inline command with a render
360
447
  function and no custom AST node:
361
448
 
362
449
  ```ts
363
- import { ReTeXEngine } from "retex";
450
+ import { ReTeXEngine } from "@regmisatyam/retex";
364
451
 
365
452
  const engine = new ReTeXEngine();
366
453
 
@@ -384,7 +471,7 @@ engine.toHtml("\\badge{Open to work}");
384
471
  Or package it as a reusable plugin and install with `.use()`:
385
472
 
386
473
  ```ts
387
- import { ReTeXEngine, badgePlugin, ratingPlugin } from "retex";
474
+ import { ReTeXEngine, badgePlugin, ratingPlugin } from "@regmisatyam/retex";
388
475
 
389
476
  const engine = new ReTeXEngine({ plugins: [badgePlugin, ratingPlugin] });
390
477
  engine.toHtml("\\badge{New} \\rating{4}");
@@ -402,7 +489,7 @@ override. See **[docs/API.md](docs/API.md)** for the full `ReTeXPlugin` contract
402
489
  All methods are pure and synchronous.
403
490
 
404
491
  ```ts
405
- import { EditorService } from "retex";
492
+ import { EditorService } from "@regmisatyam/retex";
406
493
 
407
494
  const editor = new EditorService();
408
495
 
@@ -419,6 +506,70 @@ optional quick-fixes, so editors can wire up code actions and doc links.
419
506
 
420
507
  ---
421
508
 
509
+ ## Two-way preview (source mapping)
510
+
511
+ Like Overleaf's SyncTeX, ReTeX can keep an editor and its live preview in sync
512
+ in **both directions** — click an element in the preview to jump to the source
513
+ that produced it, and move the caret in the source to highlight the matching
514
+ element in the preview.
515
+
516
+ Turn it on with the `sourceMap` render option. The HTML renderer then stamps
517
+ every meaningful element with `data-rtx-pos="start:end"` (zero-based UTF-16
518
+ offsets into the source) and a `data-rtx-type` (the originating node):
519
+
520
+ ```ts
521
+ const engine = new ReTeXEngine();
522
+ engine.toHtml(source, { sourceMap: true });
523
+ // …<section data-rtx-pos="20:40" data-rtx-type="section">…
524
+ // <p class="retex-para" data-rtx-pos="101:130" data-rtx-type="para">
525
+ // Built <strong data-rtx-pos="110:128" data-rtx-type="bold">compilers</strong>.
526
+ // </p>…
527
+ ```
528
+
529
+ `sourceMap` defaults to **off**, so exported/production HTML stays clean and
530
+ unannotated — you only pay for the attributes when driving a live editor.
531
+
532
+ A few tiny, **editor-agnostic** helpers turn those attributes into sync (they
533
+ work with the real DOM or any object exposing `getAttribute`, so the core never
534
+ touches `document`):
535
+
536
+ ```ts
537
+ import {
538
+ closestSourcePos, // preview → code: nearest annotated ancestor's span
539
+ pickElementForOffset, // code → preview: tightest element under the caret
540
+ SOURCE_POS_ATTR,
541
+ } from "@regmisatyam/retex";
542
+
543
+ // Reverse sync — click the preview, select the source it came from:
544
+ preview.addEventListener("click", (e) => {
545
+ const span = closestSourcePos(e.target);
546
+ if (span) {
547
+ textarea.focus();
548
+ textarea.setSelectionRange(span.start, span.end);
549
+ }
550
+ });
551
+
552
+ // Forward sync — caret moves, highlight the matching preview element:
553
+ function onCaretMove() {
554
+ const el = pickElementForOffset(
555
+ preview.querySelectorAll(`[${SOURCE_POS_ATTR}]`),
556
+ textarea.selectionStart,
557
+ );
558
+ el?.classList.add("active");
559
+ }
560
+ ```
561
+
562
+ ### Try it
563
+
564
+ A complete, dependency-free playground ships in [`examples/playground.html`](examples/playground.html)
565
+ — a split editor/preview with both sync directions wired up:
566
+
567
+ ```bash
568
+ npm run playground # builds the library, serves it, and opens the demo
569
+ ```
570
+
571
+ ---
572
+
422
573
  ## Security
423
574
 
424
575
  ReTeX renders untrusted markup, so safety is built into every layer:
@@ -446,7 +597,7 @@ paragraph re-parses only that block while the rest are served from cache and
446
597
  cheaply re-positioned.
447
598
 
448
599
  ```ts
449
- import { IncrementalCompiler } from "retex";
600
+ import { IncrementalCompiler } from "@regmisatyam/retex";
450
601
 
451
602
  const inc = new IncrementalCompiler();
452
603
  const { ast, diagnostics, stats } = inc.compile(source);
@@ -459,6 +610,7 @@ const { ast, diagnostics, stats } = inc.compile(source);
459
610
 
460
611
  ```bash
461
612
  npm run build # bundle with tsup (ESM + CJS + d.ts)
613
+ npm run playground # build + serve the two-way preview demo
462
614
  npm run typecheck # tsc --noEmit
463
615
  npm test # run the test suite (vitest)
464
616
  npm run test:watch # watch mode