refine resume typography and spacing
This commit is contained in:
189
scripts/patch-reactive-resume-glalie-layout.sh
Executable file
189
scripts/patch-reactive-resume-glalie-layout.sh
Executable file
@@ -0,0 +1,189 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONTAINER="${1:-reactive-resume-reactive-resume-1}"
|
||||||
|
|
||||||
|
docker exec -u root -i "$CONTAINER" sh <<'SH'
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
SSR_FILE="/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs"
|
||||||
|
PUBLIC_FILE="/app/apps/web/.output/public/assets/pdf-document-BplbXx-0.js"
|
||||||
|
SERVER_INDEX_FILE="/app/apps/web/.output/server/index.mjs"
|
||||||
|
CACHE_BUST="rr-glalie-layout-20260518"
|
||||||
|
|
||||||
|
test -f "$SSR_FILE.bak-glalie-layout" || cp "$SSR_FILE" "$SSR_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
test -f "$PUBLIC_FILE.bak-glalie-layout" || cp "$PUBLIC_FILE" "$PUBLIC_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
test -f "$SERVER_INDEX_FILE.bak-glalie-layout" || cp "$SERVER_INDEX_FILE" "$SERVER_INDEX_FILE.bak-glalie-layout" 2>/dev/null || true
|
||||||
|
|
||||||
|
node - <<'NODE'
|
||||||
|
const fs = require("fs");
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
const ssrFile = "/app/apps/web/.output/server/_ssr/pdf-document-COfeOLVC.mjs";
|
||||||
|
const publicFile = "/app/apps/web/.output/public/assets/pdf-document-BplbXx-0.js";
|
||||||
|
const serverIndexFile = "/app/apps/web/.output/server/index.mjs";
|
||||||
|
const cacheBust = "rr-glalie-layout-20260518";
|
||||||
|
|
||||||
|
function replaceOnce(source, from, to, label) {
|
||||||
|
if (source.includes(to)) return source;
|
||||||
|
if (!source.includes(from)) throw new Error(`Patch marker not found: ${label}`);
|
||||||
|
return source.replace(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceRegexOnce(source, regex, to, label) {
|
||||||
|
if (source.includes(to)) return source;
|
||||||
|
const next = source.replace(regex, to);
|
||||||
|
if (next === source) throw new Error(`Patch marker not found: ${label}`);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchSsr(source) {
|
||||||
|
source = source.replace(/metrics\.gapY\(3\.5\)/g, "metrics.gapY(2.6)");
|
||||||
|
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"style: composeStyles(styles.sidebarContent, { rowGap: metrics.sectionGap }),",
|
||||||
|
"style: composeStyles(styles.sidebarContent, { rowGap: metrics.gapY(2.6) }),",
|
||||||
|
"SSR Glalie sidebar section gap",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"style: composeStyles(styles.mainContent, { rowGap: metrics.sectionGap }),",
|
||||||
|
"style: composeStyles(styles.mainContent, { rowGap: metrics.gapY(2.6) }),",
|
||||||
|
"SSR Glalie main section gap",
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/sectionHeading: \{\s*borderBottomWidth: 1,\s*borderBottomColor: primary\s*\},/,
|
||||||
|
`sectionHeading: {
|
||||||
|
\t\t\t\t\tborderBottomWidth: 1,
|
||||||
|
\t\t\t\t\tborderBottomColor: primary,
|
||||||
|
\t\t\t\t\tpaddingBottom: 1
|
||||||
|
\t\t\t\t},`,
|
||||||
|
"SSR Glalie heading underline padding",
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/sidebarColumn: \{\s*zIndex: 1,\s*backgroundColor: primaryTint,\s*paddingHorizontal: metrics\.page\.paddingHorizontal,\s*paddingTop: metrics\.page\.paddingVertical,\s*(?:paddingBottom: metrics\.page\.paddingVertical,\s*)?rowGap: (?:metrics\.sectionGap|metrics\.gapY\([^)]+\))\s*\},/,
|
||||||
|
`sidebarColumn: {
|
||||||
|
\t\t\t\t\tzIndex: 1,
|
||||||
|
\t\t\t\t\tbackgroundColor: primaryTint,
|
||||||
|
\t\t\t\t\tpaddingHorizontal: metrics.page.paddingHorizontal,
|
||||||
|
\t\t\t\t\tpaddingTop: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\tpaddingBottom: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\trowGap: metrics.gapY(2.6)
|
||||||
|
\t\t\t\t},`,
|
||||||
|
"SSR Glalie sidebar bottom padding",
|
||||||
|
);
|
||||||
|
source = replaceRegexOnce(
|
||||||
|
source,
|
||||||
|
/mainContent: \{\s*paddingHorizontal: metrics\.page\.paddingHorizontal,\s*paddingTop: metrics\.page\.paddingVertical,\s*(?:paddingBottom: metrics\.page\.paddingVertical\s*)?\},/,
|
||||||
|
`mainContent: {
|
||||||
|
\t\t\t\t\tpaddingHorizontal: metrics.page.paddingHorizontal,
|
||||||
|
\t\t\t\t\tpaddingTop: metrics.page.paddingVertical,
|
||||||
|
\t\t\t\t\tpaddingBottom: metrics.page.paddingVertical
|
||||||
|
\t\t\t\t},`,
|
||||||
|
"SSR Glalie main bottom padding",
|
||||||
|
);
|
||||||
|
return source.replace(/metrics\.gapY\(3\.5\)/g, "metrics.gapY(2.6)");
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchPublic(source) {
|
||||||
|
source = source
|
||||||
|
.replace(/o\.gapY\(3\.5\)/g, "o.gapY(2.6)")
|
||||||
|
.replace(/c\.gapY\(3\.5\)/g, "c.gapY(2.6)");
|
||||||
|
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"style:$(a.sidebarContent,{rowGap:o.sectionGap})",
|
||||||
|
"style:$(a.sidebarContent,{rowGap:o.gapY(2.6)})",
|
||||||
|
"public Glalie sidebar section gap",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"style:$(a.mainContent,{rowGap:o.sectionGap})",
|
||||||
|
"style:$(a.mainContent,{rowGap:o.gapY(2.6)})",
|
||||||
|
"public Glalie main section gap",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"sectionHeading:{borderBottomWidth:1,borderBottomColor:a}",
|
||||||
|
"sectionHeading:{borderBottomWidth:1,borderBottomColor:a,paddingBottom:1}",
|
||||||
|
"public Glalie heading underline padding",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"sidebarColumn:{zIndex:1,backgroundColor:o,paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,rowGap:c.sectionGap}",
|
||||||
|
"sidebarColumn:{zIndex:1,backgroundColor:o,paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,paddingBottom:c.page.paddingVertical,rowGap:c.gapY(2.6)}",
|
||||||
|
"public Glalie sidebar bottom padding",
|
||||||
|
);
|
||||||
|
source = replaceOnce(
|
||||||
|
source,
|
||||||
|
"mainContent:{paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical}",
|
||||||
|
"mainContent:{paddingHorizontal:c.page.paddingHorizontal,paddingTop:c.page.paddingVertical,paddingBottom:c.page.paddingVertical}",
|
||||||
|
"public Glalie main bottom padding",
|
||||||
|
);
|
||||||
|
return source
|
||||||
|
.replace(/o\.gapY\(3\.5\)/g, "o.gapY(2.6)")
|
||||||
|
.replace(/c\.gapY\(3\.5\)/g, "c.gapY(2.6)");
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchImporters() {
|
||||||
|
const assetsDir = "/app/apps/web/.output/public/assets";
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(assetsDir)
|
||||||
|
.filter((name) => name.endsWith(".js"))
|
||||||
|
.map((name) => `${assetsDir}/${name}`)
|
||||||
|
.filter((file) => fs.readFileSync(file, "utf8").includes("pdf-document-BplbXx-0.js"));
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
let source = fs.readFileSync(file, "utf8");
|
||||||
|
source = source.replace(/\.\/pdf-document-BplbXx-0\.js(?:\?v=rr-glalie-layout-20260518)?/g, `./pdf-document-BplbXx-0.js?v=${cacheBust}`);
|
||||||
|
fs.writeFileSync(file, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeEtag(buffer) {
|
||||||
|
const digest = crypto.createHash("sha1").update(buffer).digest("base64").replace(/=+$/g, "");
|
||||||
|
return `"${buffer.length.toString(16)}-${digest}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchStaticManifestEntry(source, urlPath, filePath) {
|
||||||
|
const buffer = fs.readFileSync(filePath);
|
||||||
|
const startMarker = `"${urlPath}": {`;
|
||||||
|
const start = source.indexOf(startMarker);
|
||||||
|
if (start === -1) throw new Error(`Static manifest entry not found for ${urlPath}`);
|
||||||
|
|
||||||
|
const commaEnd = source.indexOf("\n\t},", start);
|
||||||
|
const objectEnd = source.indexOf("\n\t}", start);
|
||||||
|
const end = commaEnd === -1 ? objectEnd : Math.min(commaEnd, objectEnd);
|
||||||
|
if (end === -1) throw new Error(`Static manifest entry end not found for ${urlPath}`);
|
||||||
|
|
||||||
|
let entry = source.slice(start, end);
|
||||||
|
entry = entry
|
||||||
|
.replace(/"etag": "(?:\\.|[^"\\])*"/, `"etag": ${JSON.stringify(makeEtag(buffer))}`)
|
||||||
|
.replace(/"mtime": "(?:\\.|[^"\\])*"/, `"mtime": ${JSON.stringify(new Date().toISOString())}`)
|
||||||
|
.replace(/"size": \d+/, `"size": ${buffer.length}`);
|
||||||
|
|
||||||
|
return source.slice(0, start) + entry + source.slice(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(ssrFile, patchSsr(fs.readFileSync(ssrFile, "utf8")));
|
||||||
|
fs.writeFileSync(publicFile, patchPublic(fs.readFileSync(publicFile, "utf8")));
|
||||||
|
|
||||||
|
const importers = patchImporters();
|
||||||
|
let serverIndex = fs.readFileSync(serverIndexFile, "utf8");
|
||||||
|
serverIndex = patchStaticManifestEntry(serverIndex, "/assets/pdf-document-BplbXx-0.js", publicFile);
|
||||||
|
for (const file of importers) {
|
||||||
|
const urlPath = `/assets/${file.split("/").pop()}`;
|
||||||
|
serverIndex = patchStaticManifestEntry(serverIndex, urlPath, file);
|
||||||
|
}
|
||||||
|
fs.writeFileSync(serverIndexFile, serverIndex);
|
||||||
|
NODE
|
||||||
|
|
||||||
|
node --check "$SSR_FILE" >/dev/null
|
||||||
|
node --check "$PUBLIC_FILE" >/dev/null
|
||||||
|
node --check "$SERVER_INDEX_FILE" >/dev/null
|
||||||
|
SH
|
||||||
1188
生成简历/backups/王志博-医工智能外科简历-排版优化前备份-20260518T194756.json
Normal file
1188
生成简历/backups/王志博-医工智能外科简历-排版优化前备份-20260518T194756.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,15 +50,15 @@
|
|||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"page": {
|
"page": {
|
||||||
"gapX": 4.2,
|
"gapX": 3.8,
|
||||||
"gapY": 0.85,
|
"gapY": 0.54,
|
||||||
"format": "a4",
|
"format": "a4",
|
||||||
"locale": "zh-CN",
|
"locale": "zh-CN",
|
||||||
"marginX": 8.4,
|
"marginX": 8.4,
|
||||||
"marginY": 8.8,
|
"marginY": 8.5,
|
||||||
"hideIcons": false
|
"hideIcons": false
|
||||||
},
|
},
|
||||||
"notes": "Design direction: refined clinical-tech academic profile. Glalie two-column layout, wider A4 margins, CJK-first typography, navy-blue medical technology accent, and two-page hierarchy for personal achievements and translational impact.",
|
"notes": "Design direction: refined clinical-tech academic profile. Glalie two-column layout with CJK-safe line height, modestly larger body type, compact section rhythm, navy-blue medical technology accent, and two-page hierarchy for personal achievements and translational impact.",
|
||||||
"design": {
|
"design": {
|
||||||
"level": {
|
"level": {
|
||||||
"icon": "star",
|
"icon": "star",
|
||||||
@@ -107,9 +107,9 @@
|
|||||||
"template": "glalie",
|
"template": "glalie",
|
||||||
"typography": {
|
"typography": {
|
||||||
"body": {
|
"body": {
|
||||||
"fontSize": 6.65,
|
"fontSize": 6.86,
|
||||||
"fontFamily": "Noto Sans SC",
|
"fontFamily": "Noto Sans SC",
|
||||||
"lineHeight": 1.14,
|
"lineHeight": 1.2,
|
||||||
"fontWeights": [
|
"fontWeights": [
|
||||||
"400",
|
"400",
|
||||||
"500",
|
"500",
|
||||||
@@ -117,9 +117,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"heading": {
|
"heading": {
|
||||||
"fontSize": 8.85,
|
"fontSize": 9.05,
|
||||||
"fontFamily": "Noto Serif SC",
|
"fontFamily": "Noto Serif SC",
|
||||||
"lineHeight": 1.12,
|
"lineHeight": 1.18,
|
||||||
"fontWeights": [
|
"fontWeights": [
|
||||||
"600",
|
"600",
|
||||||
"700"
|
"700"
|
||||||
|
|||||||
Reference in New Issue
Block a user