diff --git a/.gitignore b/.gitignore
index afaf5c6..28e6dda 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,9 @@
!resume/sample.pdf
!feasibility/sample.pdf
+# 本地定制简历(个人内容、照片、PDF,不提交)
+resume/local/
+
# IDE / 编辑器本地配置
.cursor/
.vscode/
diff --git a/resume/cv.typ b/resume/cv.typ
index 449485c..2fa9205 100644
--- a/resume/cv.typ
+++ b/resume/cv.typ
@@ -33,12 +33,22 @@
]
#let main-content = [
- #section-title("工作经历", above: space-none)
+ #if research != "" [
+ #section-title("研究经历", above: space-none)
+ #research-block()
+ ]
+
+ #section-title("工作经历", above: if research != "" { auto } else { space-none })
#experiences-block()
#section-title("项目经历")
#projects-block()
+ #if publications.len() > 0 [
+ #section-title("代表性成果")
+ #publications-block()
+ ]
+
#if awards.len() > 0 [
#section-title("获奖经历")
#awards-block()
diff --git a/resume/data/config.typ b/resume/data/config.typ
index 0bab1fe..4a6928d 100644
--- a/resume/data/config.typ
+++ b/resume/data/config.typ
@@ -99,3 +99,7 @@
)
#let awards = ()
+
+// 可选:研究经历、代表性成果(留空则不渲染)
+#let research = ""
+#let publications = ()
diff --git a/resume/data/layout.typ b/resume/data/layout.typ
index 856a0aa..f314aa2 100644
--- a/resume/data/layout.typ
+++ b/resume/data/layout.typ
@@ -234,6 +234,21 @@
}
}
+#let has-skill-groups() = skills.at("groups", default: ()).len() > 0
+
+#let skill-groups-block() = {
+ for (i, group) in skills.at("groups", default: ()).enumerate() {
+ sidebar-subtitle(
+ group.title,
+ above: if i == 0 and not has-abilities-section() { space-none } else { space-subsection-gap },
+ )
+ block(below: space-none)[
+ #set par(leading: list-item-leading, spacing: space-none, justify: true)
+ #text(fill: color-text-light, size: size-secondary)[#group.items]
+ ]
+ }
+}
+
#let tech-pill(label) = box(
fill: color-page,
stroke: stroke-pill,
@@ -255,11 +270,15 @@
#let skills-section-block() = {
abilities-block()
- sidebar-subtitle(
- "技术栈",
- above: if has-abilities-section() { space-subsection-gap } else { space-none },
- )
- tech-stack-block()
+ if has-skill-groups() {
+ skill-groups-block()
+ } else {
+ sidebar-subtitle(
+ "技术栈",
+ above: if has-abilities-section() { space-subsection-gap } else { space-none },
+ )
+ tech-stack-block()
+ }
if skills.notes.len() > 0 {
sidebar-subtitle("补充", above: space-subsection-gap)
skill-notes-block()
@@ -280,6 +299,12 @@
#text(fill: color-text-light, size: size-body)[
#entry.location#if entry.location != "" [,]#entry.start - #entry.end
]
+ #let intro = entry.at("intro", default: "")
+ #if intro != "" [
+ #v(space-body)
+ #set par(leading: list-item-leading, spacing: space-none, justify: true)
+ #text(weight: "regular", fill: color-text-light, size: size-body)[#intro]
+ ]
#v(space-body)
#set text(weight: "regular", fill: color-text-light, size: size-body)
#set par(leading: list-item-leading, spacing: space-none, justify: true)
@@ -337,3 +362,29 @@
award-entry(entry)
}
}
+
+#let research-block() = {
+ if research != "" {
+ block(above: space-none)[
+ #set par(leading: leading-body, spacing: space-none, justify: true)
+ #text(fill: color-text-light, size: size-body)[#research]
+ ]
+ }
+}
+
+#let publication-entry(entry, index) = block(above: space-none, below: space-none)[
+ #if index > 1 { v(space-between-entries) }
+ #text(fill: color-body, size: size-secondary, weight: "bold")[
+ \[#index\] #entry.title
+ ]
+ #v(space-inline)
+ #text(fill: color-text-light, size: size-secondary)[#entry.authors]
+ #v(space-micro)
+ #text(fill: color-text-light, size: size-secondary)[#entry.venue]
+]
+
+#let publications-block() = {
+ for (i, entry) in publications.enumerate() {
+ publication-entry(entry, i + 1)
+ }
+}
diff --git a/resume/sample.pdf b/resume/sample.pdf
index b31f402..546f839 100644
--- a/resume/sample.pdf
+++ b/resume/sample.pdf
@@ -4733,12 +4733,12 @@ T
endstream
endobj
119 0 obj
-<>
+<>
endobj
120 0 obj
<>
stream
-Typst 0.15.0zh2026-06-25T16:22:02+08:002026-06-25T16:22:02+08:001application/pdfwFnMDS1jMz01jF6pKGYgXA==wFnMDS1jMz01jF6pKGYgXA==proof1.7
+Typst 0.15.0zh2026-06-25T17:10:58+08:002026-06-25T17:10:58+08:001application/pdfYG9afPnvozvA+8c8Oh87JQ==YG9afPnvozvA+8c8Oh87JQ==proof1.7
endstream
endobj
121 0 obj
@@ -4869,7 +4869,7 @@ xref
0001005638 00000 n
0001006711 00000 n
trailer
-<>
+<>
startxref
1006881
%%EOF
\ No newline at end of file