@cristianormazabal/triton-latex 0.1.13 → 0.1.15

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
@@ -104,6 +104,8 @@ by any character (here `|`) and an optional `\includegraphics` key list:
104
104
  | `\tritonsetup{<opts>}` | Default `\includegraphics` options (default `width=\linewidth`). |
105
105
  | `\tritoncli{<cmd>}` | CLI invocation (default `triton-latex`). |
106
106
  | `\tritontheme{<name>}` | Theme preset passed to the CLI for inline renders. |
107
+ | `\tritonthemefile{<path>}` | Load an external `.triton-theme.json` by file path. |
108
+ | `\tritonthemesdir{<dir>}` | Scan a directory for `*.triton-theme.json` files. |
107
109
  | `\tritonscale{<n>}` | Scale passed to the CLI (default `1`). |
108
110
  | `\tritoncachedir{<dir>}` | Render cache directory (default `\jobname.triton-cache`). |
109
111
  | `\triton[<opts>]{<name>}` | **Precompile fallback:** `\includegraphics` of `<dir>/<name>.pdf`. |
@@ -148,6 +150,49 @@ node dist/cli.cjs render diagram.mmd -o out.pdf --theme executive --scale 2
148
150
 
149
151
  Installed as the `triton-latex` bin when the package is linked.
150
152
 
153
+ ## External themes
154
+
155
+ Use a `.triton-theme.json` file to apply a custom colour palette and typography
156
+ to every inline diagram in your document.
157
+
158
+ ### `.sty` macros
159
+
160
+ ```latex
161
+ \tritonthemefile{.triton/themes/my-brand.triton-theme.json}
162
+ % — OR —
163
+ \tritonthemesdir{.triton/themes} % scan a directory, then use \tritontheme{name}
164
+ ```
165
+
166
+ `\tritonthemefile{<path>}` passes `--theme-file <path>` to the CLI for every
167
+ inline `\begin{triton}` render in the document. `\tritonthemesdir{<dir>}` passes
168
+ `--themes-dir <dir>`; combine it with `\tritontheme{<name>}` to select a theme by
169
+ name from the discovered registry.
170
+
171
+ ### CLI flags
172
+
173
+ ```sh
174
+ # Load a specific theme file
175
+ node dist/cli.cjs render diagram.mmd -o out.pdf \
176
+ --theme-file .triton/themes/my-brand.triton-theme.json
177
+
178
+ # Scan a directory and select by name
179
+ node dist/cli.cjs render diagram.mmd -o out.pdf \
180
+ --themes-dir .triton/themes --theme my-brand
181
+ ```
182
+
183
+ ### ⚠️ Cache caveat
184
+
185
+ The cache key includes the theme-file **path**, not its **content**. After editing
186
+ a `.triton-theme.json` in place, clear the cache before recompiling:
187
+
188
+ ```sh
189
+ latexmk -C # preferred
190
+ rm -r <jobname>.triton-cache # or manually
191
+ ```
192
+
193
+ See [docs/external-themes.md](../docs/external-themes.md) for the full format
194
+ reference, field list, built-in preset names, and cross-host worked example.
195
+
151
196
  ## Overleaf / no-shell-escape fallback
152
197
 
153
198
  Overleaf (and any compile with shell-escape disabled) can't run the CLI, so render
package/dist/cli.cjs CHANGED
@@ -24,8 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli.ts
27
- var import_node_fs = require("node:fs");
28
- var import_node_path = require("node:path");
27
+ var import_node_fs2 = require("node:fs");
28
+ var import_node_path3 = require("node:path");
29
+ var import_node_url = require("node:url");
29
30
 
30
31
  // ../src/contracts/animations.ts
31
32
  var CONNECTOR_ANIMATIONS = [
@@ -44097,6 +44098,211 @@ var svgRenderer = {
44097
44098
  render: renderSVG
44098
44099
  };
44099
44100
 
44101
+ // ../src/theme/validate.ts
44102
+ function isBuiltinThemeName(name) {
44103
+ return themePresetNames.includes(name);
44104
+ }
44105
+ var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
44106
+ "name",
44107
+ "base",
44108
+ "palette",
44109
+ "typography",
44110
+ "spacing",
44111
+ "edges",
44112
+ "panel"
44113
+ ]);
44114
+ var PALETTE_KEYS = /* @__PURE__ */ new Set([
44115
+ "primary",
44116
+ "secondary",
44117
+ "background",
44118
+ "surface",
44119
+ "border",
44120
+ "text",
44121
+ "textMuted",
44122
+ "success",
44123
+ "warning",
44124
+ "error"
44125
+ ]);
44126
+ var TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
44127
+ "fontFamily",
44128
+ "monoFamily",
44129
+ "baseFontSize",
44130
+ "titleFontSize",
44131
+ "smallFontSize",
44132
+ "lineHeight"
44133
+ ]);
44134
+ var SPACING_KEYS = /* @__PURE__ */ new Set([
44135
+ "unit",
44136
+ "nodePadding",
44137
+ "nodeGap",
44138
+ "diagramMargin"
44139
+ ]);
44140
+ var EDGES_KEYS = /* @__PURE__ */ new Set([
44141
+ "strokeWidth",
44142
+ "arrowSize",
44143
+ "labelFontSize",
44144
+ "curveTension"
44145
+ ]);
44146
+ var PANEL_KEYS = /* @__PURE__ */ new Set([
44147
+ "titleAlign",
44148
+ "titlePosition",
44149
+ "titleChrome"
44150
+ ]);
44151
+ var HEX_COLOR = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
44152
+ var SLUG = /^[a-z0-9-]+$/;
44153
+ var CSS_INJECTION = /url\s*\(|expression\s*\(/i;
44154
+ function isPlainObject(v) {
44155
+ return v !== null && typeof v === "object" && !Array.isArray(v);
44156
+ }
44157
+ function fail(message) {
44158
+ return err("THEME_VALIDATION_ERROR", message);
44159
+ }
44160
+ function validatePalette(obj) {
44161
+ if (!isPlainObject(obj)) return fail('"palette" must be a plain object');
44162
+ for (const key of Object.keys(obj)) {
44163
+ if (!PALETTE_KEYS.has(key)) return fail(`Unknown key in palette: "${key}"`);
44164
+ }
44165
+ for (const key of Object.keys(obj)) {
44166
+ const val = obj[key];
44167
+ if (typeof val !== "string" || !HEX_COLOR.test(val)) {
44168
+ return fail(`palette.${key} must be a CSS hex color (e.g. "#RGB" or "#RRGGBB"), got ${JSON.stringify(val)}`);
44169
+ }
44170
+ }
44171
+ return ok(obj);
44172
+ }
44173
+ function validateTypography(obj) {
44174
+ if (!isPlainObject(obj)) return fail('"typography" must be a plain object');
44175
+ for (const key of Object.keys(obj)) {
44176
+ if (!TYPOGRAPHY_KEYS.has(key)) return fail(`Unknown key in typography: "${key}"`);
44177
+ }
44178
+ const stringFields = ["fontFamily", "monoFamily"];
44179
+ for (const key of stringFields) {
44180
+ if (key in obj) {
44181
+ const val = obj[key];
44182
+ if (typeof val !== "string") return fail(`typography.${key} must be a string`);
44183
+ if (CSS_INJECTION.test(val)) return fail(`typography.${key} contains disallowed CSS expression`);
44184
+ }
44185
+ }
44186
+ const positiveFields = ["baseFontSize", "titleFontSize", "smallFontSize", "lineHeight"];
44187
+ for (const key of positiveFields) {
44188
+ if (key in obj) {
44189
+ const val = obj[key];
44190
+ if (typeof val !== "number" || val <= 0) {
44191
+ return fail(`typography.${key} must be a positive number, got ${JSON.stringify(val)}`);
44192
+ }
44193
+ }
44194
+ }
44195
+ return ok(obj);
44196
+ }
44197
+ function validateSpacing(obj) {
44198
+ if (!isPlainObject(obj)) return fail('"spacing" must be a plain object');
44199
+ for (const key of Object.keys(obj)) {
44200
+ if (!SPACING_KEYS.has(key)) return fail(`Unknown key in spacing: "${key}"`);
44201
+ }
44202
+ for (const key of Object.keys(obj)) {
44203
+ const val = obj[key];
44204
+ if (typeof val !== "number" || val < 0) {
44205
+ return fail(`spacing.${key} must be a non-negative number, got ${JSON.stringify(val)}`);
44206
+ }
44207
+ }
44208
+ return ok(obj);
44209
+ }
44210
+ function validateEdges(obj) {
44211
+ if (!isPlainObject(obj)) return fail('"edges" must be a plain object');
44212
+ for (const key of Object.keys(obj)) {
44213
+ if (!EDGES_KEYS.has(key)) return fail(`Unknown key in edges: "${key}"`);
44214
+ }
44215
+ const nonNegativeFields = ["strokeWidth", "arrowSize", "labelFontSize"];
44216
+ for (const key of nonNegativeFields) {
44217
+ if (key in obj) {
44218
+ const val = obj[key];
44219
+ if (typeof val !== "number" || val < 0) {
44220
+ return fail(`edges.${key} must be a non-negative number, got ${JSON.stringify(val)}`);
44221
+ }
44222
+ }
44223
+ }
44224
+ if ("curveTension" in obj) {
44225
+ const val = obj["curveTension"];
44226
+ if (typeof val !== "number" || val < 0 || val > 1) {
44227
+ return fail(`edges.curveTension must be a number between 0 and 1, got ${JSON.stringify(val)}`);
44228
+ }
44229
+ }
44230
+ return ok(obj);
44231
+ }
44232
+ function validatePanel(obj) {
44233
+ if (!isPlainObject(obj)) return fail('"panel" must be a plain object');
44234
+ for (const key of Object.keys(obj)) {
44235
+ if (!PANEL_KEYS.has(key)) return fail(`Unknown key in panel: "${key}"`);
44236
+ }
44237
+ if ("titleAlign" in obj) {
44238
+ const val = obj["titleAlign"];
44239
+ if (val !== "left" && val !== "center" && val !== "right") {
44240
+ return fail(`panel.titleAlign must be "left", "center", or "right", got ${JSON.stringify(val)}`);
44241
+ }
44242
+ }
44243
+ if ("titlePosition" in obj) {
44244
+ const val = obj["titlePosition"];
44245
+ if (val !== "inside" && val !== "on-border" && val !== "above") {
44246
+ return fail(`panel.titlePosition must be "inside", "on-border", or "above", got ${JSON.stringify(val)}`);
44247
+ }
44248
+ }
44249
+ if ("titleChrome" in obj) {
44250
+ const val = obj["titleChrome"];
44251
+ if (val !== "none" && val !== "box" && val !== "pill") {
44252
+ return fail(`panel.titleChrome must be "none", "box", or "pill", got ${JSON.stringify(val)}`);
44253
+ }
44254
+ }
44255
+ return ok(obj);
44256
+ }
44257
+ function validateThemeInput(json) {
44258
+ if (!isPlainObject(json)) {
44259
+ return fail(`ThemeInput must be a non-null plain object, got ${json === null ? "null" : Array.isArray(json) ? "array" : typeof json}`);
44260
+ }
44261
+ for (const key of Object.keys(json)) {
44262
+ if (!TOP_LEVEL_KEYS.has(key)) return fail(`Unknown top-level key: "${key}"`);
44263
+ }
44264
+ const result = {};
44265
+ if ("name" in json) {
44266
+ const v = json["name"];
44267
+ if (typeof v !== "string") return fail(`"name" must be a string, got ${JSON.stringify(v)}`);
44268
+ if (v.length < 1 || v.length > 64) return fail(`"name" must be 1\u201364 characters, got length ${v.length}`);
44269
+ if (!SLUG.test(v)) return fail(`"name" must match ^[a-z0-9-]+$, got ${JSON.stringify(v)}`);
44270
+ result["name"] = v;
44271
+ }
44272
+ if ("base" in json) {
44273
+ const v = json["base"];
44274
+ if (typeof v !== "string") return fail(`"base" must be a string, got ${JSON.stringify(v)}`);
44275
+ if (!isBuiltinThemeName(v)) return fail(`"base" must be a built-in preset name; "${v}" is not recognised`);
44276
+ result["base"] = v;
44277
+ }
44278
+ if ("palette" in json) {
44279
+ const r = validatePalette(json["palette"]);
44280
+ if (!r.ok) return r;
44281
+ result["palette"] = r.value;
44282
+ }
44283
+ if ("typography" in json) {
44284
+ const r = validateTypography(json["typography"]);
44285
+ if (!r.ok) return r;
44286
+ result["typography"] = r.value;
44287
+ }
44288
+ if ("spacing" in json) {
44289
+ const r = validateSpacing(json["spacing"]);
44290
+ if (!r.ok) return r;
44291
+ result["spacing"] = r.value;
44292
+ }
44293
+ if ("edges" in json) {
44294
+ const r = validateEdges(json["edges"]);
44295
+ if (!r.ok) return r;
44296
+ result["edges"] = r.value;
44297
+ }
44298
+ if ("panel" in json) {
44299
+ const r = validatePanel(json["panel"]);
44300
+ if (!r.ok) return r;
44301
+ result["panel"] = r.value;
44302
+ }
44303
+ return ok(result);
44304
+ }
44305
+
44100
44306
  // ../src/frontend/index.ts
44101
44307
  registerDiagram("flowchart", flowchart);
44102
44308
  registerDiagram("timeline", timeline);
@@ -44229,11 +44435,144 @@ function svgToPdf(svg, opts2 = {}) {
44229
44435
  });
44230
44436
  }
44231
44437
 
44438
+ // src/theme-resolve.ts
44439
+ var import_node_path2 = require("node:path");
44440
+
44441
+ // ../src/theme/discover.ts
44442
+ var import_node_fs = require("node:fs");
44443
+ var import_node_path = require("node:path");
44444
+ var THEME_SUFFIX = ".triton-theme.json";
44445
+ var SLUG2 = /^[a-z0-9-]+$/;
44446
+ function deriveNameFromFilename(filename) {
44447
+ return (0, import_node_path.basename)(filename, THEME_SUFFIX);
44448
+ }
44449
+ function loadThemeFile(filePath) {
44450
+ let raw;
44451
+ try {
44452
+ raw = (0, import_node_fs.readFileSync)(filePath, "utf8");
44453
+ } catch (cause) {
44454
+ return err("THEME_VALIDATION_ERROR", `Cannot read theme file "${filePath}": ${String(cause)}`, cause);
44455
+ }
44456
+ let parsed;
44457
+ try {
44458
+ parsed = JSON.parse(raw);
44459
+ } catch (cause) {
44460
+ return err("THEME_VALIDATION_ERROR", `Invalid JSON in theme file "${filePath}": ${String(cause)}`, cause);
44461
+ }
44462
+ const validated = validateThemeInput(parsed);
44463
+ if (!validated.ok) {
44464
+ return err(
44465
+ "THEME_VALIDATION_ERROR",
44466
+ `Theme file "${filePath}" failed validation: ${validated.error.message}`,
44467
+ validated.error
44468
+ );
44469
+ }
44470
+ const input = validated.value;
44471
+ const baseName = parsed["base"] ?? "default";
44472
+ const base = getThemePreset(baseName);
44473
+ const resolved = resolveTheme(input, base);
44474
+ return ok(resolved);
44475
+ }
44476
+ function discoverThemes(dir) {
44477
+ const themes = /* @__PURE__ */ new Map();
44478
+ const warnings = [];
44479
+ let entries;
44480
+ try {
44481
+ entries = (0, import_node_fs.readdirSync)(dir);
44482
+ } catch (cause) {
44483
+ warnings.push(`Cannot read themes directory "${dir}": ${String(cause)}`);
44484
+ return { themes, warnings };
44485
+ }
44486
+ const themeFiles = entries.filter((e) => e.endsWith(THEME_SUFFIX));
44487
+ for (const filename of themeFiles) {
44488
+ const filePath = (0, import_node_path.join)(dir, filename);
44489
+ const result = loadThemeFile(filePath);
44490
+ if (!result.ok) {
44491
+ warnings.push(result.error.message);
44492
+ continue;
44493
+ }
44494
+ const resolved = result.value;
44495
+ const name = extractNameFromFile(filePath) ?? deriveNameFromFilename(filename);
44496
+ if (!SLUG2.test(name)) {
44497
+ warnings.push(`Theme file "${filename}": derived name "${name}" is not a valid slug (^[a-z0-9-]+$); skipped`);
44498
+ continue;
44499
+ }
44500
+ if (isBuiltinThemeName(name)) {
44501
+ warnings.push(`Theme "${name}" shadows a built-in preset; skipped`);
44502
+ continue;
44503
+ }
44504
+ if (themes.has(name)) {
44505
+ warnings.push(`Duplicate theme name "${name}": "${filename}" overrides an earlier definition`);
44506
+ }
44507
+ themes.set(name, resolved);
44508
+ }
44509
+ return { themes, warnings };
44510
+ }
44511
+ function extractNameFromFile(filePath) {
44512
+ try {
44513
+ const raw = (0, import_node_fs.readFileSync)(filePath, "utf8");
44514
+ const parsed = JSON.parse(raw);
44515
+ return typeof parsed["name"] === "string" ? parsed["name"] : null;
44516
+ } catch {
44517
+ return null;
44518
+ }
44519
+ }
44520
+ function findTritonThemesDir(startDir) {
44521
+ const MAX_LEVELS = 10;
44522
+ let current = (0, import_node_path.resolve)(startDir);
44523
+ for (let level = 0; level < MAX_LEVELS; level++) {
44524
+ const candidate = (0, import_node_path.join)(current, ".triton", "themes");
44525
+ try {
44526
+ if ((0, import_node_fs.existsSync)(candidate) && (0, import_node_fs.statSync)(candidate).isDirectory()) {
44527
+ return candidate;
44528
+ }
44529
+ } catch {
44530
+ }
44531
+ const parent = (0, import_node_path.resolve)((0, import_node_path.join)(current, ".."));
44532
+ if (parent === current) break;
44533
+ current = parent;
44534
+ }
44535
+ return void 0;
44536
+ }
44537
+
44538
+ // src/theme-resolve.ts
44539
+ function resolveCliTheme(args, inputDir) {
44540
+ if (args.themeFile) {
44541
+ const result = loadThemeFile((0, import_node_path2.resolve)(args.themeFile));
44542
+ if (!result.ok) {
44543
+ throw new Error(`--theme-file: ${result.error.message}`);
44544
+ }
44545
+ return result.value;
44546
+ }
44547
+ const registry4 = /* @__PURE__ */ new Map();
44548
+ const autoDir = findTritonThemesDir(inputDir);
44549
+ if (autoDir) {
44550
+ const { themes, warnings } = discoverThemes(autoDir);
44551
+ for (const [name, theme] of themes) registry4.set(name, theme);
44552
+ for (const w of warnings) console.error(`warning: ${w}`);
44553
+ }
44554
+ if (args.themesDir) {
44555
+ const { themes, warnings } = discoverThemes((0, import_node_path2.resolve)(args.themesDir));
44556
+ for (const [name, theme] of themes) registry4.set(name, theme);
44557
+ for (const w of warnings) console.error(`warning: ${w}`);
44558
+ }
44559
+ if (args.theme) {
44560
+ if (registry4.has(args.theme)) {
44561
+ return registry4.get(args.theme);
44562
+ }
44563
+ return getThemePreset(args.theme);
44564
+ }
44565
+ return void 0;
44566
+ }
44567
+
44232
44568
  // src/cli.ts
44569
+ var import_meta = {};
44233
44570
  function parseArgs(argv) {
44234
44571
  const positionals = [];
44235
44572
  let out;
44236
44573
  let theme;
44574
+ let themeFile;
44575
+ let themesDir;
44237
44576
  let scale = 1;
44238
44577
  let help = false;
44239
44578
  for (let i = 0; i < argv.length; i++) {
@@ -44246,6 +44585,12 @@ function parseArgs(argv) {
44246
44585
  case "--theme":
44247
44586
  theme = argv[++i];
44248
44587
  break;
44588
+ case "--theme-file":
44589
+ themeFile = argv[++i];
44590
+ break;
44591
+ case "--themes-dir":
44592
+ themesDir = argv[++i];
44593
+ break;
44249
44594
  case "--scale":
44250
44595
  scale = Number(argv[++i]) || 1;
44251
44596
  break;
@@ -44257,7 +44602,7 @@ function parseArgs(argv) {
44257
44602
  positionals.push(a);
44258
44603
  }
44259
44604
  }
44260
- return { command: positionals.shift(), positionals, out, theme, scale, help };
44605
+ return { command: positionals.shift(), positionals, out, theme, themeFile, themesDir, scale, help };
44261
44606
  }
44262
44607
  var USAGE = `triton-latex \u2014 render Triton diagrams to vector PDF for LaTeX.
44263
44608
 
@@ -44266,50 +44611,63 @@ Usage:
44266
44611
  triton-latex render-dir <srcDir> -o <outDir> [options]
44267
44612
 
44268
44613
  Options:
44269
- -o, --out <path> Output file (render) or output directory (render-dir).
44270
- --theme <name> Theme preset (default, executive, minimal, \u2026).
44271
- --scale <number> Uniform scale for the PDF page box (default 1).
44272
- -h, --help Show this help.
44614
+ -o, --out <path> Output file (render) or output directory (render-dir).
44615
+ --theme <name> Theme preset name (default, executive, minimal, \u2026) or
44616
+ a name from the discovered/loaded custom theme registry.
44617
+ --theme-file <path> Path to a .triton-theme.json file. Takes precedence over
44618
+ --theme and --themes-dir. Fails loudly on load error.
44619
+ --themes-dir <dir> Directory of .triton-theme.json files to register; merged
44620
+ on top of auto-discovered .triton/themes/ (overrides on
44621
+ name collision). Use with --theme <name>.
44622
+ --scale <number> Uniform scale for the PDF page box (default 1).
44623
+ -h, --help Show this help.
44624
+
44625
+ Theme resolution order:
44626
+ 1. --theme-file <path> \u2192 load that exact file (errors are fatal)
44627
+ 2. --themes-dir + auto-discovery (.triton/themes/ ancestor walk) \u2192 build registry
44628
+ --theme <name> \u2192 look up name in registry, then fall back to built-in preset
44629
+ 3. No theme flags \u2192 core uses diagram frontmatter / default preset
44273
44630
 
44274
44631
  Examples:
44275
44632
  triton-latex render diagram.mmd -o figures/diagram.pdf
44276
- triton-latex render diagram.mmd -o diagram.svg # SVG pass-through
44277
- triton-latex render-dir diagrams/ -o figures/ # batch \u2192 *.pdf
44633
+ triton-latex render diagram.mmd -o diagram.svg # SVG pass-through
44634
+ triton-latex render diagram.mmd -o out.pdf --theme-file my.triton-theme.json
44635
+ triton-latex render diagram.mmd -o out.pdf --themes-dir .triton/themes --theme acme
44636
+ triton-latex render-dir diagrams/ -o figures/ # batch \u2192 *.pdf
44278
44637
  `;
44279
44638
  var DIAGRAM_EXTS = /* @__PURE__ */ new Set([".triton", ".mmd"]);
44280
- function renderToSvg(source, themeName) {
44281
- const themeInput = themeName ? getThemePreset(themeName) : void 0;
44639
+ function renderToSvg(source, themeInput) {
44282
44640
  const result = renderSync(source, themeInput);
44283
44641
  if (!result.ok) {
44284
44642
  throw new Error(`${result.error.code}: ${result.error.message}`);
44285
44643
  }
44286
44644
  return result.value;
44287
44645
  }
44288
- async function renderFile(inputPath, outPath, themeName, scale) {
44289
- const source = (0, import_node_fs.readFileSync)(inputPath, "utf8");
44290
- const svg = renderToSvg(source, themeName);
44291
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)((0, import_node_path.resolve)(outPath)), { recursive: true });
44292
- if ((0, import_node_path.extname)(outPath).toLowerCase() === ".svg") {
44293
- (0, import_node_fs.writeFileSync)(outPath, svg, "utf8");
44646
+ async function renderFile(inputPath, outPath, themeInput, scale) {
44647
+ const source = (0, import_node_fs2.readFileSync)(inputPath, "utf8");
44648
+ const svg = renderToSvg(source, themeInput);
44649
+ (0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)((0, import_node_path3.resolve)(outPath)), { recursive: true });
44650
+ if ((0, import_node_path3.extname)(outPath).toLowerCase() === ".svg") {
44651
+ (0, import_node_fs2.writeFileSync)(outPath, svg, "utf8");
44294
44652
  return;
44295
44653
  }
44296
44654
  const pdf = await svgToPdf(svg, { scale });
44297
- (0, import_node_fs.writeFileSync)(outPath, pdf);
44655
+ (0, import_node_fs2.writeFileSync)(outPath, pdf);
44298
44656
  }
44299
- async function renderDir(srcDir, outDir, themeName, scale) {
44300
- (0, import_node_fs.mkdirSync)(outDir, { recursive: true });
44301
- const entries = (0, import_node_fs.readdirSync)(srcDir).filter((f2) => {
44302
- const full = (0, import_node_path.join)(srcDir, f2);
44303
- return (0, import_node_fs.statSync)(full).isFile() && DIAGRAM_EXTS.has((0, import_node_path.extname)(f2).toLowerCase());
44657
+ async function renderDir(srcDir, outDir, themeInput, scale) {
44658
+ (0, import_node_fs2.mkdirSync)(outDir, { recursive: true });
44659
+ const entries = (0, import_node_fs2.readdirSync)(srcDir).filter((f2) => {
44660
+ const full = (0, import_node_path3.join)(srcDir, f2);
44661
+ return (0, import_node_fs2.statSync)(full).isFile() && DIAGRAM_EXTS.has((0, import_node_path3.extname)(f2).toLowerCase());
44304
44662
  });
44305
44663
  let rendered = 0;
44306
44664
  const failed = [];
44307
44665
  for (const file of entries) {
44308
- const inputPath = (0, import_node_path.join)(srcDir, file);
44309
- const name = (0, import_node_path.basename)(file, (0, import_node_path.extname)(file));
44310
- const outPath = (0, import_node_path.join)(outDir, `${name}.pdf`);
44666
+ const inputPath = (0, import_node_path3.join)(srcDir, file);
44667
+ const name = (0, import_node_path3.basename)(file, (0, import_node_path3.extname)(file));
44668
+ const outPath = (0, import_node_path3.join)(outDir, `${name}.pdf`);
44311
44669
  try {
44312
- await renderFile(inputPath, outPath, themeName, scale);
44670
+ await renderFile(inputPath, outPath, themeInput, scale);
44313
44671
  console.log(` \u2713 ${file} \u2192 ${name}.pdf`);
44314
44672
  rendered++;
44315
44673
  } catch (cause) {
@@ -44333,7 +44691,15 @@ async function main() {
44333
44691
  console.error(USAGE);
44334
44692
  return 1;
44335
44693
  }
44336
- await renderFile((0, import_node_path.resolve)(input), (0, import_node_path.resolve)(args.out), args.theme, args.scale);
44694
+ const inputPath = (0, import_node_path3.resolve)(input);
44695
+ let themeInput;
44696
+ try {
44697
+ themeInput = resolveCliTheme(args, (0, import_node_path3.dirname)(inputPath));
44698
+ } catch (e) {
44699
+ console.error(`error: ${e.message}`);
44700
+ return 1;
44701
+ }
44702
+ await renderFile(inputPath, (0, import_node_path3.resolve)(args.out), themeInput, args.scale);
44337
44703
  console.log(`\u2713 ${input} \u2192 ${args.out}`);
44338
44704
  return 0;
44339
44705
  }
@@ -44344,10 +44710,18 @@ async function main() {
44344
44710
  console.error(USAGE);
44345
44711
  return 1;
44346
44712
  }
44713
+ const resolvedSrcDir = (0, import_node_path3.resolve)(srcDir);
44714
+ let themeInput;
44715
+ try {
44716
+ themeInput = resolveCliTheme(args, resolvedSrcDir);
44717
+ } catch (e) {
44718
+ console.error(`error: ${e.message}`);
44719
+ return 1;
44720
+ }
44347
44721
  const { rendered, failed } = await renderDir(
44348
- (0, import_node_path.resolve)(srcDir),
44349
- (0, import_node_path.resolve)(args.out),
44350
- args.theme,
44722
+ resolvedSrcDir,
44723
+ (0, import_node_path3.resolve)(args.out),
44724
+ themeInput,
44351
44725
  args.scale
44352
44726
  );
44353
44727
  console.log(`
@@ -44359,9 +44733,11 @@ ${rendered} rendered, ${failed.length} failed.`);
44359
44733
  console.error(USAGE);
44360
44734
  return 1;
44361
44735
  }
44362
- main().then((code) => process.exit(code)).catch((cause) => {
44363
- const message = cause instanceof Error ? cause.message : String(cause);
44364
- console.error(`error: ${message}`);
44365
- process.exit(1);
44366
- });
44736
+ if (process.argv[1] === (0, import_node_url.fileURLToPath)(import_meta.url)) {
44737
+ main().then((code) => process.exit(code)).catch((cause) => {
44738
+ const message = cause instanceof Error ? cause.message : String(cause);
44739
+ console.error(`error: ${message}`);
44740
+ process.exit(1);
44741
+ });
44742
+ }
44367
44743
  //# sourceMappingURL=cli.cjs.map