amlei-skills 0.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.
Files changed (36) hide show
  1. package/.claude-plugin/plugin.json +16 -0
  2. package/LICENSE +21 -0
  3. package/README.md +21 -0
  4. package/package.json +35 -0
  5. package/skills/academic-ref-retrieval/SKILL.md +103 -0
  6. package/skills/academic-ref-retrieval/resources/arxiv-api-example.py +42 -0
  7. package/skills/academic-ref-retrieval/resources/cnki-scraper-example.py +58 -0
  8. package/skills/amlei-git-gh/SKILL.md +156 -0
  9. package/skills/amlei-resume/SKILL.md +204 -0
  10. package/skills/amlei-resume/assets/preview-shell.html +438 -0
  11. package/skills/amlei-resume/assets/sample-preview.html +361 -0
  12. package/skills/amlei-resume/assets/sample-resume.md +57 -0
  13. package/skills/amlei-resume/references/export.md +76 -0
  14. package/skills/amlei-resume/references/icons.md +35 -0
  15. package/skills/amlei-resume/references/profile-evaluator.md +44 -0
  16. package/skills/amlei-resume/references/profile.md +43 -0
  17. package/skills/amlei-resume/references/projects.md +76 -0
  18. package/skills/amlei-resume/references/resume-evaluator.md +121 -0
  19. package/skills/amlei-resume/references/revise.md +11 -0
  20. package/skills/amlei-resume/references/themes/theme-academic.md +238 -0
  21. package/skills/amlei-resume/references/themes/theme-content-green.md +138 -0
  22. package/skills/amlei-resume/references/themes/theme-english-mnc.md +127 -0
  23. package/skills/amlei-resume/references/themes/theme-index.md +46 -0
  24. package/skills/amlei-resume/references/themes/theme-sidebar-creative.md +163 -0
  25. package/skills/amlei-resume/references/themes/theme-soe-formal.md +153 -0
  26. package/skills/amlei-resume/references/themes/theme-tech-dense.md +157 -0
  27. package/skills/amlei-resume/references/write.md +14 -0
  28. package/skills/amlei-resume/scripts/boss_zhipin.py +676 -0
  29. package/skills/amlei-resume/scripts/extract_avatar.py +157 -0
  30. package/skills/amlei-resume/scripts/profile.py +465 -0
  31. package/skills/amlei-resume/scripts/validate_resume.py +195 -0
  32. package/skills/amlei-resume/scripts/wrap_preview.py +104 -0
  33. package/skills/amlei-story-generator/SKILL.md +67 -0
  34. package/skills/amlei-story-generator/resources/narrative-craft.md +74 -0
  35. package/skills/amlei-story-generator/resources/rubric.md +28 -0
  36. package/skills/amlei-story-generator/resources/source-frameworks.md +89 -0
@@ -0,0 +1,438 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{TITLE}} · 简历 A4 预览</title>
7
+ <!--
8
+ 主题无关的预览外壳(chrome):
9
+ · 顶部"导出 PDF"工具条(屏幕可见 / 打印隐藏)
10
+ · A4 舞台(.viewport/.doc-stage/.scaler/.page)+ 自动分页 JS
11
+ · 打印规则(每页严格 A4、工具条隐藏)
12
+ 主题的视觉(设计令牌 + 组件 class 样式)由 wrap_preview.py 把
13
+ 渲染产物里的 <style> 注入到下方的 <!--RESUME_STYLE--> 槽位。
14
+ 本文件不持有任何主题色 / 组件样式,换主题无需改这里。
15
+ -->
16
+ <style>
17
+ /* —— 工具条(chrome,用字面色,不依赖主题令牌)—— */
18
+ *{box-sizing:border-box;margin:0;padding:0}
19
+ html{-webkit-text-size-adjust:100%}
20
+ body{
21
+ background:#e9ebef;
22
+ font-family:'PingFang SC','HarmonyOS Sans SC','Microsoft YaHei',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
23
+ color:#1b1f24;
24
+ -webkit-font-smoothing:antialiased;
25
+ }
26
+ .rv-toolbar{
27
+ position:sticky;top:0;z-index:99;
28
+ display:flex;align-items:center;justify-content:space-between;gap:12px;
29
+ padding:10px 18px;background:#fff;border-bottom:1px solid #d7dbe0;
30
+ box-shadow:0 1px 10px rgba(20,24,30,.05);
31
+ }
32
+ .rv-toolbar .rv-hint{font-size:12.5px;color:#5a6470;line-height:1.4}
33
+ .rv-toolbar .rv-hint b{color:#1b1f24}
34
+ .rv-toolbar .rv-actions{display:flex;align-items:center;gap:8px}
35
+ .rv-btn{
36
+ border:1px solid #d7dbe0;background:#fff;color:#1b1f24;
37
+ border-radius:8px;padding:8px 14px;font-size:13px;font-weight:600;
38
+ font-family:inherit;cursor:pointer;white-space:nowrap;line-height:1;
39
+ transition:background .15s,border-color .15s,transform .06s;
40
+ }
41
+ .rv-btn:hover{background:#eef1f4;border-color:#aab2bb}
42
+ .rv-btn:active{transform:translateY(1px)}
43
+ .rv-btn--primary{background:#2b5bd6;color:#fff;border-color:#2b5bd6}
44
+ .rv-btn--primary:hover{background:#234cba;border-color:#234cba}
45
+ .rv-toast{
46
+ position:fixed;top:60px;left:50%;transform:translateX(-50%);
47
+ background:#1b1f24;color:#fff;padding:10px 18px;border-radius:9px;
48
+ font-size:13px;font-weight:600;opacity:0;pointer-events:none;
49
+ transition:opacity .25s;z-index:100;box-shadow:0 6px 20px rgba(20,24,30,.25);
50
+ max-width:88vw;text-align:center;
51
+ }
52
+ .rv-toast.show{opacity:1}
53
+
54
+ /* —— 配色方案切换:覆盖主题点缀色 --accent*(soe 的 --navy* 跟随)——
55
+ :root[data-scheme] 特异性 (0,2,0) > 主题 :root (0,1,0),故总能盖过主题原色。
56
+ 不选(默认)= 主题原色。选了就实时换色,打印 / 导出 PDF 沿用当前配色。 */
57
+ .rv-scheme{display:flex;align-items:center;gap:5px;font-size:12px;color:#5a6470;user-select:none}
58
+ .rv-select{
59
+ border:1px solid #d7dbe0;background:#fff;color:#1b1f24;border-radius:7px;
60
+ padding:6px 8px;font-size:12.5px;font-family:inherit;cursor:pointer;line-height:1.4;
61
+ }
62
+ .rv-select:hover{border-color:#aab2bb}
63
+ :root[data-scheme="ink"]{--accent:oklch(40% .14 255);--accent-ink:oklch(34% .12 255);--accent-soft:oklch(96% .022 255);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
64
+ :root[data-scheme="tech"]{--accent:oklch(46% .17 242);--accent-ink:oklch(38% .15 242);--accent-soft:oklch(95% .03 242);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
65
+ :root[data-scheme="green"]{--accent:oklch(42% .07 155);--accent-ink:oklch(32% .06 155);--accent-soft:oklch(95% .022 155);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
66
+ :root[data-scheme="burgundy"]{--accent:oklch(40% .13 25);--accent-ink:oklch(33% .12 25);--accent-soft:oklch(95% .022 25);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
67
+ :root[data-scheme="navy"]{--accent:oklch(33% .06 258);--accent-ink:oklch(26% .06 258);--accent-soft:oklch(95% .012 258);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
68
+ :root[data-scheme="purple"]{--accent:oklch(42% .12 295);--accent-ink:oklch(34% .11 295);--accent-soft:oklch(95% .022 295);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
69
+ :root[data-scheme="amber"]{--accent:oklch(55% .13 60);--accent-ink:oklch(45% .12 60);--accent-soft:oklch(95% .03 60);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
70
+ :root[data-scheme="graphite"]{--accent:oklch(28% .006 250);--accent-ink:oklch(22% .006 250);--accent-soft:oklch(95% .004 250);--navy:var(--accent);--navy-deep:var(--accent-ink);--navy-soft:var(--accent-soft)}
71
+
72
+ /* —— A4 舞台结构(所有主题共用)—— */
73
+ .viewport{width:100%;overflow-x:auto;overflow-y:visible}
74
+ .doc-stage{position:relative;width:100%;margin:0 auto}
75
+ .scaler{
76
+ display:flex;flex-direction:column;align-items:center;gap:26px;
77
+ padding:30px 0;width:210mm;margin:0 auto;
78
+ transform:scale(var(--scale,1));transform-origin:top center;
79
+ }
80
+ .page{
81
+ width:210mm;height:297mm;background:#fff;position:relative;
82
+ overflow:hidden;flex-shrink:0;
83
+ box-shadow:0 1px 2px rgba(20,24,30,.05),0 18px 44px -22px rgba(20,24,30,.28);
84
+ }
85
+ .page-content{position:absolute;inset:0;padding:14mm 16mm 13mm}
86
+ .page-content>*:first-child{margin-top:0!important}
87
+ #source{position:absolute;left:-100000px;top:0;width:178mm;opacity:0;pointer-events:none}
88
+
89
+ /* —— 打印 / 导出 PDF —— */
90
+ @media print{
91
+ @page{size:A4;margin:0}
92
+ html,body{background:#fff!important}
93
+ *{print-color-adjust:exact;-webkit-print-color-adjust:exact}
94
+ .rv-toolbar,.rv-toast{display:none!important}
95
+ .viewport{overflow:visible!important}
96
+ .doc-stage{height:auto!important}
97
+ .scaler{transform:none!important;gap:0!important;padding:0!important;width:auto!important;display:block!important}
98
+ .page{width:210mm!important;height:297mm!important;box-shadow:none!important;page-break-after:always;break-after:page}
99
+ .page:last-child{page-break-after:auto;break-after:auto}
100
+ .page .page-content{background:var(--paper,#fff)!important}
101
+ }
102
+ </style>
103
+ <!--RESUME_STYLE-->
104
+ </head>
105
+ <body data-od-id="resume-preview">
106
+
107
+ <!-- 顶部工具条:屏幕可见,打印隐藏 -->
108
+ <div class="rv-toolbar">
109
+ <span class="rv-hint">👇 下方是 A4 简历效果 · 点右侧 <b>导出 PDF</b> 可直接打印或另存为 PDF</span>
110
+ <div class="rv-actions">
111
+ <label class="rv-scheme">配色
112
+ <select id="rvScheme" class="rv-select" title="切换简历点缀色(导出 PDF 沿用)">
113
+ <option value="">主题原色</option>
114
+ <option value="ink">墨蓝</option>
115
+ <option value="tech">工程蓝</option>
116
+ <option value="green">森林绿</option>
117
+ <option value="burgundy">酒红</option>
118
+ <option value="navy">藏青</option>
119
+ <option value="purple">紫</option>
120
+ <option value="amber">琥珀</option>
121
+ <option value="graphite">石墨黑</option>
122
+ </select>
123
+ </label>
124
+ <button class="rv-btn" id="rvZoomOut" title="缩小查看">-</button>
125
+ <button class="rv-btn" id="rvZoomIn" title="放大查看">+</button>
126
+ <button class="rv-btn" id="rvEditBtn">✏️ 编辑</button>
127
+ <button class="rv-btn rv-btn--primary" id="rvPdfBtn" onclick="rvPrint()">📄 导出 PDF</button>
128
+ <button class="rv-btn rv-btn--primary" id="rvSaveBtn" style="display:none">📝 源码</button>
129
+ </div>
130
+ </div>
131
+ <div class="rv-toast" id="rvToast"></div>
132
+
133
+ <!-- A4 舞台:分页 JS 把 #source 里的原子分配到各 .page -->
134
+ <div class="viewport" id="viewport"><div class="doc-stage" id="docStage"><div class="scaler" id="scaler"></div></div></div>
135
+
136
+ <!-- 简历正文原子(由 wrap_preview.py 注入;渲染器=LLM 按所选主题的组件库装配) -->
137
+ <div id="source">
138
+ <!--RESUME_BODY-->
139
+ </div>
140
+
141
+ <script>
142
+ var rvUserScale=1;
143
+ function rvShowToast(msg){
144
+ var t=document.getElementById('rvToast');
145
+ t.textContent=msg;t.classList.add('show');
146
+ clearTimeout(t._timer);t._timer=setTimeout(function(){t.classList.remove('show');},2600);
147
+ }
148
+ function rvPrint(){
149
+ rvShowToast('在弹出的打印窗口里选"另存为 PDF"即可导出 A4 简历');
150
+ setTimeout(function(){ window.print(); },120);
151
+ }
152
+
153
+ /* 配色方案切换:覆盖主题点缀色,记住选择;打印 / 导出 PDF 沿用当前配色 */
154
+ (function(){
155
+ var KEY='resume-color-scheme', html=document.documentElement, sel=document.getElementById('rvScheme');
156
+ var store={
157
+ get:function(k){try{return localStorage.getItem(k)}catch(e){return null}},
158
+ set:function(k,v){try{v==null?localStorage.removeItem(k):localStorage.setItem(k,v)}catch(e){}}
159
+ };
160
+ var saved=store.get(KEY);
161
+ if(saved){ html.dataset.scheme=saved; if(sel) sel.value=saved; }
162
+ if(sel){
163
+ sel.addEventListener('change',function(){
164
+ var v=sel.value;
165
+ if(v){ html.dataset.scheme=v; store.set(KEY,v); }
166
+ else { delete html.dataset.scheme; store.set(KEY,null); }
167
+ });
168
+ }
169
+ })();
170
+
171
+ /* ============================================================
172
+ A4 自动分页:测量 #source 每个原子高度,按可用内容高分配页。
173
+ data-stick="1" 的 sec-head 不会和紧随其后的条目被拆到两页。
174
+ ============================================================ */
175
+ (function(){
176
+ var PX_PER_MM=96/25.4,PAGE_H_MM=297,PAGE_W_MM=210;
177
+ var tmp=document.createElement('div');tmp.className='page-content';tmp.style.position='absolute';tmp.style.left='-9999px';
178
+ document.body.appendChild(tmp);
179
+ var cs=getComputedStyle(tmp);
180
+ var PAD_LEFT_PX=parseFloat(cs.paddingLeft),PAD_RIGHT_PX=parseFloat(cs.paddingRight);
181
+ var PAD_TOP_PX=parseFloat(cs.paddingTop),PAD_BOTTOM_PX=parseFloat(cs.paddingBottom);
182
+ tmp.remove();
183
+ var CONTENT_H_PX=(PAGE_H_MM*PX_PER_MM)-PAD_TOP_PX-PAD_BOTTOM_PX;
184
+ var GAP_PX=26,PAD_STAGE_PX=30;
185
+ var source=document.getElementById('source'),
186
+ scaler=document.getElementById('scaler'),
187
+ docStage=document.getElementById('docStage'),
188
+ viewport=document.getElementById('viewport');
189
+ function collect(){
190
+ return Array.prototype.slice.call(source.children).filter(function(el){
191
+ return el.nodeType===1;
192
+ });
193
+ }
194
+ function candidate(){
195
+ var nameEl=source.querySelector('.name-row .name')||source.querySelector('.head .name')
196
+ ||source.querySelector('.name-block .cn')||source.querySelector('.name')||source.querySelector('.cn')||source.querySelector('h1');
197
+ var roleEl=source.querySelector('.name-row .role')||source.querySelector('.head .role')||source.querySelector('.role');
198
+ var name=nameEl?nameEl.textContent.trim():'简历';
199
+ var role='';
200
+ if(roleEl){
201
+ role=roleEl.textContent.trim()
202
+ .replace(/^(意向岗位|求职意向|目标岗位|申请方向)\s*[::·.\-\s]*/,'')
203
+ .split(/\s*[\/·—,,]\s*/)[0]
204
+ .replace(/[((][^))]*[))]\s*$/,'')
205
+ .replace(/\s+/g,'');
206
+ }
207
+ return role?(name+'-'+role):(name+' · 简历');
208
+ }
209
+ function outerHeight(el){
210
+ var st=getComputedStyle(el);
211
+ return el.offsetHeight+parseFloat(st.marginTop||0)+parseFloat(st.marginBottom||0);
212
+ }
213
+ function distribute(atoms){
214
+ var heights=atoms.map(outerHeight),pages=[[]],used=0;
215
+ for(var i=0;i<atoms.length;i++){
216
+ var h=heights[i],
217
+ stick=atoms[i].getAttribute && atoms[i].getAttribute('data-stick')==='1',
218
+ nextH=(i+1<atoms.length)?heights[i+1]:0,
219
+ placeOnNew=false;
220
+ if(used!==0){
221
+ if(used+h>CONTENT_H_PX) placeOnNew=true;
222
+ else if(stick && nextH>0 && (used+h+nextH>CONTENT_H_PX)) placeOnNew=true;
223
+ }
224
+ if(placeOnNew){pages.push([]);used=0;}
225
+ pages[pages.length-1].push(i);used+=h;
226
+ }
227
+ return pages;
228
+ }
229
+ function makePage(){
230
+ var p=document.createElement('section');p.className='page';
231
+ p.innerHTML='<div class="page-content"></div>';return p;
232
+ }
233
+ function render(){
234
+ var atoms=collect(); if(!atoms.length){fitScale();return;}
235
+ var pages=distribute(atoms);
236
+ scaler.innerHTML='';
237
+ pages.forEach(function(idxs){
238
+ var page=makePage(),content=page.querySelector('.page-content');
239
+ idxs.forEach(function(i){content.appendChild(atoms[i].cloneNode(true));});
240
+ scaler.appendChild(page);
241
+ });
242
+ fitScale();
243
+ }
244
+ function fitScale(){
245
+ var pageWPx=210*PX_PER_MM,s=Math.min(1,viewport.clientWidth/pageWPx)*rvUserScale;
246
+ scaler.style.setProperty('--scale',s);
247
+ var n=scaler.querySelectorAll('.page').length,
248
+ fullH=PAD_STAGE_PX*2+n*(297*PX_PER_MM)+Math.max(0,n-1)*GAP_PX;
249
+ docStage.style.height=(fullH*s)+'px';
250
+ }
251
+ function init(){
252
+ source.style.width=(PAGE_W_MM*PX_PER_MM-PAD_LEFT_PX-PAD_RIGHT_PX)+'px';
253
+ render();try{document.title=candidate();}catch(e){}
254
+ // 初始渲染完成后解除重绘事件(避免编辑模式被覆盖)
255
+ if(document.fonts&&document.fonts.ready)document.fonts.ready.then(function(){});
256
+ window.removeEventListener('load',render);
257
+ }
258
+ if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
259
+ if(document.fonts&&document.fonts.ready)document.fonts.ready.then(render);
260
+ window.addEventListener('load',render);
261
+ window.addEventListener('resize',fitScale);
262
+ document.getElementById('rvZoomIn').addEventListener('click',function(){
263
+ rvUserScale=Math.min(2,+(rvUserScale+0.1).toFixed(2));fitScale();});
264
+ document.getElementById('rvZoomOut').addEventListener('click',function(){
265
+ rvUserScale=Math.min(2,+(rvUserScale+0.1).toFixed(2));fitScale();});
266
+
267
+ // 暴露给编辑模式:按当前页面内容重新分页(保留编辑后的 DOM 节点)
268
+ window.__reflow = function(){
269
+ var allAtoms=[];
270
+ document.querySelectorAll('#scaler .page-content').forEach(function(c){
271
+ Array.from(c.children).forEach(function(el){allAtoms.push(el);});
272
+ });
273
+ if(!allAtoms.length) return;
274
+ var heights=allAtoms.map(outerHeight), pages=distribute(allAtoms);
275
+ scaler.innerHTML='';
276
+ pages.forEach(function(idxs){
277
+ var page=makePage(), content=page.querySelector('.page-content');
278
+ idxs.forEach(function(i){content.appendChild(allAtoms[i]);});
279
+ scaler.appendChild(page);
280
+ });
281
+ fitScale();
282
+ // 重新挂 contenteditable
283
+ var sel='.bullet,.summary p,.desc,.entry-title,.entry-meta span,.name,.role,.edu-line,.contact,.name-block .cn,.name-block .en,.one';
284
+ document.querySelectorAll('#scaler '+sel).forEach(function(el){
285
+ if(window.__editing){ el.contentEditable=true; el.style.outline='1px dashed var(--accent, #888)'; }
286
+ });
287
+ };
288
+ })();
289
+
290
+ /* —— 编辑模式:contenteditable + 自动隐藏空行 + 重排 + 保存 —— */
291
+ (function(){
292
+ var editing=false, editBtn=document.getElementById('rvEditBtn'), saveBtn=document.getElementById('rvSaveBtn');
293
+ var reflowTimer=null;
294
+
295
+ function setEditable(on){
296
+ window.__editing=on;
297
+ var sel='.bullet,.summary p,.desc,.entry-title,.entry-meta span,.name,.role,.edu-line,.contact,.name-block .cn,.name-block .en,.one';
298
+ document.querySelectorAll('#scaler '+sel).forEach(function(el){
299
+ el.contentEditable=on;
300
+ el.style.outline=on?'1px dashed var(--accent, #888)':'';
301
+ });
302
+ // 技能 chip 编辑:删除按钮 + 新增按钮
303
+ document.querySelectorAll('#scaler .stack').forEach(function(stack){
304
+ stack.querySelectorAll('.chips').forEach(function(chips){
305
+ // 清除旧按钮避免重复
306
+ chips.querySelectorAll('.chip-del,.chip-add').forEach(function(b){b.remove();});
307
+ if(on){
308
+ // 每个 chip 右上角删除
309
+ chips.querySelectorAll('.chip').forEach(function(chip){
310
+ var del=document.createElement('span');
311
+ del.className='chip-del';
312
+ del.textContent='×';
313
+ del.style.cssText='cursor:pointer;margin-left:4px;font-size:11px;color:var(--accent,#888);line-height:1';
314
+ del.onclick=function(e){e.stopPropagation();chip.remove();};
315
+ chip.appendChild(del);
316
+ });
317
+ // 末尾新增按钮
318
+ var add=document.createElement('span');
319
+ add.className='chip-add';
320
+ add.textContent='+';
321
+ add.style.cssText='cursor:pointer;font-family:var(--font-mono,monospace);font-size:14px;color:var(--accent,#888);border:1px dashed var(--accent,#888);padding:1.5px 8px;line-height:1.4';
322
+ add.onclick=function(){
323
+ var newChip=document.createElement('span');
324
+ newChip.className='chip';
325
+ newChip.contentEditable=true;
326
+ newChip.textContent='新技能';
327
+ var del=document.createElement('span');
328
+ del.className='chip-del';
329
+ del.textContent='×';
330
+ del.style.cssText='cursor:pointer;margin-left:4px;font-size:11px;color:var(--accent,#888);line-height:1';
331
+ del.onclick=function(e){e.stopPropagation();newChip.remove();};
332
+ newChip.appendChild(del);
333
+ chips.insertBefore(newChip,add);
334
+ newChip.focus();
335
+ // 选中文字方便直接输入
336
+ var r=document.createRange();r.selectNodeContents(newChip);var s=window.getSelection();s.removeAllRanges();s.addRange(r);
337
+ };
338
+ chips.appendChild(add);
339
+ }
340
+ });
341
+ });
342
+ }
343
+
344
+ function afterEdit(){
345
+ // 隐藏空 bullet
346
+ document.querySelectorAll('#scaler .bullet').forEach(function(b){
347
+ b.style.display=b.textContent.trim()?'':'none';
348
+ });
349
+ // 防抖重排
350
+ if(reflowTimer) clearTimeout(reflowTimer);
351
+ reflowTimer=setTimeout(function(){
352
+ if(window.__reflow) window.__reflow();
353
+ // 重排后重新隐藏空 bullet
354
+ document.querySelectorAll('#scaler .bullet').forEach(function(b){
355
+ b.style.display=b.textContent.trim()?'':'none';
356
+ });
357
+ }, 400);
358
+ }
359
+
360
+ var pdfBtn=document.getElementById('rvPdfBtn');
361
+ editBtn.addEventListener('click',function(){
362
+ editing=!editing;
363
+ editBtn.textContent=editing?'查看':'✏️ 编辑';
364
+ pdfBtn.style.display=editing?'none':'';
365
+ saveBtn.style.display=editing?'':'none';
366
+ setEditable(editing);
367
+ if(editing){
368
+ document.querySelector('#scaler').addEventListener('input',afterEdit);
369
+ } else {
370
+ document.querySelector('#scaler').removeEventListener('input',afterEdit);
371
+ }
372
+ });
373
+
374
+ saveBtn.addEventListener('click',function(){
375
+ var md='';
376
+ document.querySelectorAll('#scaler .page').forEach(function(page){
377
+ page.querySelectorAll('.page-content>*').forEach(function(atom){
378
+ var tag=atom.tagName.toLowerCase();
379
+ if(atom.classList.contains('resume-header')){
380
+ md+='# self-intro\n';
381
+ var name=atom.querySelector('.name'), role=atom.querySelector('.role');
382
+ if(name) md+='name: '+name.textContent.trim()+'\n';
383
+ if(role) md+='role: '+role.textContent.trim()+'\n';
384
+ var edu=atom.querySelector('.edu-line');
385
+ if(edu) md+='education: '+edu.textContent.trim()+'\n';
386
+ var contact=atom.querySelector('.contact');
387
+ if(contact) md+='contact: '+contact.textContent.trim()+'\n\n';
388
+ } else if(atom.classList.contains('sec-head')){
389
+ var title=atom.querySelector('h2');
390
+ if(title) md+='\n# '+title.textContent.trim()+'\n';
391
+ } else if(atom.classList.contains('bullet')){
392
+ md+='- '+atom.textContent.trim()+'\n';
393
+ } else if(atom.classList.contains('entry')){
394
+ var org=atom.querySelector('.org,.proj'), role=atom.querySelector('.entry-title .role');
395
+ if(org) md+='\n## '+org.textContent.trim();
396
+ if(role) md+=' | '+role.textContent.trim();
397
+ md+='\n';
398
+ var date=atom.querySelector('.entry-date');
399
+ if(date) md+='date: '+date.textContent.trim()+'\n';
400
+ var meta=atom.querySelector('.entry-meta span');
401
+ if(meta) md+='meta: '+meta.textContent.trim()+'\n';
402
+ } else if(atom.classList.contains('desc')){
403
+ md+=atom.textContent.trim()+'\n';
404
+ } else if(atom.classList.contains('summary')){
405
+ md+=atom.textContent.trim()+'\n';
406
+ } else if(atom.classList.contains('stack')){
407
+ atom.querySelectorAll('.stack-row').forEach(function(row){
408
+ var cat=row.querySelector('.cat');
409
+ if(cat) md+='- '+cat.textContent.trim()+': ';
410
+ var chips=row.querySelectorAll('.chip');
411
+ md+=Array.from(chips).map(function(c){return c.textContent.trim();}).join(' · ')+'\n';
412
+ });
413
+ }
414
+ });
415
+ });
416
+ // show result
417
+ var ta=document.getElementById('rvMdOutput');
418
+ if(!ta){
419
+ ta=document.createElement('textarea');
420
+ ta.id='rvMdOutput';
421
+ ta.style.cssText='position:fixed;top:56px;right:12px;width:420px;height:70vh;z-index:100;font-family:monospace;font-size:12px;padding:10px;border:1px solid #d7dbe0;border-radius:8px;background:#fff;box-shadow:0 8px 30px rgba(0,0,0,.12);resize:both;white-space:pre;line-height:1.5';
422
+ var close=document.createElement('div');
423
+ close.textContent='✕';
424
+ close.style.cssText='position:fixed;top:56px;right:440px;z-index:101;cursor:pointer;font-size:18px;color:#888;padding:4px 8px';
425
+ close.onclick=function(){ta.remove();close.remove();};
426
+ document.body.appendChild(close);
427
+ document.body.appendChild(ta);
428
+ }
429
+ ta.value=md;
430
+ ta.style.display='block';
431
+ ta.focus();
432
+ ta.select();
433
+ rvShowToast('已生成 Markdown,可复制');
434
+ });
435
+ })();
436
+ </script>
437
+ </body>
438
+ </html>