-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
682 lines (645 loc) · 39.5 KB
/
Copy pathscript.js
File metadata and controls
682 lines (645 loc) · 39.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
const translations = {
zh: {
skipLink: "跳到主要内容",
navLabel: "页面导航",
backToTop: "回到顶部",
primaryNavLabel: "主要分区",
navWork: "项目",
navCapabilities: "能力",
navRepositories: "仓库",
navAbout: "关于",
languageLabel: "语言",
themeToggleDark: "切换深色模式",
themeToggleLight: "切换浅色模式",
workTitle: "最近做的",
workIntro: "最近比较完整的四项工作。",
filterLabel: "按方向筛选",
filterAll: "全部",
filterHardwareAi: "D2NN / 光电",
filterEmbedded: "嵌入式",
filterSoftware: "软件 / Web",
filterModeling: "建模 / 数据",
filterOther: "其他",
searchLabel: "搜索仓库",
searchPlaceholder: "搜索项目或技术",
resetFilters: "重置筛选",
repositoryListLabel: "公开项目索引",
aboutTitle: "关于我",
contactTitle: "联系",
sourceLink: "本站源码 ↗",
repository: "代码",
live: "在线打开",
technicalNotes: "项目里做了什么",
count: (shown, total) => `显示 ${shown} / ${total} 个项目`,
emptyState: "没有匹配的项目。调整搜索词或重置筛选。",
projectPreview: "项目预览",
pageTitle: "陈云海 / Cyh29hao",
pageDescription: "陈云海的个人主页:嵌入式、D2NN 复现、软件工具,以及全部公开 GitHub 项目。",
navArchive: "全部",
introNote: "上海 · 本科在读",
introTitle: "你好,我是陈云海。",
introLead: "我在上海交通大学集成电路学院读本科。最近主要在做嵌入式、D2NN 复现,也写一些自己或身边的人会实际用到的小工具。",
introBody: "这个网站把我做过的项目集中放在一起。下面先列最近的几项,再往后是全部公开仓库。",
quickLinksLabel: "常用链接",
labWebsite: "实验室网站",
minesweeperOnline: "在线扫雷",
archiveTitle: "全部项目",
archiveIntro: "目前公开的 19 个仓库。可以按方向筛选,也可以直接搜索。",
toolsTitle: "用过的东西",
toolsIntro: "这些不是熟练度排名,只是我在项目里确实用到过的工具。",
toolsEmbedded: "嵌入式",
toolsSoftware: "软件与网页",
toolsModeling: "建模与实验",
aboutBodyOne: "我目前是上海交通大学集成电路学院 2024 级本科生。课程和项目里接触得最多的是电路、嵌入式和信号处理;课外则在整理 D2NN 复现,也给实际使用场景写过桌面工具和网页。",
aboutBodyTwo: "2026 年参加 SJTUCTF,线上赛和线下赛均获二等奖;也参加了 GoldRush 2.0 智能决策竞赛,做过 C++ 策略实现、自动化对局和结果分析。公开仓库只放适合公开的版本;课程数据、模型权重或业务材料不方便上传时,我会在对应 README 里说明。",
contactText: "邮件最方便,也可以直接从 GitHub 找到我。",
notes: "展开",
hideNotes: "收起",
featuredLabel: "重点项目",
showMore: (remaining) => `继续看其余 ${remaining} 个`,
showLess: "收起全部项目",
shuffleProject: "随便看看",
},
en: {
skipLink: "Skip to main content",
navLabel: "Page navigation",
backToTop: "Back to top",
primaryNavLabel: "Primary sections",
navWork: "Projects",
navCapabilities: "Capabilities",
navRepositories: "Repositories",
navAbout: "About",
languageLabel: "Language",
themeToggleDark: "Switch to dark theme",
themeToggleLight: "Switch to light theme",
workTitle: "Recent work",
workIntro: "Four of my more complete recent projects.",
filterLabel: "Filter by direction",
filterAll: "All",
filterHardwareAi: "D2NN / Photonics",
filterEmbedded: "Embedded",
filterSoftware: "Software / Web",
filterModeling: "Modeling / Data",
filterOther: "Other",
searchLabel: "Search repositories",
searchPlaceholder: "Search projects or tools",
resetFilters: "Reset filters",
repositoryListLabel: "Public project index",
aboutTitle: "About",
contactTitle: "Contact",
sourceLink: "Site source ↗",
repository: "Code",
live: "Open live",
technicalNotes: "What I worked on",
count: (shown, total) => `Showing ${shown} of ${total} projects`,
emptyState: "No projects match. Change the query or reset the filters.",
projectPreview: "project preview",
pageTitle: "Chen Yunhai / Cyh29hao",
pageDescription: "Chen Yunhai’s personal site: embedded systems, D2NN reproduction, software tools, and all public GitHub projects.",
navArchive: "All work",
introNote: "Shanghai · Undergraduate",
introTitle: "Hi, I’m Chen Yunhai.",
introLead: "I study integrated circuits at Shanghai Jiao Tong University. Lately I’ve been working on embedded systems, reproducing D2NN experiments, and writing small tools that I or people around me actually use.",
introBody: "This site keeps those projects in one place. A few recent ones come first, followed by the full list of public repositories.",
quickLinksLabel: "Quick links",
labWebsite: "Lab website",
minesweeperOnline: "Minesweeper",
archiveTitle: "All projects",
archiveIntro: "Nineteen public repositories so far. Filter by area or search directly.",
toolsTitle: "Things I’ve used",
toolsIntro: "This is not a proficiency ranking, just a list of tools I have actually used in the projects above.",
toolsEmbedded: "Embedded",
toolsSoftware: "Software and web",
toolsModeling: "Modeling and experiments",
aboutBodyOne: "I am a 2024-entry undergraduate at SJTU’s School of Integrated Circuits. Most of my coursework and projects are around circuits, embedded systems, and signal processing. Outside class, I have been organizing D2NN reproductions and building desktop tools and websites for real use cases.",
aboutBodyTwo: "I received second prize in both the online and offline rounds of SJTUCTF 2026. I also took part in the GoldRush 2.0 intelligent-decision competition, working on C++ strategy implementation, automated matches, and result analysis. The repositories here are public editions; when course data, model weights, or business material cannot be uploaded, I say so in the relevant README.",
contactText: "Email works best. You can also find me directly on GitHub.",
notes: "Notes",
hideNotes: "Close",
featuredLabel: "Featured projects",
showMore: (remaining) => `Show ${remaining} more`,
showLess: "Show fewer projects",
shuffleProject: "Pick one for me",
},
};
const categoryNames = {
all: { zh: "全部", en: "All" },
"ai-hardware": { zh: "D2NN / 光电", en: "D2NN / Photonics" },
embedded: { zh: "嵌入式", en: "Embedded" },
software: { zh: "软件 / Web", en: "Software / Web" },
modeling: { zh: "建模 / 数据", en: "Modeling / Data" },
other: { zh: "其他", en: "Other" },
};
const projects = [
{
name: "Smart-Networked-Clock-System",
title: "Smart Networked Clock System",
url: "https://github.com/Cyh29hao/Smart-Networked-Clock-System",
category: "embedded",
featured: 1,
image: "./assets/previews/smart-clock.png",
tags: ["C", "TM4C1294", "S800", "UART", "PyQt5", "NTP"],
overview: {
zh: "一套 TM4C1294/S800 智能联网时钟。板端 C 固件负责按键、显示和串口通信,PyQt5 上位机负责配置、网络对时、天气和日程。",
en: "A TM4C1294/S800 smart clock. The C firmware handles buttons, display, and serial communication; a PyQt5 desktop app handles setup, network time, weather, and schedules.",
},
technical: {
zh: ["板端固件围绕时间状态、按键事件、显示刷新和串口收发组织。", "PC 端通过串口协议与板端协同,并保留 Keil 构建与真机烧录说明。", "README 记录协议回归和 v2.2 验证结果。"],
en: ["Firmware is organized around time state, key events, display refresh, and serial I/O.", "The desktop host coordinates with the board through a serial protocol, with Keil build and flashing instructions retained.", "The README records protocol regression and v2.2 verification results."],
},
evidence: {
zh: "公开仓库包含代码、截图、构建说明、运行说明和验证记录。",
en: "The public repository includes code, screenshots, build and run instructions, and verification records.",
},
},
{
name: "D2NN-ViT-KD-Ver11r-Reproducible",
title: "D2NN Ver11r Reproducible Archive",
url: "https://github.com/Cyh29hao/D2NN-ViT-KD-Ver11r-Reproducible",
category: "ai-hardware",
featured: 2,
image: "./assets/previews/d2nn-ver11r.png",
tags: ["PyTorch", "Jupyter", "D2NN", "ViT", "Knowledge Distillation"],
overview: {
zh: "把前几版 diffraction backbone 实验重新整理成一个能公开运行的 Ver11r 版本,内容包括 D2NN、ViT 师生蒸馏、环境配置和 CPU 检查。",
en: "A cleaned-up public version of several diffraction-backbone experiments, covering D2NN, ViT teacher-student distillation, environment setup, and CPU checks.",
},
technical: {
zh: ["保留 notebooks、Python smoke test、CPU diagnostic、requirements 和运行指南。", "数据集、ViT 权重和 checkpoint 通过 OPEN_SOURCE_BOUNDARY 明确排除。", "VERSION 与 REPORT 说明版本来源、验证范围和本地资源要求。"],
en: ["Includes notebooks, a Python smoke test, CPU diagnostics, requirements, and run guidance.", "Datasets, ViT weights, and checkpoints are explicitly excluded through OPEN_SOURCE_BOUNDARY.", "VERSION and REPORT document provenance, validation scope, and required local resources."],
},
evidence: {
zh: "仓库包含 smoke test、CPU 诊断结果、版本记录和开源边界说明。",
en: "The repository includes a smoke test, CPU diagnostic result, version record, and open-source boundary documentation.",
},
},
{
name: "The-SJTU-Intelligent-Optoelectronic-Computing-Lab",
title: "SJTU Intelligent Optoelectronic Computing Lab Website",
url: "https://github.com/Cyh29hao/The-SJTU-Intelligent-Optoelectronic-Computing-Lab",
liveUrl: "https://opticompt-lab.sjtu.edu.cn",
category: "ai-hardware",
featured: 3,
image: "./assets/previews/lab-site.png",
tags: ["Website", "Deployment", "Responsive UI", "Content Structure"],
overview: {
zh: "上海交通大学光电智能计算实验室的公开网站,用来发布研究方向、论文、新闻和成员信息,现已正式上线。",
en: "The public website for SJTU’s Intelligent Optoelectronic Computing Lab, with research directions, publications, news, and member information. It is live now.",
},
technical: {
zh: ["首页围绕光电 AI 芯片、光子 AI 加速和光子芯片设计组织信息。", "页面包含导航、语言入口、论文、成员和新闻模块。", "重点在稳定访问、响应式信息结构和后续维护。"],
en: ["The home page structures content around optoelectronic AI chips, photonic AI acceleration, and photonic chip design.", "The interface includes navigation, language access, publications, members, and news.", "The delivery emphasizes stable access, responsive information architecture, and maintainability."],
},
evidence: {
zh: "线上站点可直接访问,公开仓库保留页面源码和部署材料。",
en: "The live site is directly accessible, and the public repository retains source and deployment material.",
},
},
{
name: "Static-Online-Minesweeper-Game",
title: "Static Online Minesweeper Game",
url: "https://github.com/Cyh29hao/Static-Online-Minesweeper-Game",
liveUrl: "https://cyh29hao.github.io/Static-Online-Minesweeper-Game/",
category: "software",
featured: 4,
image: "./assets/previews/minesweeper.png",
tags: ["HTML", "JavaScript", "Game Logic", "GitHub Pages", "Supabase"],
overview: {
zh: "一个不用安装就能玩的在线扫雷。玩家根据数字线索标记雷和安全格,核心玩法不依赖后端。",
en: "A Minesweeper game that runs directly in the browser. Players mark mines and safe cells from the number clues, and the core game does not need a backend.",
},
technical: {
zh: ["前端处理棋盘渲染、标记、计时、难度和题面切换。", "整理 45 个 puzzle,并保留 puzzle_audit.py 检查题面性质。", "Supabase 排行榜可选,localStorage 提供本地兜底。"],
en: ["The frontend handles board rendering, marking, timing, difficulty, and puzzle selection.", "The repository contains 45 puzzles and puzzle_audit.py for puzzle checks.", "Supabase leaderboards are optional, with localStorage as a local fallback."],
},
evidence: {
zh: "仓库包含页面代码、题库、审计脚本、在线部署和排行榜降级方案。",
en: "The repository includes page code, puzzles, an audit script, live deployment, and leaderboard fallback behavior.",
},
},
{
name: "shuixian-skill",
title: "Shuixian Skill / 水仙 Skill",
url: "https://github.com/Cyh29hao/shuixian-skill",
category: "software",
tags: ["Python", "Codex Skill", "Importers", "Context Management"],
overview: {
zh: "围绕 Codex skill 的工具化项目,把结构化文本、聊天记录或素材整理成可配置的 self-mirror companion skill。",
en: "A Codex skill tooling project that turns structured text, chat exports, or source material into a configurable self-mirror companion skill.",
},
technical: {
zh: ["导入器、画像分析、prompt 生成、隐私边界和版本回滚落在可检查的文件结构中。", "使用 AGENTS.md、上下文继承和 Markdown 资料组织保持长任务连续性。"],
en: ["Importers, profile analysis, prompt generation, privacy boundaries, and rollback are represented in reviewable files.", "AGENTS.md, inherited context, and Markdown sources maintain continuity across longer tasks."],
},
evidence: { zh: "公开仓库包含 skill 结构、脚本、模板、资产图和版本说明。", en: "The public repository includes the skill structure, scripts, templates, visual assets, and version notes." },
},
{
name: "Engineering-Modeling-and-Simulation-MATLAB-Cases",
title: "Engineering Modeling and Simulation MATLAB Cases",
url: "https://github.com/Cyh29hao/Engineering-Modeling-and-Simulation-MATLAB-Cases",
category: "modeling",
tags: ["MATLAB", "Spline", "Optimization", "Monte Carlo", "MTTF"],
overview: {
zh: "工程问题建模与仿真课程的两个公开案例:测量点优化和系统可靠性仿真。",
en: "Two public engineering modeling and simulation cases covering measurement-point optimization and system reliability simulation.",
},
technical: {
zh: ["Case 1 使用三次样条、遗传搜索和局部优化寻找测量点方案。", "Case 2 使用 Monte Carlo 估计可靠度和 MTTF。"],
en: ["Case 1 uses cubic splines, genetic search, and local optimization for measurement-point placement.", "Case 2 uses Monte Carlo simulation to estimate reliability and MTTF."],
},
evidence: { zh: "仓库包含 MATLAB 脚本、数据、结果图和运行说明。", en: "The repository includes MATLAB scripts, data, result figures, and run instructions." },
},
{
name: "smart-electronic-scale-based-on-Energia",
title: "Smart Electronic Scale Based on Energia",
url: "https://github.com/Cyh29hao/smart-electronic-scale-based-on-Energia",
category: "embedded",
tags: ["Energia", "HX711", "TimerA", "Calibration", "Filtering"],
overview: { zh: "Energia + HX711 智能电子秤原型,覆盖称重、校准、去皮、单位切换、滤波和串口输出。", en: "An Energia and HX711 smart-scale prototype covering measurement, calibration, tare, unit switching, filtering, and serial output." },
technical: { zh: ["HX711 读数与标定参数结合,处理称重稳定性、去皮和单位转换。", "按钮、计时器和串口日志用于交互与调试。"], en: ["HX711 readings combine with calibration parameters for stability, tare, and unit conversion.", "Buttons, timers, and serial logs support interaction and debugging."] },
evidence: { zh: "仓库包含 Energia 代码、注释和 README。", en: "The repository includes Energia code, comments, and a README." },
},
{
name: "Simple-water-dispenser-based-on-Energia",
title: "Simple Water Dispenser Based on Energia",
url: "https://github.com/Cyh29hao/Simple-water-dispenser-based-on-Energia",
category: "embedded",
tags: ["Energia", "Finite State Machine", "TimerA", "Button", "LED"],
overview: { zh: "简易饮水机状态机项目,用按键和 LED 模拟童锁、解锁和放水流程。", en: "A finite-state-machine water-dispenser project using buttons and LEDs to simulate child lock, unlock, and dispensing flows." },
technical: { zh: ["有限状态机拆分设备流程,状态转移与按键事件同步说明。", "TimerA 与 LED 显示用于表现流程阶段。"], en: ["A finite state machine separates device flows and documents transitions with button events.", "TimerA and LED output represent process stages."] },
evidence: { zh: "仓库包含代码、状态图、流程图和双语说明。", en: "The repository contains code, state and flow diagrams, and bilingual documentation." },
},
{
name: "ARM-based-temperature-sensing-and-transmission-project",
title: "ARM Temperature Sensing and Transmission",
url: "https://github.com/Cyh29hao/ARM-based-temperature-sensing-and-transmission-project",
category: "embedded",
tags: ["C", "ARM", "Keil", "LM75BD", "ADC", "TM1638"],
overview: { zh: "ARM 温度采集与传输综合实验,包含双板 Keil 工程、LM75BD、ADC、显示和编码传输。", en: "An ARM temperature acquisition and transmission experiment with dual-board Keil projects, LM75BD, ADC, display, and encoded communication." },
technical: { zh: ["温度和电压数据经过采集、显示、编码、传输和复核。", "仓库保留硬件实验和调试过程材料。"], en: ["Temperature and voltage data move through acquisition, display, encoding, transmission, and verification.", "The repository retains hardware experiment and debugging material."] },
evidence: { zh: "仓库包含 Keil 工程、代码和实验报告材料。", en: "The repository includes Keil projects, code, and experiment report material." },
},
{
name: "Signals-and-Systems-Course-Project",
title: "Signals and Systems Course Project",
url: "https://github.com/Cyh29hao/Signals-and-Systems-Course-Project",
category: "modeling",
tags: ["Python", "FFT", "Audio", "Tkinter", "Filtering"],
overview: { zh: "围绕音频傅里叶变换、频谱可视化、低通滤波和简易人声消除整理的课程项目公开版。", en: "A public course-project edition covering audio Fourier transforms, spectrum visualization, low-pass filtering, and simple vocal removal." },
technical: { zh: ["Python/Tkinter 提供交互界面,FFT 与滤波逻辑用于可视化和音频处理。", "公开版排除课程 PDF、原始歌曲、exe 和含学号报告。"], en: ["Python and Tkinter provide the interface, with FFT and filtering for visualization and audio processing.", "The public edition excludes course PDFs, original songs, executables, and reports containing student identifiers."] },
evidence: { zh: "仓库包含可运行代码、demo 音频和公开边界说明。", en: "The repository includes runnable code, demo audio, and a public-scope statement." },
},
{
name: "AMS-Assistant-for-Maritime-Service",
title: "AMS Assistant for Maritime Service",
url: "https://github.com/Cyh29hao/AMS-Assistant-for-Maritime-Service",
category: "software",
tags: ["Python", "Desktop Tool", "Excel", "Word", "Workflow"],
overview: { zh: "面向航运服务流程的本地桌面自动化工作台,覆盖文档、通关、船期、表格同步和业务包机制。", en: "A local desktop automation workbench for maritime-service workflows, covering documents, customs, schedules, spreadsheet sync, and business packages." },
technical: { zh: ["文档生成、表格同步和统一入口减少重复办公操作。", "公开代码与私密业务包分离。"], en: ["Document generation, spreadsheet synchronization, and a unified entry point reduce repetitive office work.", "Public code is separated from private business packages."] },
evidence: { zh: "仓库包含桌面工具源码、说明和公开边界。", en: "The repository includes desktop-tool source, documentation, and public-scope boundaries." },
},
{
name: "Mail-Sorting-Assistant-for-Maritime-Service",
title: "Mail Sorting Assistant for Maritime Service",
url: "https://github.com/Cyh29hao/Mail-Sorting-Assistant-for-Maritime-Service",
category: "software",
tags: ["Python", "FastAPI", "SQLite", "IMAP", "Web UI"],
overview: { zh: "航运邮件同步、抽取和查询 MVP,使用 FastAPI、SQLite、IMAP 增量同步和网页界面。", en: "A maritime-mail synchronization, extraction, and search MVP using FastAPI, SQLite, incremental IMAP sync, and a web interface." },
technical: { zh: ["IMAP 同步把邮件写入本地数据库,规则抽取将业务信息变成可查询字段。", "Web UI 用于搜索和查看记录。"], en: ["IMAP synchronization writes mail to a local database, and rule-based extraction turns business information into queryable fields.", "The web interface supports search and record inspection."] },
evidence: { zh: "仓库包含服务端、数据库和界面代码。", en: "The repository includes server, database, and interface code." },
},
{
name: "Automatic-Sync-Assistant-for-Excel-Files",
title: "Automatic Sync Assistant for Excel Files",
url: "https://github.com/Cyh29hao/Automatic-Sync-Assistant-for-Excel-Files",
category: "software",
tags: ["Python", "Excel", "GUI", "PyInstaller", "File Lock Retry"],
overview: { zh: "Excel Sync Manager V1,用于源表监听、列同步、锁文件重试和打包交付。", en: "Excel Sync Manager V1 monitors source workbooks, synchronizes columns, retries file locks, and supports packaged delivery." },
technical: { zh: ["处理 Excel/WPS 锁文件、重试和列映射。", "从脚本推进到 GUI 和 PyInstaller 打包。"], en: ["Handles Excel/WPS lock files, retries, and column mappings.", "Progressed from a script to a GUI and PyInstaller package."] },
evidence: { zh: "仓库包含 GUI 代码、打包说明和版本记录。", en: "The repository includes GUI code, packaging instructions, and version notes." },
},
{
name: "clash-royale-card-balance-statistics",
title: "Clash Royale Card Balance Statistics",
url: "https://github.com/Cyh29hao/clash-royale-card-balance-statistics",
category: "modeling",
tags: ["Python", "Selenium", "Statistics", "LaTeX", "Visualization"],
overview: { zh: "基于 RoyaleAPI 的概率统计课程项目,报告 168 张卡牌和 39,429,252 加权 matchup 样本。", en: "A probability and statistics course project based on RoyaleAPI, reporting 168 cards and 39,429,252 weighted matchup samples." },
technical: { zh: ["数据采集、清洗、加权统计、可视化和 LaTeX/PDF 报告形成完整流水线。", "重点在数据处理和统计表达。"], en: ["Collection, cleaning, weighted statistics, visualization, and LaTeX/PDF reporting form an end-to-end pipeline.", "The project focuses on data processing and statistical communication."] },
evidence: { zh: "仓库包含脚本、统计结果和报告生成材料。", en: "The repository includes scripts, statistical results, and report-generation material." },
},
{
name: "D2NN-ViT-KD-final",
title: "D2NN-ViT-KD Final Archive",
url: "https://github.com/Cyh29hao/D2NN-ViT-KD-final",
category: "ai-hardware",
tags: ["PyTorch", "Jupyter", "D2NN", "ViT"],
overview: { zh: "较早的 D2NN / ViT / KD 复现打包版本,作为 Ver11r 之前的阶段归档。", en: "An earlier packaged D2NN, ViT, and knowledge-distillation reproduction retained as a stage archive before Ver11r." },
technical: { zh: ["包含 notebooks、requirements、路径说明、缓存和报告。", "展示定位低于 Ver11r,主要记录迭代过程。"], en: ["Includes notebooks, requirements, path notes, caches, and reports.", "It ranks below Ver11r and primarily records the iteration path."] },
evidence: { zh: "仓库保留早期归档结构和说明。", en: "The repository retains the earlier archive structure and notes." },
},
{
name: "My-own-D2NN-Research-Practices",
title: "My Own D2NN Research Practices",
url: "https://github.com/Cyh29hao/My-own-D2NN-Research-Practices",
category: "ai-hardware",
tags: ["Jupyter", "D2NN", "CIFAR-10", "ViT"],
overview: { zh: "个人 D2NN 研究练习空间,包含 D2NN/CIFAR-10、ViT、KD notebook 和设计笔记。", en: "A personal D2NN research-practice space containing D2NN/CIFAR-10, ViT, knowledge-distillation notebooks, and design notes." },
technical: { zh: ["记录探索、对比、试错和整理过程。", "补充硬件 AI 与光学神经网络方向的学习轨迹。"], en: ["Records exploration, comparison, failed attempts, and curation.", "Supplements the learning path in hardware AI and optical neural networks."] },
evidence: { zh: "仓库包含 notebook 和练习材料。", en: "The repository includes notebooks and practice material." },
},
{
name: "SJTUCTF-2026-Experience",
title: "SJTUCTF 2026 Experience",
url: "https://github.com/Cyh29hao/SJTUCTF-2026-Experience",
category: "other",
tags: ["CTF", "Web", "Crypto", "Reverse", "Codex"],
overview: { zh: "SJTUCTF 2026 线上赛与线下赛经历归档;两场均获二等奖,并明确记录 AI 辅助边界。", en: "An archive of SJTUCTF 2026 online and offline participation; both received second prize, with explicit AI-assistance boundaries." },
technical: { zh: ["write-up 作为经历主线,并标注 AI 辅助范围。", "线下赛补充安全入门、Crypto/RSA 和独立解题经历。"], en: ["Write-ups form the main record and state the scope of AI assistance.", "The offline event adds security fundamentals, Crypto/RSA, and independent problem solving."] },
evidence: { zh: "仓库整理比赛经历、write-up 和成绩说明。", en: "The repository organizes competition records, write-ups, and result notes." },
},
{
name: "Public-Writing-and-AI-Notes",
title: "Public Writing and AI Notes",
url: "https://github.com/Cyh29hao/Public-Writing-and-AI-Notes",
category: "other",
tags: ["Writing", "AI Reflection", "D2NN Explainer"],
overview: { zh: "公开写作和 AI 使用反思索引,包括对工具误差的复盘和 D2NN 科普计划。", en: "An index of public writing and reflections on AI use, including tool-error reviews and a planned D2NN explainer." },
technical: { zh: ["补充技术表达和复盘习惯,不与工程项目同权。", "记录技术解释、工具边界和协作流程。"], en: ["Supplements technical communication and review habits without carrying the same weight as engineering projects.", "Records technical explanation, tool boundaries, and collaboration workflows."] },
evidence: { zh: "仓库索引公开文章和计划主题。", en: "The repository indexes public articles and planned topics." },
},
{
name: "Interesting-algorithms-in-my-OI-journey",
title: "Interesting Algorithms in My OI Journey",
url: "https://github.com/Cyh29hao/Interesting-algorithms-in-my-OI-journey",
category: "other",
tags: ["C++", "Algorithms", "OI"],
overview: { zh: "高中 OI 时期的 C++ 算法旧档,写于 AI coding 之前。", en: "A C++ algorithm archive from high-school OI training, written before AI-assisted coding." },
technical: { zh: ["保留早期算法训练和调试习惯。", "作为补充经历,权重低于当前工程项目。"], en: ["Retains evidence of early algorithm training and debugging habits.", "Presented as background rather than current engineering work."] },
evidence: { zh: "仓库包含 C++ 算法练习旧档。", en: "The repository contains archived C++ algorithm exercises." },
},
];
const featuredContainer = document.querySelector("#featuredProjects");
const projectList = document.querySelector("#projectList");
const projectCount = document.querySelector("#projectCount");
const searchInput = document.querySelector("#projectSearch");
const resetSearch = document.querySelector("#resetSearch");
const toggleArchive = document.querySelector("#toggleArchive");
const shuffleProject = document.querySelector("#shuffleProject");
const filterButtons = [...document.querySelectorAll(".filter-button")];
const languageButtons = [...document.querySelectorAll("[data-language]")];
const themeToggle = document.querySelector("#themeToggle");
const themeMeta = document.querySelector('meta[name="theme-color"]');
let activeLanguage = localStorage.getItem("portfolio-language") || (navigator.language.toLowerCase().startsWith("zh") ? "zh" : "en");
let activeFilter = "all";
let expandedProject = "";
let activeFeatured = projects.find((project) => project.featured === 1).name;
let archiveExpanded = false;
const archiveLimit = 7;
function t(key) {
return translations[activeLanguage][key];
}
function localized(value) {
return value[activeLanguage];
}
function tagLine(tags) {
return `<p class="tech-line">${tags.join(" · ")}</p>`;
}
function projectActions(project) {
return `
<div class="project-actions">
<a class="project-link" href="${project.url}" target="_blank" rel="noreferrer">${t("repository")} <span aria-hidden="true">↗</span></a>
${project.liveUrl ? `<a class="project-link" href="${project.liveUrl}" target="_blank" rel="noreferrer">${t("live")} <span aria-hidden="true">↗</span></a>` : ""}
</div>
`;
}
function renderFeaturedProjects() {
const featured = projects.filter((project) => project.featured).sort((a, b) => a.featured - b.featured);
const activeProject = featured.find((project) => project.name === activeFeatured) || featured[0];
const activeIndex = featured.indexOf(activeProject);
activeFeatured = activeProject.name;
featuredContainer.innerHTML = `
<div class="project-spotlight">
<div class="spotlight-tabs" role="tablist" aria-label="${t("featuredLabel")}">
${featured
.map(
(project, index) => `
<button
id="featured-tab-${index}"
class="spotlight-tab"
type="button"
role="tab"
data-featured-project="${project.name}"
data-category="${project.category}"
aria-controls="featuredPanel"
aria-selected="${project.name === activeProject.name}"
tabindex="${project.name === activeProject.name ? "0" : "-1"}"
>
<span>${localized(categoryNames[project.category])}</span>
<strong>${project.title}</strong>
</button>
`,
)
.join("")}
</div>
<article
id="featuredPanel"
class="spotlight-panel"
role="tabpanel"
aria-labelledby="featured-tab-${activeIndex}"
data-category="${activeProject.category}"
>
<a class="project-media spotlight-media" href="${activeProject.liveUrl || activeProject.url}" target="_blank" rel="noreferrer" aria-label="${activeProject.title}">
<img src="${activeProject.image}" alt="${activeProject.title} ${t("projectPreview")}" width="1280" height="720" decoding="async" />
</a>
<div class="project-copy spotlight-copy">
<span class="project-kind">${localized(categoryNames[activeProject.category])}</span>
<h3>${activeProject.title}</h3>
<p class="project-summary">${localized(activeProject.overview)}</p>
${tagLine(activeProject.tags)}
${projectActions(activeProject)}
</div>
</article>
</div>
`;
}
function projectMatches(project, query) {
if (activeFilter !== "all" && project.category !== activeFilter) return false;
if (!query) return true;
const searchable = [
project.name,
project.title,
...project.tags,
project.overview.zh,
project.overview.en,
project.evidence.zh,
project.evidence.en,
...project.technical.zh,
...project.technical.en,
]
.join(" ")
.toLowerCase();
return searchable.includes(query.toLowerCase());
}
function renderProjectList() {
const query = searchInput.value.trim();
const matched = projects.filter((project) => projectMatches(project, query));
const isCompact = activeFilter === "all" && !query && !archiveExpanded;
const shown = isCompact ? matched.slice(0, archiveLimit) : matched;
if (!shown.length) {
projectList.innerHTML = `<p class="empty-state">${t("emptyState")}</p>`;
} else {
projectList.innerHTML = shown
.map((project) => {
const isExpanded = expandedProject === project.name;
return `
<article class="project-row repo-row" data-project-row data-project-name="${project.name}">
<span class="project-row-category">${localized(categoryNames[project.category])}</span>
<div class="project-row-main">
<h3>${project.title}</h3>
<p class="project-row-summary">${localized(project.overview)}</p>
</div>
<div class="row-actions">
<button class="detail-toggle" type="button" data-project="${project.name}" data-project-detail aria-expanded="${isExpanded}">${isExpanded ? t("hideNotes") : t("notes")}</button>
<a class="project-link" href="${project.url}" target="_blank" rel="noreferrer">${t("repository")} <span aria-hidden="true">↗</span></a>
${project.liveUrl ? `<a class="project-link" href="${project.liveUrl}" target="_blank" rel="noreferrer">${t("live")} <span aria-hidden="true">↗</span></a>` : ""}
</div>
${
isExpanded
? `<div class="project-detail">
<h4>${t("technicalNotes")}</h4>
<ul>${localized(project.technical).map((item) => `<li>${item}</li>`).join("")}</ul>
</div>`
: ""
}
</article>
`;
})
.join("");
}
projectCount.textContent = t("count")(shown.length, projects.length);
const canToggleArchive = activeFilter === "all" && !query && matched.length > archiveLimit;
toggleArchive.hidden = !canToggleArchive;
toggleArchive.setAttribute("aria-expanded", String(archiveExpanded));
toggleArchive.textContent = archiveExpanded ? t("showLess") : t("showMore")(matched.length - archiveLimit);
}
function selectFeaturedProject(projectName, shouldFocus = false) {
activeFeatured = projectName;
renderFeaturedProjects();
if (shouldFocus) {
featuredContainer.querySelector(`[data-featured-project="${projectName}"]`)?.focus();
}
}
function applyLanguage(language) {
activeLanguage = language;
localStorage.setItem("portfolio-language", language);
document.documentElement.lang = language === "zh" ? "zh-CN" : "en";
document.title = t("pageTitle");
document.querySelector('meta[name="description"]').setAttribute("content", t("pageDescription"));
document.querySelectorAll("[data-i18n]").forEach((element) => {
element.textContent = t(element.dataset.i18n);
});
document.querySelectorAll("[data-i18n-aria]").forEach((element) => {
element.setAttribute("aria-label", t(element.dataset.i18nAria));
});
document.querySelectorAll("[data-i18n-placeholder]").forEach((element) => {
element.setAttribute("placeholder", t(element.dataset.i18nPlaceholder));
});
document.querySelectorAll("[data-i18n-alt]").forEach((element) => {
element.setAttribute("alt", t(element.dataset.i18nAlt));
});
languageButtons.forEach((button) => {
button.setAttribute("aria-pressed", String(button.dataset.language === language));
});
updateThemeLabel();
renderFeaturedProjects();
renderProjectList();
}
function updateThemeLabel() {
const nextTheme = document.documentElement.dataset.theme === "dark" ? "light" : "dark";
themeToggle.setAttribute("aria-label", t(nextTheme === "dark" ? "themeToggleDark" : "themeToggleLight"));
themeMeta.setAttribute("content", document.documentElement.dataset.theme === "dark" ? "#171917" : "#f7f7f4");
}
function setTheme(theme) {
document.documentElement.dataset.theme = theme;
localStorage.setItem("portfolio-theme", theme);
updateThemeLabel();
}
languageButtons.forEach((button) => {
button.addEventListener("click", () => applyLanguage(button.dataset.language));
});
themeToggle.addEventListener("click", () => {
setTheme(document.documentElement.dataset.theme === "dark" ? "light" : "dark");
});
filterButtons.forEach((button) => {
button.addEventListener("click", () => {
activeFilter = button.dataset.filter;
archiveExpanded = false;
filterButtons.forEach((item) => item.classList.toggle("active", item === button));
expandedProject = "";
renderProjectList();
});
});
featuredContainer.addEventListener("click", (event) => {
const button = event.target.closest("[data-featured-project]");
if (!button) return;
selectFeaturedProject(button.dataset.featuredProject, true);
});
featuredContainer.addEventListener("keydown", (event) => {
const button = event.target.closest("[data-featured-project]");
if (!button) return;
const tabs = [...featuredContainer.querySelectorAll("[data-featured-project]")];
const currentIndex = tabs.indexOf(button);
let nextIndex;
if (event.key === "ArrowDown" || event.key === "ArrowRight") nextIndex = (currentIndex + 1) % tabs.length;
if (event.key === "ArrowUp" || event.key === "ArrowLeft") nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
if (event.key === "Home") nextIndex = 0;
if (event.key === "End") nextIndex = tabs.length - 1;
if (nextIndex === undefined) return;
event.preventDefault();
selectFeaturedProject(tabs[nextIndex].dataset.featuredProject, true);
});
projectList.addEventListener("click", (event) => {
const button = event.target.closest("[data-project]");
if (!button) return;
expandedProject = expandedProject === button.dataset.project ? "" : button.dataset.project;
renderProjectList();
const updatedButton = projectList.querySelector(`[data-project="${button.dataset.project}"]`);
updatedButton?.focus();
});
searchInput.addEventListener("input", () => {
archiveExpanded = false;
expandedProject = "";
renderProjectList();
});
resetSearch.addEventListener("click", () => {
activeFilter = "all";
archiveExpanded = false;
expandedProject = "";
searchInput.value = "";
filterButtons.forEach((button) => button.classList.toggle("active", button.dataset.filter === "all"));
renderProjectList();
searchInput.focus();
});
toggleArchive.addEventListener("click", () => {
archiveExpanded = !archiveExpanded;
expandedProject = "";
renderProjectList();
toggleArchive.focus();
});
shuffleProject.addEventListener("click", () => {
const pickedProject = projects[Math.floor(Math.random() * projects.length)];
activeFilter = "all";
archiveExpanded = true;
expandedProject = pickedProject.name;
searchInput.value = "";
filterButtons.forEach((button) => button.classList.toggle("active", button.dataset.filter === "all"));
renderProjectList();
requestAnimationFrame(() => {
const pickedRow = projectList.querySelector(`[data-project-name="${pickedProject.name}"]`);
pickedRow?.scrollIntoView({ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth", block: "center" });
pickedRow?.querySelector("[data-project-detail]")?.focus({ preventScroll: true });
});
});
applyLanguage(activeLanguage);