jsonresume-theme-operations-precision 0.3.2 → 0.3.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # jsonresume-theme-operations-precision
2
2
 
3
+ ## 0.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - @jsonresume/core@0.3.2
8
+
9
+ ## 0.3.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 8973d5a: use @jsonresume/core/ssr renderResumeDocument
14
+ - Updated dependencies [ff09f75]
15
+ - @jsonresume/core@0.3.1
16
+
3
17
  ## 0.3.2
4
18
 
5
19
  ### Patch Changes
package/dist/index.css ADDED
@@ -0,0 +1 @@
1
+ :root{--resume-font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;--resume-font-serif: Cambria, Georgia, "Times New Roman", serif;--resume-font-mono: "Courier New", Courier, monospace;--resume-size-name: 36px;--resume-size-heading: 16px;--resume-size-subheading: 14px;--resume-size-body: 11px;--resume-size-small: 10px;--resume-weight-normal: 400;--resume-weight-medium: 500;--resume-weight-semibold: 600;--resume-weight-bold: 700;--resume-line-height-tight: 1.2;--resume-line-height-normal: 1.5;--resume-line-height-relaxed: 1.75;--resume-color-primary: #1a1a1a;--resume-color-secondary: #4a4a4a;--resume-color-accent: #2563eb;--resume-color-background: #ffffff;--resume-color-border: #e5e7eb;--resume-space-section: 24px;--resume-space-item: 16px;--resume-space-tight: 8px;--resume-space-margin: 48px;--resume-max-width: 660px;--resume-column-gap: 24px;--resume-radius-sm: 4px;--resume-radius-md: 8px;--resume-radius-lg: 12px;--resume-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--resume-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1)}[data-theme=modern]{--resume-color-primary: #0f172a;--resume-color-secondary: #475569;--resume-color-accent: #8b5cf6;--resume-font-sans: "Inter", -apple-system, BlinkMacSystemFont, sans-serif}[data-theme=classic]{--resume-color-primary: #000000;--resume-color-secondary: #333333;--resume-color-accent: #0066cc;--resume-font-sans: Georgia, "Times New Roman", serif}[data-theme=minimal]{--resume-color-primary: #18181b;--resume-color-secondary: #71717a;--resume-color-accent: #000000;--resume-space-section: 32px;--resume-space-item: 20px}[data-theme=high-contrast]{--resume-color-primary: #000000;--resume-color-secondary: #000000;--resume-color-accent: #0000ff;--resume-color-background: #ffffff;--resume-color-border: #000000}@media print{:root{--resume-space-section: 18px;--resume-space-item: 12px}@page{size:A4;margin:.5in}.resume-section{page-break-inside:avoid}.resume-item{break-inside:avoid}p,li{widows:3;orphans:3}.resume-description{hyphens:auto}.no-print{display:none!important}}[dir=rtl]{text-align:right}[dir=rtl] .resume-item{padding-left:0;padding-right:var(--resume-space-tight)}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { renderToStaticMarkup } from "react-dom/server";
2
3
  import o, { useRef, useContext, useState, useMemo, useEffect, useDebugValue, createElement, createContext } from "react";
3
- import { renderToString } from "react-dom/server";
4
4
  import { marked } from "marked";
5
5
  var __assign = function() {
6
6
  __assign = Object.assign || function __assign2(t) {
@@ -42,38 +42,46 @@ var isPropValid = /* @__PURE__ */ memoize(
42
42
  function getDefaultExportFromCjs(x2) {
43
43
  return x2 && x2.__esModule && Object.prototype.hasOwnProperty.call(x2, "default") ? x2["default"] : x2;
44
44
  }
45
- var shallowequal = function shallowEqual(objA, objB, compare, compareContext) {
46
- var ret = compare ? compare.call(compareContext, objA, objB) : void 0;
47
- if (ret !== void 0) {
48
- return !!ret;
49
- }
50
- if (objA === objB) {
51
- return true;
52
- }
53
- if (typeof objA !== "object" || !objA || typeof objB !== "object" || !objB) {
54
- return false;
55
- }
56
- var keysA = Object.keys(objA);
57
- var keysB = Object.keys(objB);
58
- if (keysA.length !== keysB.length) {
59
- return false;
60
- }
61
- var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
62
- for (var idx = 0; idx < keysA.length; idx++) {
63
- var key = keysA[idx];
64
- if (!bHasOwnProperty(key)) {
45
+ var shallowequal;
46
+ var hasRequiredShallowequal;
47
+ function requireShallowequal() {
48
+ if (hasRequiredShallowequal) return shallowequal;
49
+ hasRequiredShallowequal = 1;
50
+ shallowequal = function shallowEqual(objA, objB, compare, compareContext) {
51
+ var ret = compare ? compare.call(compareContext, objA, objB) : void 0;
52
+ if (ret !== void 0) {
53
+ return !!ret;
54
+ }
55
+ if (objA === objB) {
56
+ return true;
57
+ }
58
+ if (typeof objA !== "object" || !objA || typeof objB !== "object" || !objB) {
65
59
  return false;
66
60
  }
67
- var valueA = objA[key];
68
- var valueB = objB[key];
69
- ret = compare ? compare.call(compareContext, valueA, valueB, key) : void 0;
70
- if (ret === false || ret === void 0 && valueA !== valueB) {
61
+ var keysA = Object.keys(objA);
62
+ var keysB = Object.keys(objB);
63
+ if (keysA.length !== keysB.length) {
71
64
  return false;
72
65
  }
73
- }
74
- return true;
75
- };
76
- const p = /* @__PURE__ */ getDefaultExportFromCjs(shallowequal);
66
+ var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
67
+ for (var idx = 0; idx < keysA.length; idx++) {
68
+ var key = keysA[idx];
69
+ if (!bHasOwnProperty(key)) {
70
+ return false;
71
+ }
72
+ var valueA = objA[key];
73
+ var valueB = objB[key];
74
+ ret = compare ? compare.call(compareContext, valueA, valueB, key) : void 0;
75
+ if (ret === false || ret === void 0 && valueA !== valueB) {
76
+ return false;
77
+ }
78
+ }
79
+ return true;
80
+ };
81
+ return shallowequal;
82
+ }
83
+ var shallowequalExports = requireShallowequal();
84
+ const p = /* @__PURE__ */ getDefaultExportFromCjs(shallowequalExports);
77
85
  var MS = "-ms-";
78
86
  var MOZ = "-moz-";
79
87
  var WEBKIT = "-webkit-";
@@ -101,8 +109,8 @@ function replace(value, pattern, replacement) {
101
109
  function indexof(value, search, position2) {
102
110
  return value.indexOf(search, position2);
103
111
  }
104
- function charat(value, index) {
105
- return value.charCodeAt(index) | 0;
112
+ function charat(value, index2) {
113
+ return value.charCodeAt(index2) | 0;
106
114
  }
107
115
  function substr(value, begin, end) {
108
116
  return value.slice(begin, end);
@@ -220,11 +228,11 @@ function whitespace(type) {
220
228
  break;
221
229
  return token(type) > 2 || token(character) > 3 ? "" : " ";
222
230
  }
223
- function escaping(index, count) {
231
+ function escaping(index2, count) {
224
232
  while (--count && next())
225
233
  if (character < 48 || character > 102 || character > 57 && character < 65 || character > 70 && character < 97)
226
234
  break;
227
- return slice(index, caret() + (count < 6 && peek() == 32 && next() == 32));
235
+ return slice(index2, caret() + (count < 6 && peek() == 32 && next() == 32));
228
236
  }
229
237
  function delimiter(type) {
230
238
  while (next())
@@ -250,24 +258,24 @@ function delimiter(type) {
250
258
  }
251
259
  return position;
252
260
  }
253
- function commenter(type, index) {
261
+ function commenter(type, index2) {
254
262
  while (next())
255
263
  if (type + character === 47 + 10)
256
264
  break;
257
265
  else if (type + character === 42 + 42 && peek() === 47)
258
266
  break;
259
- return "/*" + slice(index, position - 1) + "*" + from(type === 47 ? type : next());
267
+ return "/*" + slice(index2, position - 1) + "*" + from(type === 47 ? type : next());
260
268
  }
261
- function identifier(index) {
269
+ function identifier(index2) {
262
270
  while (!token(peek()))
263
271
  next();
264
- return slice(index, position);
272
+ return slice(index2, position);
265
273
  }
266
274
  function compile(value) {
267
275
  return dealloc(parse("", null, null, null, [""], value = alloc(value), 0, [0], value));
268
276
  }
269
277
  function parse(value, root, parent, rule, rules, rulesets, pseudo, points, declarations) {
270
- var index = 0;
278
+ var index2 = 0;
271
279
  var offset = 0;
272
280
  var length2 = pseudo;
273
281
  var atrule = 0;
@@ -287,7 +295,7 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
287
295
  // (
288
296
  case 40:
289
297
  if (previous != 108 && charat(characters2, length2 - 1) == 58) {
290
- if (indexof(characters2 += replace(delimit(character2), "&", "&\f"), "&\f", abs(index ? points[index - 1] : 0)) != -1)
298
+ if (indexof(characters2 += replace(delimit(character2), "&", "&\f"), "&\f", abs(index2 ? points[index2 - 1] : 0)) != -1)
291
299
  ampersand = -1;
292
300
  break;
293
301
  }
@@ -321,7 +329,7 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
321
329
  break;
322
330
  // {
323
331
  case 123 * variable:
324
- points[index++] = strlen(characters2) * ampersand;
332
+ points[index2++] = strlen(characters2) * ampersand;
325
333
  // } ; \0
326
334
  case 125 * variable:
327
335
  case 59:
@@ -342,7 +350,7 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
342
350
  characters2 += ";";
343
351
  // { rule/at-rule
344
352
  default:
345
- append(reference = ruleset(characters2, root, parent, index, offset, rules, points, type, props = [], children = [], length2, rulesets), rulesets);
353
+ append(reference = ruleset(characters2, root, parent, index2, offset, rules, points, type, props = [], children = [], length2, rulesets), rulesets);
346
354
  if (character2 === 123)
347
355
  if (offset === 0)
348
356
  parse(characters2, root, reference, reference, props, rulesets, length2, points, children);
@@ -359,7 +367,7 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
359
367
  parse(characters2, reference, reference, reference, [""], children, 0, points, children);
360
368
  }
361
369
  }
362
- index = offset = property = 0, variable = ampersand = 1, type = characters2 = "", length2 = pseudo;
370
+ index2 = offset = property = 0, variable = ampersand = 1, type = characters2 = "", length2 = pseudo;
363
371
  break;
364
372
  // :
365
373
  case 58:
@@ -378,7 +386,7 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
378
386
  break;
379
387
  // ,
380
388
  case 44:
381
- points[index++] = (strlen(characters2) - 1) * ampersand, ampersand = 1;
389
+ points[index2++] = (strlen(characters2) - 1) * ampersand, ampersand = 1;
382
390
  break;
383
391
  // @
384
392
  case 64:
@@ -394,11 +402,11 @@ function parse(value, root, parent, rule, rules, rulesets, pseudo, points, decla
394
402
  }
395
403
  return rulesets;
396
404
  }
397
- function ruleset(value, root, parent, index, offset, rules, points, type, props, children, length2, siblings) {
405
+ function ruleset(value, root, parent, index2, offset, rules, points, type, props, children, length2, siblings) {
398
406
  var post = offset - 1;
399
407
  var rule = offset === 0 ? rules : [""];
400
408
  var size = sizeof(rule);
401
- for (var i = 0, j2 = 0, k2 = 0; i < index; ++i)
409
+ for (var i = 0, j2 = 0, k2 = 0; i < index2; ++i)
402
410
  for (var x2 = 0, y2 = substr(value, post + 1, post = abs(j2 = points[i])), z2 = value; x2 < size; ++x2)
403
411
  if (z2 = trim(j2 > 0 ? rule[x2] + " " + y2 : replace(y2, /&\f/g, rule[x2])))
404
412
  props[k2++] = z2;
@@ -518,8 +526,8 @@ function prefix(value, length2, children) {
518
526
  // grid-(row|column)-start
519
527
  case 4384:
520
528
  case 3616:
521
- if (children && children.some(function(element, index) {
522
- return length2 = index, match(element.props, /grid-\w+-end/);
529
+ if (children && children.some(function(element, index2) {
530
+ return length2 = index2, match(element.props, /grid-\w+-end/);
523
531
  })) {
524
532
  return ~indexof(value + (children = children[length2].value), "span", 0) ? value : MS + replace(value, "-start", "") + value + MS + "grid-row-span:" + (~indexof(children, "span", 0) ? match(children, /\d+/) : +match(children, /\d+/) - +match(value, /\d+/)) + ";";
525
533
  }
@@ -601,7 +609,7 @@ function serialize(children, callback) {
601
609
  output += callback(children[i], i, children, callback) || "";
602
610
  return output;
603
611
  }
604
- function stringify(element, index, children, callback) {
612
+ function stringify(element, index2, children, callback) {
605
613
  switch (element.type) {
606
614
  case LAYER:
607
615
  if (element.children.length) break;
@@ -619,10 +627,10 @@ function stringify(element, index, children, callback) {
619
627
  }
620
628
  function middleware(collection) {
621
629
  var length2 = sizeof(collection);
622
- return function(element, index, children, callback) {
630
+ return function(element, index2, children, callback) {
623
631
  var output = "";
624
632
  for (var i = 0; i < length2; i++)
625
- output += collection[i](element, index, children, callback) || "";
633
+ output += collection[i](element, index2, children, callback) || "";
626
634
  return output;
627
635
  };
628
636
  }
@@ -634,7 +642,7 @@ function rulesheet(callback) {
634
642
  }
635
643
  };
636
644
  }
637
- function prefixer(element, index, children, callback) {
645
+ function prefixer(element, index2, children, callback) {
638
646
  if (element.length > -1) {
639
647
  if (!element.return)
640
648
  switch (element.type) {
@@ -1301,6 +1309,66 @@ var vt = /^\s*<\/[a-z]/i, gt = (function() {
1301
1309
  "production" !== process.env.NODE_ENV && "undefined" != typeof navigator && "ReactNative" === navigator.product && console.warn("It looks like you've imported 'styled-components' on React Native.\nPerhaps you're looking to import 'styled-components/native'?\nRead more about this at https://www.styled-components.com/docs/basics#react-native");
1302
1310
  var wt = "__sc-".concat(f, "__");
1303
1311
  "production" !== process.env.NODE_ENV && "test" !== process.env.NODE_ENV && "undefined" != typeof window && (window[wt] || (window[wt] = 0), 1 === window[wt] && console.warn("It looks like there are several instances of 'styled-components' initialized in this application. This may cause dynamic styles to not render properly, errors during the rehydration process, a missing theme prop, and makes your application bigger without good reason.\n\nSee https://s-c.sh/2BAXzed for more info."), window[wt] += 1);
1312
+ const CSS_RESET = "<style>*,*::before,*::after{box-sizing:border-box}html,body{margin:0;padding:0}body{-webkit-font-smoothing:antialiased}</style>";
1313
+ const TOKENS_CSS_HREF = "https://unpkg.com/@jsonresume/core/dist/tokens.css";
1314
+ const FONTS_PRECONNECT = '<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
1315
+ function isHref(value) {
1316
+ return /^(https?:)?\/\//.test(value) || value.trim().startsWith("<link");
1317
+ }
1318
+ function familyParam(family) {
1319
+ const [name, ...rest] = String(family).split(":");
1320
+ const encodedName = name.trim().replace(/\s+/g, "+");
1321
+ return rest.length ? `${encodedName}:${rest.join(":")}` : encodedName;
1322
+ }
1323
+ function googleFontsLinks(families) {
1324
+ if (!Array.isArray(families) || families.length === 0) return "";
1325
+ const passthrough = [];
1326
+ const names = [];
1327
+ for (const entry of families) {
1328
+ if (entry == null || entry === "") continue;
1329
+ if (isHref(entry)) passthrough.push(entry);
1330
+ else names.push(entry);
1331
+ }
1332
+ const links = passthrough.map(
1333
+ (href) => href.trim().startsWith("<link") ? href : `<link href="${href}" rel="stylesheet">`
1334
+ );
1335
+ if (names.length > 0) {
1336
+ const query = names.map(familyParam).join("&family=");
1337
+ links.unshift(
1338
+ `<link href="https://fonts.googleapis.com/css2?family=${query}&display=swap" rel="stylesheet">`
1339
+ );
1340
+ }
1341
+ if (links.length === 0) return "";
1342
+ return FONTS_PRECONNECT + links.join("");
1343
+ }
1344
+ function renderResumeDocument(element, options = {}) {
1345
+ const {
1346
+ fonts,
1347
+ title,
1348
+ lang = "en",
1349
+ dir = "ltr",
1350
+ reset = false,
1351
+ head = "",
1352
+ headAfterStyles = "",
1353
+ includeTokensCss = true,
1354
+ bodyClass
1355
+ } = options;
1356
+ const sheet = new gt();
1357
+ let html;
1358
+ let styleTags;
1359
+ try {
1360
+ html = renderToStaticMarkup(sheet.collectStyles(element));
1361
+ styleTags = sheet.getStyleTags();
1362
+ } finally {
1363
+ sheet.seal();
1364
+ }
1365
+ const fontLinks = googleFontsLinks(fonts);
1366
+ const tokensLink = includeTokensCss ? `<link rel="stylesheet" href="${TOKENS_CSS_HREF}">` : "";
1367
+ const resetTag = reset ? CSS_RESET : "";
1368
+ const titleTag = title ? `<title>${title}</title>` : "";
1369
+ const bodyAttr = bodyClass ? ` class="${bodyClass}"` : "";
1370
+ return `<!DOCTYPE html><html lang="${lang}" dir="${dir}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` + fontLinks + tokensLink + resetTag + head + styleTags + headAfterStyles + titleTag + `</head><body${bodyAttr}>${html}</body></html>`;
1371
+ }
1304
1372
  createContext({
1305
1373
  theme: "professional",
1306
1374
  setTheme: () => {
@@ -1460,6 +1528,7 @@ function formatDateRange({
1460
1528
  };
1461
1529
  const formatDate = (dateStr) => {
1462
1530
  if (!dateStr) return getPresentLabel();
1531
+ const isDateOnlyIso = typeof dateStr === "string" && /^\d{4}(-\d{2}){0,2}$/.test(dateStr);
1463
1532
  const date = typeof dateStr === "string" ? new Date(dateStr) : dateStr;
1464
1533
  if (isNaN(date.getTime())) return dateStr;
1465
1534
  const monthFormats = {
@@ -1471,6 +1540,9 @@ function formatDateRange({
1471
1540
  ...monthFormats[format],
1472
1541
  year: "numeric"
1473
1542
  };
1543
+ if (isDateOnlyIso) {
1544
+ options.timeZone = "UTC";
1545
+ }
1474
1546
  if (numberingSystem) {
1475
1547
  options.numberingSystem = numberingSystem;
1476
1548
  }
@@ -1478,7 +1550,9 @@ function formatDateRange({
1478
1550
  return formatter.format(date);
1479
1551
  };
1480
1552
  const start = formatDate(startDate);
1481
- if (endDate === void 0) return start;
1553
+ if (endDate === void 0) {
1554
+ return start;
1555
+ }
1482
1556
  const end = formatDate(endDate);
1483
1557
  return `${start} - ${end}`;
1484
1558
  }
@@ -1563,7 +1637,6 @@ function safeUrl(url) {
1563
1637
  const trimmed = url.trim();
1564
1638
  const dangerousProtocols = /^(javascript|data|vbscript|file|about):/i;
1565
1639
  if (dangerousProtocols.test(trimmed)) {
1566
- console.warn(`[Security] Blocked dangerous URL: ${trimmed.slice(0, 50)}`);
1567
1640
  return null;
1568
1641
  }
1569
1642
  const safeProtocols = /^(https?|mailto|tel|sms|ftp):/i;
@@ -1579,7 +1652,6 @@ function safeUrl(url) {
1579
1652
  if (/^[a-z0-9][a-z0-9.-]+\.[a-z]{2,}$/i.test(trimmed)) {
1580
1653
  return `https://${trimmed}`;
1581
1654
  }
1582
- console.warn(`[Security] Uncertain URL safety: ${trimmed.slice(0, 50)}`);
1583
1655
  return trimmed;
1584
1656
  }
1585
1657
  const ContactContainer = dt.div`
@@ -1643,7 +1715,7 @@ function ContactInfo({ basics = {}, separator = "•", className }) {
1643
1715
  ) }, "url")
1644
1716
  );
1645
1717
  }
1646
- profiles.forEach((profile, index) => {
1718
+ profiles.forEach((profile, index2) => {
1647
1719
  if (profile.url) {
1648
1720
  items.push(
1649
1721
  /* @__PURE__ */ jsx(ContactItem, { children: /* @__PURE__ */ jsx(
@@ -1655,15 +1727,15 @@ function ContactInfo({ basics = {}, separator = "•", className }) {
1655
1727
  "aria-label": profile.network,
1656
1728
  children: profile.network || profile.username
1657
1729
  }
1658
- ) }, `profile-${index}`)
1730
+ ) }, `profile-${index2}`)
1659
1731
  );
1660
1732
  }
1661
1733
  });
1662
1734
  if (items.length === 0) return null;
1663
- return /* @__PURE__ */ jsx(ContactContainer, { className: `resume-contact ${className || ""}`.trim(), children: items.map((item, index) => /* @__PURE__ */ jsxs(o.Fragment, { children: [
1664
- index > 0 && /* @__PURE__ */ jsx(Separator, { "aria-hidden": "true", children: separator }),
1735
+ return /* @__PURE__ */ jsx(ContactContainer, { className: `resume-contact ${className || ""}`.trim(), children: items.map((item, index2) => /* @__PURE__ */ jsxs(o.Fragment, { children: [
1736
+ index2 > 0 && /* @__PURE__ */ jsx(Separator, { "aria-hidden": "true", children: separator }),
1665
1737
  item
1666
- ] }, index)) });
1738
+ ] }, index2)) });
1667
1739
  }
1668
1740
  dt.a`
1669
1741
  color: ${(props) => props.theme?.colors?.link || "var(--resume-color-link, #0066cc)"};
@@ -6264,7 +6336,7 @@ function Resume({ resume }) {
6264
6336
  ] }),
6265
6337
  work?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6266
6338
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Experience" }),
6267
- work.map((job, index) => /* @__PURE__ */ jsxs(WorkItem, { children: [
6339
+ work.map((job, index2) => /* @__PURE__ */ jsxs(WorkItem, { children: [
6268
6340
  /* @__PURE__ */ jsxs(WorkHeader, { children: [
6269
6341
  /* @__PURE__ */ jsxs("div", { children: [
6270
6342
  /* @__PURE__ */ jsx(Position, { children: job.position }),
@@ -6291,18 +6363,18 @@ function Resume({ resume }) {
6291
6363
  },
6292
6364
  i
6293
6365
  )) })
6294
- ] }, index))
6366
+ ] }, index2))
6295
6367
  ] }),
6296
6368
  skills?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6297
6369
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Skills & Tools" }),
6298
- /* @__PURE__ */ jsx(SkillsTable, { children: skills.map((skill, index) => /* @__PURE__ */ jsxs(o.Fragment, { children: [
6370
+ /* @__PURE__ */ jsx(SkillsTable, { children: skills.map((skill, index2) => /* @__PURE__ */ jsxs(o.Fragment, { children: [
6299
6371
  /* @__PURE__ */ jsx(SkillName, { children: skill.name }),
6300
6372
  /* @__PURE__ */ jsx(SkillTags, { children: skill.keywords?.length > 0 ? skill.keywords.join(" • ") : "—" })
6301
- ] }, index)) })
6373
+ ] }, index2)) })
6302
6374
  ] }),
6303
6375
  education?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6304
6376
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Education" }),
6305
- education.map((edu, index) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6377
+ education.map((edu, index2) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6306
6378
  /* @__PURE__ */ jsx(Institution, { children: edu.institution }),
6307
6379
  /* @__PURE__ */ jsxs(Degree, { children: [
6308
6380
  edu.studyType,
@@ -6311,11 +6383,11 @@ function Resume({ resume }) {
6311
6383
  edu.score && ` • ${edu.score}`
6312
6384
  ] }),
6313
6385
  /* @__PURE__ */ jsx(EducationDate, { children: /* @__PURE__ */ jsx(DateRange, { startDate: edu.startDate, endDate: edu.endDate }) })
6314
- ] }, index))
6386
+ ] }, index2))
6315
6387
  ] }),
6316
6388
  projects?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6317
6389
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Projects" }),
6318
- projects.map((project, index) => /* @__PURE__ */ jsxs(ProjectItem, { children: [
6390
+ projects.map((project, index2) => /* @__PURE__ */ jsxs(ProjectItem, { children: [
6319
6391
  /* @__PURE__ */ jsxs(ProjectHeader, { children: [
6320
6392
  /* @__PURE__ */ jsx(ProjectName, { children: project.name }),
6321
6393
  (project.startDate || project.endDate) && /* @__PURE__ */ jsx(DateText, { children: /* @__PURE__ */ jsx(
@@ -6345,11 +6417,11 @@ function Resume({ resume }) {
6345
6417
  },
6346
6418
  i
6347
6419
  )) })
6348
- ] }, index))
6420
+ ] }, index2))
6349
6421
  ] }),
6350
6422
  volunteer?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6351
6423
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Volunteer" }),
6352
- volunteer.map((vol, index) => /* @__PURE__ */ jsxs(WorkItem, { children: [
6424
+ volunteer.map((vol, index2) => /* @__PURE__ */ jsxs(WorkItem, { children: [
6353
6425
  /* @__PURE__ */ jsxs(WorkHeader, { children: [
6354
6426
  /* @__PURE__ */ jsxs("div", { children: [
6355
6427
  /* @__PURE__ */ jsx(Position, { children: vol.position }),
@@ -6382,11 +6454,11 @@ function Resume({ resume }) {
6382
6454
  },
6383
6455
  i
6384
6456
  )) })
6385
- ] }, index))
6457
+ ] }, index2))
6386
6458
  ] }),
6387
6459
  awards?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6388
6460
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Awards" }),
6389
- awards.map((award, index) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6461
+ awards.map((award, index2) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6390
6462
  /* @__PURE__ */ jsx(Institution, { children: award.title }),
6391
6463
  award.awarder && /* @__PURE__ */ jsxs(Degree, { children: [
6392
6464
  "Awarded by ",
@@ -6401,11 +6473,11 @@ function Resume({ resume }) {
6401
6473
  }
6402
6474
  }
6403
6475
  )
6404
- ] }, index))
6476
+ ] }, index2))
6405
6477
  ] }),
6406
6478
  publications?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6407
6479
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Publications" }),
6408
- publications.map((pub, index) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6480
+ publications.map((pub, index2) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6409
6481
  /* @__PURE__ */ jsx(Institution, { children: pub.name }),
6410
6482
  pub.publisher && /* @__PURE__ */ jsxs(Degree, { children: [
6411
6483
  "Published by ",
@@ -6420,67 +6492,57 @@ function Resume({ resume }) {
6420
6492
  }
6421
6493
  }
6422
6494
  )
6423
- ] }, index))
6495
+ ] }, index2))
6424
6496
  ] }),
6425
6497
  certificates?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6426
6498
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Certifications" }),
6427
- certificates.map((cert, index) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6499
+ certificates.map((cert, index2) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6428
6500
  /* @__PURE__ */ jsx(Institution, { children: cert.name }),
6429
6501
  cert.issuer && /* @__PURE__ */ jsxs(Degree, { children: [
6430
6502
  "Issued by ",
6431
6503
  cert.issuer
6432
6504
  ] }),
6433
6505
  cert.date && /* @__PURE__ */ jsx(EducationDate, { children: cert.date })
6434
- ] }, index))
6506
+ ] }, index2))
6435
6507
  ] }),
6436
6508
  languages?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6437
6509
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Languages" }),
6438
- /* @__PURE__ */ jsx(TwoColumnGrid, { children: languages.map((lang, index) => /* @__PURE__ */ jsxs(CompactItem, { children: [
6510
+ /* @__PURE__ */ jsx(TwoColumnGrid, { children: languages.map((lang, index2) => /* @__PURE__ */ jsxs(CompactItem, { children: [
6439
6511
  /* @__PURE__ */ jsxs(CompactTitle, { children: [
6440
6512
  lang.language,
6441
6513
  ":"
6442
6514
  ] }),
6443
6515
  " ",
6444
6516
  lang.fluency || "—"
6445
- ] }, index)) })
6517
+ ] }, index2)) })
6446
6518
  ] }),
6447
6519
  interests?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6448
6520
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "Interests" }),
6449
- /* @__PURE__ */ jsx(TwoColumnGrid, { children: interests.map((interest, index) => /* @__PURE__ */ jsxs(CompactItem, { children: [
6521
+ /* @__PURE__ */ jsx(TwoColumnGrid, { children: interests.map((interest, index2) => /* @__PURE__ */ jsxs(CompactItem, { children: [
6450
6522
  /* @__PURE__ */ jsx(CompactTitle, { children: interest.name }),
6451
6523
  interest.keywords?.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
6452
6524
  ": ",
6453
6525
  interest.keywords.join(", ")
6454
6526
  ] })
6455
- ] }, index)) })
6527
+ ] }, index2)) })
6456
6528
  ] }),
6457
6529
  references?.length > 0 && /* @__PURE__ */ jsxs(Section, { children: [
6458
6530
  /* @__PURE__ */ jsx(StyledSectionTitle, { children: "References" }),
6459
- references.map((ref, index) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6531
+ references.map((ref, index2) => /* @__PURE__ */ jsxs(EducationItem, { children: [
6460
6532
  /* @__PURE__ */ jsx(Institution, { children: ref.name }),
6461
6533
  ref.reference && /* @__PURE__ */ jsx(WorkSummary, { children: ref.reference })
6462
- ] }, index))
6534
+ ] }, index2))
6463
6535
  ] })
6464
6536
  ] });
6465
6537
  }
6466
6538
  function render(resume) {
6467
- const sheet = new gt();
6468
- try {
6469
- const html = renderToString(
6470
- sheet.collectStyles(/* @__PURE__ */ jsx(Resume, { resume }))
6471
- );
6472
- const styles = sheet.getStyleTags();
6473
- return `<!DOCTYPE html>
6474
- <html lang="en">
6475
- <head>
6476
- <meta charset="UTF-8">
6477
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6478
- <title>${resume.basics?.name || "Resume"} - Resume</title>
6479
- <link rel="preconnect" href="https://fonts.googleapis.com">
6480
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
6481
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
6482
- ${styles}
6483
- <style>
6539
+ return renderResumeDocument(/* @__PURE__ */ jsx(Resume, { resume }), {
6540
+ fonts: ["Inter:wght@400;500;600;700"],
6541
+ title: `${resume.basics?.name || "Resume"} - Resume`,
6542
+ lang: "en",
6543
+ dir: "ltr",
6544
+ includeTokensCss: false,
6545
+ headAfterStyles: `<style>
6484
6546
  * {
6485
6547
  box-sizing: border-box;
6486
6548
  margin: 0;
@@ -6499,16 +6561,11 @@ function render(resume) {
6499
6561
  margin: 0.5in;
6500
6562
  }
6501
6563
  }
6502
- </style>
6503
- </head>
6504
- <body>
6505
- ${html}
6506
- </body>
6507
- </html>`;
6508
- } finally {
6509
- sheet.seal();
6510
- }
6564
+ </style>`
6565
+ });
6511
6566
  }
6567
+ const index = { render };
6512
6568
  export {
6569
+ index as default,
6513
6570
  render
6514
6571
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsonresume-theme-operations-precision",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Operations professionals - show your systematic approach, process improvements",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "marked": "^16.3.0",
22
22
  "styled-components": "^6.1.19",
23
- "@jsonresume/core": "0.3.0"
23
+ "@jsonresume/core": "0.3.2"
24
24
  },
25
25
  "exports": {
26
26
  ".": {
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "devDependencies": {
36
36
  "@vitejs/plugin-react": "^4.3.4",
37
- "vite": "^5.4.21"
37
+ "vite": "^6.4.3"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "vite build"
package/src/index.jsx CHANGED
@@ -1,28 +1,14 @@
1
- import React from 'react';
2
- import { renderToString } from 'react-dom/server';
3
- import { ServerStyleSheet } from 'styled-components';
1
+ import { renderResumeDocument } from '@jsonresume/core/ssr';
4
2
  import Resume from './Resume.jsx';
5
3
 
6
4
  export function render(resume) {
7
- const sheet = new ServerStyleSheet();
8
-
9
- try {
10
- const html = renderToString(
11
- sheet.collectStyles(<Resume resume={resume} />)
12
- );
13
- const styles = sheet.getStyleTags();
14
-
15
- return `<!DOCTYPE html>
16
- <html lang="en">
17
- <head>
18
- <meta charset="UTF-8">
19
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
20
- <title>${resume.basics?.name || 'Resume'} - Resume</title>
21
- <link rel="preconnect" href="https://fonts.googleapis.com">
22
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
23
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
24
- ${styles}
25
- <style>
5
+ return renderResumeDocument(<Resume resume={resume} />, {
6
+ fonts: ['Inter:wght@400;500;600;700'],
7
+ title: `${resume.basics?.name || 'Resume'} - Resume`,
8
+ lang: 'en',
9
+ dir: 'ltr',
10
+ includeTokensCss: false,
11
+ headAfterStyles: `<style>
26
12
  * {
27
13
  box-sizing: border-box;
28
14
  margin: 0;
@@ -41,13 +27,8 @@ export function render(resume) {
41
27
  margin: 0.5in;
42
28
  }
43
29
  }
44
- </style>
45
- </head>
46
- <body>
47
- ${html}
48
- </body>
49
- </html>`;
50
- } finally {
51
- sheet.seal();
52
- }
30
+ </style>`,
31
+ });
53
32
  }
33
+
34
+ export default { render };
package/dist/style.css DELETED
@@ -1,139 +0,0 @@
1
- /**
2
- * Resume Core Design Tokens
3
- * Framework-agnostic CSS variables for theming
4
- * Based on 2025 resume best practices and ATS compatibility research
5
- */
6
-
7
- :root {
8
- /* Typography - ATS-friendly fonts */
9
- --resume-font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
10
- --resume-font-serif: Cambria, Georgia, "Times New Roman", serif;
11
- --resume-font-mono: "Courier New", Courier, monospace;
12
-
13
- /* Font Sizes - Optimal readability */
14
- --resume-size-name: 36px; /* Large, prominent name */
15
- --resume-size-heading: 16px; /* Section titles */
16
- --resume-size-subheading: 14px; /* Job titles, degrees */
17
- --resume-size-body: 11px; /* Body text, descriptions */
18
- --resume-size-small: 10px; /* Dates, locations, metadata */
19
-
20
- /* Font Weights */
21
- --resume-weight-normal: 400;
22
- --resume-weight-medium: 500;
23
- --resume-weight-semibold: 600;
24
- --resume-weight-bold: 700;
25
-
26
- /* Line Heights */
27
- --resume-line-height-tight: 1.2;
28
- --resume-line-height-normal: 1.5;
29
- --resume-line-height-relaxed: 1.75;
30
-
31
- /* Colors - Professional Theme (default) */
32
- --resume-color-primary: #1a1a1a;
33
- --resume-color-secondary: #4a4a4a;
34
- --resume-color-accent: #2563eb;
35
- --resume-color-background: #ffffff;
36
- --resume-color-border: #e5e7eb;
37
-
38
- /* Spacing - Consistent rhythm */
39
- --resume-space-section: 24px; /* Between major sections */
40
- --resume-space-item: 16px; /* Between list items */
41
- --resume-space-tight: 8px; /* Within items */
42
- --resume-space-margin: 48px; /* Page margins */
43
-
44
- /* Layout - Optimal line length */
45
- --resume-max-width: 660px; /* ~80 chars at body size */
46
- --resume-column-gap: 24px;
47
-
48
- /* Border Radius */
49
- --resume-radius-sm: 4px;
50
- --resume-radius-md: 8px;
51
- --resume-radius-lg: 12px;
52
-
53
- /* Shadows - Subtle depth */
54
- --resume-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
55
- --resume-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
56
- }
57
-
58
- /* Modern Theme Variant */
59
- [data-theme="modern"] {
60
- --resume-color-primary: #0f172a;
61
- --resume-color-secondary: #475569;
62
- --resume-color-accent: #8b5cf6;
63
- --resume-font-sans: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
64
- }
65
-
66
- /* Classic Theme Variant */
67
- [data-theme="classic"] {
68
- --resume-color-primary: #000000;
69
- --resume-color-secondary: #333333;
70
- --resume-color-accent: #0066cc;
71
- --resume-font-sans: Georgia, "Times New Roman", serif;
72
- }
73
-
74
- /* Minimal Theme Variant */
75
- [data-theme="minimal"] {
76
- --resume-color-primary: #18181b;
77
- --resume-color-secondary: #71717a;
78
- --resume-color-accent: #000000;
79
- --resume-space-section: 32px;
80
- --resume-space-item: 20px;
81
- }
82
-
83
- /* High Contrast Variant (Accessibility) */
84
- [data-theme="high-contrast"] {
85
- --resume-color-primary: #000000;
86
- --resume-color-secondary: #000000;
87
- --resume-color-accent: #0000ff;
88
- --resume-color-background: #ffffff;
89
- --resume-color-border: #000000;
90
- }
91
-
92
- /* Print Styles - ATS-Friendly PDF Export */
93
- @media print {
94
- :root {
95
- /* Optimize for print */
96
- --resume-space-section: 18px;
97
- --resume-space-item: 12px;
98
- }
99
-
100
- @page {
101
- size: A4;
102
- margin: 0.5in;
103
- }
104
-
105
- /* Prevent awkward breaks */
106
- .resume-section {
107
- page-break-inside: avoid;
108
- }
109
-
110
- .resume-item {
111
- break-inside: avoid;
112
- }
113
-
114
- /* Widows and orphans control */
115
- p, li {
116
- widows: 3;
117
- orphans: 3;
118
- }
119
-
120
- /* Hyphenation for long words */
121
- .resume-description {
122
- hyphens: auto;
123
- }
124
-
125
- /* Hide interactive elements */
126
- .no-print {
127
- display: none !important;
128
- }
129
- }
130
-
131
- /* RTL Support */
132
- [dir="rtl"] {
133
- text-align: right;
134
- }
135
-
136
- [dir="rtl"] .resume-item {
137
- padding-left: 0;
138
- padding-right: var(--resume-space-tight);
139
- }