@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/dist/react.cjs CHANGED
@@ -1,18 +1,25 @@
1
1
  'use strict';
2
2
 
3
3
  // src/theme/default.ts
4
- var defaultTheme = {
5
- name: "default",
6
- colors: {
7
- primary: "#2563eb",
8
- secondary: "#64748b",
9
- text: "#1e293b",
10
- muted: "#64748b",
11
- background: "#ffffff",
12
- border: "#e2e8f0",
13
- accent: "#2563eb",
14
- success: "#16a34a"
15
- },
4
+ var blankTheme = {
5
+ name: "blank"
6
+ };
7
+ var defaultTheme = blankTheme;
8
+
9
+ // src/theme/themes.ts
10
+ function resolveTheme(partial, base = defaultTheme) {
11
+ return {
12
+ name: partial?.name ?? base.name,
13
+ colors: { ...base.colors, ...partial?.colors },
14
+ fonts: { ...base.fonts, ...partial?.fonts },
15
+ fontSizes: { ...base.fontSizes, ...partial?.fontSizes },
16
+ spacing: { ...base.spacing, ...partial?.spacing },
17
+ page: { ...base.page, ...partial?.page },
18
+ sectionStyle: partial?.sectionStyle ?? base.sectionStyle,
19
+ headings: { ...base.headings, ...partial?.headings }
20
+ };
21
+ }
22
+ var styledBase = {
16
23
  fonts: {
17
24
  heading: '"Inter", "Helvetica Neue", Helvetica, Arial, sans-serif',
18
25
  body: '"Inter", "Helvetica Neue", Helvetica, Arial, sans-serif',
@@ -27,41 +34,36 @@ var defaultTheme = {
27
34
  name: "26pt",
28
35
  section: "13pt"
29
36
  },
30
- spacing: {
31
- unit: "4px",
32
- section: "1.1rem",
33
- item: "0.28rem",
34
- page: "0.55in"
35
- },
36
- page: {
37
- size: "Letter",
38
- margin: "0.55in",
39
- maxWidth: "8.5in"
40
- },
41
- sectionStyle: "rule"
37
+ spacing: { unit: "4px", section: "1.1rem", item: "0.28rem", page: "0.55in" },
38
+ page: { size: "Letter", margin: "0.55in", maxWidth: "8.5in" },
39
+ headings: { transform: "uppercase", tracking: "0.06em", nameTracking: "-0.01em" }
42
40
  };
43
-
44
- // src/theme/themes.ts
45
- function resolveTheme(partial, base = defaultTheme) {
46
- if (!partial) return base;
47
- return {
48
- name: partial.name ?? base.name,
49
- colors: { ...base.colors, ...partial.colors },
50
- fonts: { ...base.fonts, ...partial.fonts },
51
- fontSizes: { ...base.fontSizes, ...partial.fontSizes },
52
- spacing: { ...base.spacing, ...partial.spacing },
53
- page: { ...base.page, ...partial.page },
54
- sectionStyle: partial.sectionStyle ?? base.sectionStyle
55
- };
56
- }
57
41
  resolveTheme({
42
+ ...styledBase,
58
43
  name: "modern",
59
- colors: { primary: "#7c3aed", accent: "#7c3aed", text: "#111827" },
44
+ colors: {
45
+ primary: "#7c3aed",
46
+ secondary: "#64748b",
47
+ text: "#111827",
48
+ muted: "#64748b",
49
+ background: "#ffffff",
50
+ border: "#e2e8f0",
51
+ accent: "#7c3aed"
52
+ },
60
53
  sectionStyle: "underline"
61
54
  });
62
55
  resolveTheme({
56
+ ...styledBase,
63
57
  name: "classic",
64
- colors: { primary: "#111827", secondary: "#4b5563", accent: "#111827" },
58
+ colors: {
59
+ primary: "#111827",
60
+ secondary: "#4b5563",
61
+ text: "#1e293b",
62
+ muted: "#4b5563",
63
+ background: "#ffffff",
64
+ border: "#cbd5e1",
65
+ accent: "#111827"
66
+ },
65
67
  fonts: {
66
68
  heading: 'Georgia, "Times New Roman", serif',
67
69
  body: 'Georgia, "Times New Roman", serif',
@@ -70,7 +72,17 @@ resolveTheme({
70
72
  sectionStyle: "rule"
71
73
  });
72
74
  resolveTheme({
75
+ ...styledBase,
73
76
  name: "compact",
77
+ colors: {
78
+ primary: "#2563eb",
79
+ secondary: "#64748b",
80
+ text: "#1e293b",
81
+ muted: "#64748b",
82
+ background: "#ffffff",
83
+ border: "#e2e8f0",
84
+ accent: "#2563eb"
85
+ },
74
86
  fontSizes: {
75
87
  base: "9.5pt",
76
88
  small: "8pt",
@@ -244,41 +256,59 @@ function iconToSvg(name, opts = {}) {
244
256
  const def = getIcon(name);
245
257
  if (!def) return null;
246
258
  const size = opts.size ?? "1em";
247
- const dim = typeof size === "number" ? `${size}` : size;
259
+ const dim2 = typeof size === "number" ? `${size}` : size;
248
260
  const cls = opts.className ? ` class="${opts.className}"` : "";
249
261
  const fillOrStroke = def.stroked ? 'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"' : 'fill="currentColor"';
250
- return `<svg${cls} width="${dim}" height="${dim}" viewBox="0 0 24 24" ${fillOrStroke} role="img" aria-hidden="true" focusable="false">${def.body}</svg>`;
262
+ return `<svg${cls} width="${dim2}" height="${dim2}" viewBox="0 0 24 24" ${fillOrStroke} role="img" aria-hidden="true" focusable="false">${def.body}</svg>`;
251
263
  }
252
264
 
253
265
  // src/renderers/css.ts
254
266
  var tokenSafe = (s) => s.replace(/[^a-zA-Z0-9_-]/g, "");
267
+ var has = (v) => v != null && v !== "";
268
+ var color = (v, fallback) => has(v) && isSafeColor(v) ? v : fallback;
269
+ var dim = (v, fallback) => has(v) && isSafeDimension(v) ? v : fallback;
270
+ var val = (v, fallback) => has(v) ? sanitizeStyleValue(v) ?? fallback : fallback;
255
271
  function themeToCss(theme, prefix = "retex") {
256
272
  const p = prefix;
257
- const colorVars = Object.entries(theme.colors).map(([k, v]) => ` --${p}-color-${tokenSafe(k)}: ${v};`).join("\n");
273
+ const c = theme.colors ?? {};
274
+ const f = theme.fonts ?? {};
275
+ const fs = theme.fontSizes ?? {};
276
+ const sp = theme.spacing ?? {};
277
+ const h = theme.headings ?? {};
278
+ const colorVars = Object.entries(c).filter(([, v]) => has(v) && isSafeColor(v)).map(([k, v]) => ` --${p}-color-${tokenSafe(k)}: ${v};`).join("\n");
279
+ const primary = color(c.primary, "");
280
+ const skillBg = primary ? `color-mix(in srgb, ${primary} 12%, transparent)` : "transparent";
281
+ const skillColor = primary || "inherit";
282
+ const linkDecoration = primary ? "none" : "underline";
258
283
  return `.${p}-resume {
259
- ${colorVars}
260
- --${p}-primary: ${theme.colors.primary};
261
- --${p}-text: ${theme.colors.text};
262
- --${p}-muted: ${theme.colors.muted};
263
- --${p}-border: ${theme.colors.border};
264
- --${p}-bg: ${theme.colors.background};
265
- --${p}-font-heading: ${theme.fonts.heading};
266
- --${p}-font-body: ${theme.fonts.body};
267
- --${p}-font-mono: ${theme.fonts.mono};
268
- --${p}-fs-base: ${theme.fontSizes.base};
269
- --${p}-fs-small: ${theme.fontSizes.small};
270
- --${p}-fs-large: ${theme.fontSizes.large};
271
- --${p}-fs-Large: ${theme.fontSizes.Large};
272
- --${p}-fs-Huge: ${theme.fontSizes.Huge};
273
- --${p}-fs-name: ${theme.fontSizes.name};
274
- --${p}-fs-section: ${theme.fontSizes.section};
275
- --${p}-sp-section: ${theme.spacing.section};
276
- --${p}-sp-item: ${theme.spacing.item};
284
+ ${colorVars ? colorVars + "\n" : ""} --${p}-primary: ${primary || "inherit"};
285
+ --${p}-text: ${color(c.text, "inherit")};
286
+ --${p}-muted: ${color(c.muted, "inherit")};
287
+ --${p}-border: ${color(c.border, "currentColor")};
288
+ --${p}-bg: ${color(c.background, "transparent")};
289
+ --${p}-font-heading: ${val(f.heading, "inherit")};
290
+ --${p}-font-body: ${val(f.body, "inherit")};
291
+ --${p}-font-mono: ${val(f.mono, "ui-monospace, SFMono-Regular, Menlo, monospace")};
292
+ --${p}-fs-base: ${dim(fs.base, "inherit")};
293
+ --${p}-fs-small: ${dim(fs.small, "0.85em")};
294
+ --${p}-fs-large: ${dim(fs.large, "1.2em")};
295
+ --${p}-fs-Large: ${dim(fs.Large, "1.45em")};
296
+ --${p}-fs-Huge: ${dim(fs.Huge, "2em")};
297
+ --${p}-fs-name: ${dim(fs.name, "2em")};
298
+ --${p}-fs-section: ${dim(fs.section, "1.25em")};
299
+ --${p}-sp-section: ${dim(sp.section, "1.1rem")};
300
+ --${p}-sp-item: ${dim(sp.item, "0.28rem")};
301
+ --${p}-section-transform: ${val(h.transform, "none")};
302
+ --${p}-section-tracking: ${val(h.tracking, "normal")};
303
+ --${p}-name-tracking: ${val(h.nameTracking, "normal")};
304
+ --${p}-link-decoration: ${linkDecoration};
305
+ --${p}-skill-bg: ${skillBg};
306
+ --${p}-skill-color: ${skillColor};
277
307
 
278
308
  box-sizing: border-box;
279
- max-width: ${theme.page.maxWidth};
309
+ max-width: ${dim(theme.page?.maxWidth, "none")};
280
310
  margin: 0 auto;
281
- padding: ${theme.spacing.page};
311
+ padding: ${dim(sp.page, "0")};
282
312
  background: var(--${p}-bg);
283
313
  color: var(--${p}-text);
284
314
  font-family: var(--${p}-font-body);
@@ -294,7 +324,7 @@ ${colorVars}
294
324
  font-weight: 700;
295
325
  margin: 0 0 0.1em;
296
326
  color: var(--${p}-text);
297
- letter-spacing: -0.01em;
327
+ letter-spacing: var(--${p}-name-tracking);
298
328
  }
299
329
  .${p}-title {
300
330
  font-size: var(--${p}-fs-large);
@@ -323,8 +353,8 @@ ${colorVars}
323
353
  font-family: var(--${p}-font-heading);
324
354
  font-size: var(--${p}-fs-section);
325
355
  font-weight: 700;
326
- text-transform: uppercase;
327
- letter-spacing: 0.06em;
356
+ text-transform: var(--${p}-section-transform);
357
+ letter-spacing: var(--${p}-section-tracking);
328
358
  color: var(--${p}-primary);
329
359
  margin: 0 0 0.5em;
330
360
  }
@@ -367,8 +397,8 @@ ${colorVars}
367
397
 
368
398
  .${p}-skills { display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none; margin: 0.2rem 0; padding: 0; }
369
399
  .${p}-skill {
370
- background: color-mix(in srgb, var(--${p}-primary) 12%, transparent);
371
- color: var(--${p}-primary);
400
+ background: var(--${p}-skill-bg);
401
+ color: var(--${p}-skill-color);
372
402
  border-radius: 4px;
373
403
  padding: 0.12rem 0.5rem;
374
404
  font-size: var(--${p}-fs-small);
@@ -376,10 +406,14 @@ ${colorVars}
376
406
  white-space: nowrap;
377
407
  }
378
408
 
379
- .${p}-link { color: var(--${p}-primary); text-decoration: none; }
409
+ .${p}-link { color: var(--${p}-primary); text-decoration: var(--${p}-link-decoration); }
380
410
  .${p}-link:hover { text-decoration: underline; }
381
411
  .${p}-icon { display: inline-flex; vertical-align: -0.125em; }
382
412
  .${p}-rule { border: none; border-top: 1px solid var(--${p}-border); margin: 0.6rem 0; }
413
+ .${p}-rule--dashed { border-top-style: dashed; }
414
+ .${p}-rule--dotted { border-top-style: dotted; }
415
+ .${p}-rule--thick { border-top-width: 3px; }
416
+ .${p}-rule--double { border-top-style: double; border-top-width: 3px; }
383
417
  .${p}-center { text-align: center; }
384
418
  .${p}-scale-small { font-size: var(--${p}-fs-small); }
385
419
  .${p}-scale-large { font-size: var(--${p}-fs-large); }
@@ -437,7 +471,7 @@ function splitPreamble(nodes) {
437
471
  (n) => CONTACTISH.has(n.type) && n !== name && n !== title
438
472
  );
439
473
  const other = nodes.filter(
440
- (n) => !CONTACTISH.has(n.type) && n.type !== "parbreak" && !(n.type === "text" && n.value.trim() === "")
474
+ (n) => !CONTACTISH.has(n.type) && n.type !== "parbreak" && n.type !== "usepackage" && !(n.type === "text" && n.value.trim() === "")
441
475
  );
442
476
  return { name, title, contacts, other };
443
477
  }
@@ -478,7 +512,7 @@ var ReactRenderer = class {
478
512
  "ReactRenderer requires a `createElement` factory (e.g. React.createElement)."
479
513
  );
480
514
  }
481
- this.theme = options.theme ?? resolveTheme();
515
+ this.theme = resolveTheme(options.theme);
482
516
  this.prefix = options.classPrefix ?? "retex";
483
517
  this.h = options.createElement;
484
518
  this.Fragment = options.Fragment ?? "div";
@@ -514,10 +548,11 @@ var ReactRenderer = class {
514
548
  }
515
549
  /* --------------------------- structuring ---------------------------- */
516
550
  renderSection(section, body) {
551
+ const className = this.theme.sectionStyle ? `${this.cls("section")} ${this.cls("section")}--${this.theme.sectionStyle}` : this.cls("section");
517
552
  return this.el(
518
553
  "section",
519
554
  {
520
- className: `${this.cls("section")} ${this.cls("section")}--${this.theme.sectionStyle}`
555
+ className
521
556
  },
522
557
  [
523
558
  this.el("h2", { className: this.cls("section-title") }, [section.title]),
@@ -583,10 +618,14 @@ var ReactRenderer = class {
583
618
  return node.value;
584
619
  case "parbreak":
585
620
  return null;
621
+ case "usepackage":
622
+ return null;
586
623
  case "linebreak":
587
624
  return this.el("br", null, []);
588
- case "rule":
589
- return this.el("hr", { className: this.cls("rule") }, []);
625
+ case "rule": {
626
+ const variant = node.style && node.style !== "solid" ? ` ${this.cls(`rule--${node.style}`)}` : "";
627
+ return this.el("hr", { className: `${this.cls("rule")}${variant}` }, []);
628
+ }
590
629
  case "space":
591
630
  return node.axis === "vertical" ? this.el("div", { style: { height: this.dim(node.size) } }, []) : this.el(
592
631
  "span",