@dytsou/resume-converter 2.0.3 → 2.1.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/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/.husky/pre-commit +3 -0
- package/.prettierignore +18 -0
- package/.prettierrc +10 -0
- package/README.md +7 -1
- package/eslint.config.js +3 -1
- package/latex/resume.tex +3 -3
- package/package.json +18 -11
- package/scripts/config.mjs +22 -0
- package/scripts/convert-latex.mjs +32 -713
- package/scripts/converter.mjs +76 -0
- package/scripts/html-helpers.mjs +64 -0
- package/scripts/html-template.mjs +166 -0
- package/scripts/html-transformers.mjs +366 -0
- package/scripts/latex-parser.mjs +74 -0
- package/scripts/utils.mjs +29 -0
- package/src/App.tsx +3 -1
- package/src/components/DownloadFromDriveButton.tsx +6 -4
- package/src/index.css +9 -3
- package/src/utils/googleDriveUtils.ts +1 -1
- package/src/utils/resumeUtils.ts +1 -1
- package/vite.config.ts +1 -2
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LaTeX macro parsing functions
|
|
3
|
+
* Handles parsing of custom resume macros with proper brace counting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses LaTeX macro arguments with proper brace counting
|
|
8
|
+
* Handles nested braces correctly
|
|
9
|
+
*/
|
|
10
|
+
export function parseLatexMacro(content, macroName, argCount) {
|
|
11
|
+
const matches = [];
|
|
12
|
+
const regex = new RegExp(`\\\\${macroName}\\{`, 'g');
|
|
13
|
+
let match;
|
|
14
|
+
|
|
15
|
+
while ((match = regex.exec(content)) !== null) {
|
|
16
|
+
const startPos = match.index;
|
|
17
|
+
let pos = match.index + match[0].length - 1;
|
|
18
|
+
const args = [];
|
|
19
|
+
let currentArg = '';
|
|
20
|
+
let braceCount = 1;
|
|
21
|
+
let parsedArgs = 0;
|
|
22
|
+
|
|
23
|
+
while (parsedArgs < argCount && pos < content.length) {
|
|
24
|
+
pos++;
|
|
25
|
+
const char = content[pos];
|
|
26
|
+
|
|
27
|
+
if (char === '{') {
|
|
28
|
+
braceCount++;
|
|
29
|
+
currentArg += char;
|
|
30
|
+
} else if (char === '}') {
|
|
31
|
+
braceCount--;
|
|
32
|
+
if (braceCount === 0) {
|
|
33
|
+
args.push(currentArg.trim());
|
|
34
|
+
parsedArgs++;
|
|
35
|
+
currentArg = '';
|
|
36
|
+
// Look for next argument
|
|
37
|
+
while (pos < content.length && content[pos] !== '{') {
|
|
38
|
+
pos++;
|
|
39
|
+
}
|
|
40
|
+
if (pos < content.length) {
|
|
41
|
+
braceCount = 1;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
currentArg += char;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
currentArg += char;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (args.length >= argCount) {
|
|
52
|
+
const fullMatch = content.substring(startPos, pos + 1);
|
|
53
|
+
matches.push([fullMatch, ...args]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return matches;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extracts all resume macro matches from LaTeX content
|
|
62
|
+
*/
|
|
63
|
+
export function extractMacroMatches(latexContent) {
|
|
64
|
+
return {
|
|
65
|
+
trio: parseLatexMacro(latexContent, 'resumeTrioHeading', 3),
|
|
66
|
+
quadDetails: parseLatexMacro(latexContent, 'resumeQuadHeadingDetails', 3),
|
|
67
|
+
quadHeading: parseLatexMacro(latexContent, 'resumeQuadHeading', 4),
|
|
68
|
+
sectionType: Array.from(
|
|
69
|
+
latexContent.matchAll(
|
|
70
|
+
/\\resumeSectionType\{([^}]*)\}\{([^}]*)\}\{([^}]*)\}/g
|
|
71
|
+
)
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for file operations and metadata extraction
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Ensures a directory exists, creating it recursively if needed
|
|
9
|
+
*/
|
|
10
|
+
export function ensureDirectoryExists(dir) {
|
|
11
|
+
if (!existsSync(dir)) {
|
|
12
|
+
mkdirSync(dir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extracts metadata from LaTeX document
|
|
18
|
+
*/
|
|
19
|
+
export function extractMetadata(latexContent) {
|
|
20
|
+
const titleMatch = latexContent.match(/\\title\{([^}]+)\}/);
|
|
21
|
+
const authorMatch = latexContent.match(/\\author\{([^}]+)\}/);
|
|
22
|
+
const dateMatch = latexContent.match(/\\date\{([^}]+)\}/);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
title: titleMatch ? titleMatch[1] : 'Untitled Document',
|
|
26
|
+
author: authorMatch ? authorMatch[1] : 'Unknown Author',
|
|
27
|
+
date: dateMatch ? dateMatch[1] : new Date().toISOString().split('T')[0],
|
|
28
|
+
};
|
|
29
|
+
}
|
package/src/App.tsx
CHANGED
|
@@ -18,7 +18,9 @@ function App() {
|
|
|
18
18
|
console.log('Iframe loaded successfully');
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const handleIframeError = (
|
|
21
|
+
const handleIframeError = (
|
|
22
|
+
e: React.SyntheticEvent<HTMLIFrameElement, Event>
|
|
23
|
+
) => {
|
|
22
24
|
console.error('Iframe error:', e);
|
|
23
25
|
};
|
|
24
26
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
convertToDirectDownloadLink,
|
|
4
|
+
downloadFile,
|
|
5
|
+
} from '../utils/googleDriveUtils';
|
|
3
6
|
|
|
4
7
|
interface DownloadFromDriveButtonProps {
|
|
5
8
|
/** Google Drive share link */
|
|
@@ -42,7 +45,8 @@ export function DownloadFromDriveButton({
|
|
|
42
45
|
setIsLoading(false);
|
|
43
46
|
}, 500);
|
|
44
47
|
} catch (err) {
|
|
45
|
-
const errorMessage =
|
|
48
|
+
const errorMessage =
|
|
49
|
+
err instanceof Error ? err.message : 'Failed to download file';
|
|
46
50
|
setError(errorMessage);
|
|
47
51
|
setIsLoading(false);
|
|
48
52
|
}
|
|
@@ -100,5 +104,3 @@ export function DownloadFromDriveButton({
|
|
|
100
104
|
</div>
|
|
101
105
|
);
|
|
102
106
|
}
|
|
103
|
-
|
|
104
|
-
|
package/src/index.css
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
body {
|
|
9
|
-
font-family:
|
|
9
|
+
font-family:
|
|
10
|
+
system-ui,
|
|
11
|
+
-apple-system,
|
|
12
|
+
sans-serif;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
/* Download button wrapper - positioned bottom-right */
|
|
@@ -41,7 +44,10 @@ body {
|
|
|
41
44
|
cursor: pointer;
|
|
42
45
|
transition: all 0.3s ease;
|
|
43
46
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
44
|
-
font-family:
|
|
47
|
+
font-family:
|
|
48
|
+
system-ui,
|
|
49
|
+
-apple-system,
|
|
50
|
+
sans-serif;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
.download-drive-button:hover:not(:disabled) {
|
|
@@ -119,4 +125,4 @@ body {
|
|
|
119
125
|
width: 20px;
|
|
120
126
|
height: 20px;
|
|
121
127
|
}
|
|
122
|
-
}
|
|
128
|
+
}
|
package/src/utils/resumeUtils.ts
CHANGED