PageSourceSearch

https://xavihart.github.io/

html xavihart.github.io collected 2026-10-03 10:11:16 UTC 59,955 bytes, 1,336 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en" data-theme="dark">
3<head>
4  <meta charset="utf-8" />
5  <meta name="viewport" content="width=device-width, initial-scale=1" />
6  <title>Haotian Xue</title>
7  <meta name="description" content="Haotian Xue — ML PhD @ Georgia Tech" />
8  <link rel="shortcut icon" href="static/image/logos/georgia-tech-yellow-jackets-ncaa-logo-sticker-5145-756891.png" />
9  <style>
10    :root {
11      --bg:      #0d1117;
12      --surface: #161b22;
13      --border:  #30363d;
14      --text:    #e6edf3;
15      --muted:   #8b949e;
16      --green:   #39d353;
17      --cyan:    #58a6ff;
18      --accent:  #58a6ff;
19      --purple:  #bc8cff;
20      --red:     #ff7b72;
21      --yellow:  #e3b341;
22      --radius:  10px;
23      --font:    'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;
24    }
25    html[data-theme="light"] {
26      --bg:      #f6f8fa;
27      --surface: #ffffff;
28      --border:  #d0d7de;
29      --text:    #1f2328;
30      --muted:   #636c76;
31      --green:   #1a7f37;
32      --cyan:    #0969da;
33      --accent:  #0969da;
34      --purple:  #8250df;
35      --red:     #cf222e;
36      --yellow:  #9a6700;
37    }
38
39    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
40    html, body {
41      background: var(--bg);
42      color: var(--text);
43      font-family: var(--font);
44      font-size: 14px;
45      line-height: 1.6;
46      min-height: 100vh;
47    }
48    a { color: var(--accent); text-decoration: none; }
49    a:hover { text-decoration: underline; }
50
51    .page {
52      max-width: 1040px;
53      margin: 0 auto;
54      padding: 40px 32px 80px;
55    }
56
57    /* ── top bar ─────────────────────────────────────── */
58    .topbar {
59      display: flex;
60      justify-content: space-between;
61      align-items: center;
62      margin-bottom: 32px;
63    }
64    .topbar .path { font-size: 12px; letter-spacing: .03em; }
65    .topbar .path .slash { color: var(--muted); opacity: .5; }
66    .topbar .path .seg   { color: var(--green); }
67
68    .top-right { display: flex; gap: 8px; align-items: center; }
69
70    .icon-btn {
71      background: none;
72      border: 1px solid var(--border);
73      color: var(--muted);
74      font-family: var(--font);
75      font-size: 11px;
76      padding: 3px 9px;
77      border-radius: 6px;
78      cursor: pointer;
79      transition: border-color .15s, color .15s;
80      text-decoration: none;
81      display: inline-block;
82    }
83    .icon-btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
84
85    /* ── section label ───────────────────────────────── */
86    .sec-label {
87      font-size: 11px;
88      color: var(--muted);
89      letter-spacing: .08em;
90      margin-bottom: 10px;
91      display: flex;
92      align-items: center;
93      gap: 8px;
94    }
95    .sec-label::before { content: '##'; color: var(--green); font-weight: 700; }
96    .sec-label::after  { content: ''; flex: 1; height: 1px; background: var(--border); }
97
98    /* ── hero ────────────────────────────────────────── */
99    .hero {
100      display: grid;
101      grid-template-columns: 148px 1fr;
102      gap: 28px;
103      align-items: flex-start;
104      margin-bottom: 40px;
105    }
106
107    .portrait-wrap {
108      position: relative;
109      width: 148px;
110      height: 148px;
111      cursor: pointer;
112      user-select: none;
113      flex-shrink: 0;
114    }
115    .portrait-wrap img {
116      position: absolute;
117      inset: 0;
118      width: 100%;
119      height: 100%;
120      object-fit: cover;
121      border-radius: 10px;
122      transition: opacity .35s ease;
123      border: 1px solid var(--border);
124    }
125    .portrait-wrap .main  { opacity: 1; z-index: 1; }
126    .portrait-wrap .cat   { opacity: 0; z-index: 2; }
127    .portrait-wrap.revealed .main { opacity: 0; }
128    .portrait-wrap.revealed .cat  { opacity: 1; }
129    .cat-badge {
130      position: absolute;
131      bottom: -10px; right: -10px;
132      z-index: 10;
133      background: var(--surface);
134      border: 1px solid var(--border);
135      border-radius: 6px;
136      padding: 2px 7px;
137      font-size: 10px;
138      color: var(--muted);
139      cursor: pointer;
140      white-space: nowrap;
141      transition: border-color .15s, color .15s;
142    }
143    .cat-badge:hover { border-color: var(--green); color: var(--green); }
144
145    .cat-hint {
146      position: absolute;
147      top: calc(100% + 8px); left: 50%;
148      transform: translateX(-50%);
149      display: flex; flex-direction: column; align-items: center; gap: 2px;
150      pointer-events: none;
151      animation: hintBob 1.6s ease-in-out infinite;
152      transition: opacity .4s ease;
153      white-space: nowrap;
154      z-index: 50;
155    }
156    .cat-hint.hidden { opacity: 0; pointer-events: none; }
157    .cat-hint-arrow { color: var(--green); font-size: 13px; line-height: 1; }
158    .cat-hint-label {
159      font-size: 10px; color: var(--green);
160      background: var(--surface); border: 1px solid var(--border);
161      border-radius: 5px; padding: 2px 7px;
162    }
163    @keyframes hintBob {
164      0%, 100% { transform: translateX(-50%) translateY(0); }
165      50%       { transform: translateX(-50%) translateY(-4px); }
166    }
167
168    .bio h1 { font-size: 22px; font-weight: 700; letter-spacing: -.01em; margin-bottom: 2px; }
169    .bio .pron { font-size: 11px; color: var(--muted); margin-bottom: 10px; }
170    .bio .tagline { font-size: 13px; color: var(--muted); margin-bottom: 14px; line-height: 1.5; }
171    .bio .tagline a { color: var(--accent); }
172
173    .links-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
174    .links-row a {
175      display: inline-flex; align-items: center; gap: 5px;
176      font-size: 12px; color: var(--muted);
177      border: 1px solid var(--border); border-radius: 6px; padding: 3px 10px;
178      transition: border-color .15s, color .15s;
179    }
180    .links-row a:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
181    .email { font-size: 12px; color: var(--muted); }
182    .email span { color: var(--green); }
183
184    /* ── terminal window ─────────────────────────────── */
185    .term {
186      background: var(--surface);
187      border: 1px solid var(--border);
188      border-radius: var(--radius);
189      overflow: hidden;
190      margin-bottom: 32px;
191    }
192    .term-bar {
193      background: color-mix(in oklab, var(--border) 60%, var(--surface));
194      padding: 7px 12px;
195      display: flex;
196      align-items: center;
197      gap: 10px;
198    }
199    .term-dots { display: flex; gap: 6px; }
200    .term-dots span { width: 10px; height: 10px; border-radius: 50%; }
201    .term-dots .r { background: #ff5f57; }
202    .term-dots .y { background: #febc2e; }
203    .term-dots .g { background: #28c840; }
204    .term-title { flex: 1; text-align: center; font-size: 11px; color: var(--muted); letter-spacing: .05em; }
205
206    .term-body { padding: 14px 18px; font-size: 13px; line-height: 1.8; }
207    .prompt   { color: var(--green); }
208    .cmd      { color: var(--yellow); }
209    .key      { color: var(--cyan); }
210    .val      { color: var(--muted); }
211    .cursor   {
212      display: inline-block; width: 8px; height: 14px;
213      background: var(--green); vertical-align: text-bottom;
214      animation: blink 1.1s step-end infinite;
215    }
216    @keyframes blink { 50% { opacity: 0; } }
217
218    /* ── news log ────────────────────────────────────── */
219    .news-log {
220      list-style: none;
221      font-size: 12px;
222      line-height: 1.7;
223      overflow-y: auto;
224      max-height: 210px;
225      padding: 12px 18px;
226    }
227    .news-log::-webkit-scrollbar { width: 4px; }
228    .news-log::-webkit-scrollbar-track { background: transparent; }
229    .news-log::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
230
231    .news-log li { display: flex; gap: 10px; padding: 3px 0; border-bottom: 1px dashed color-mix(in oklab, var(--border) 50%, transparent); }
232    .news-log li:last-child { border-bottom: none; }
233    .news-log .ts { color: var(--green); flex-shrink: 0; min-width: 60px; }
234    .news-log .msg { color: var(--muted); }
235    .news-log .msg a { color: var(--cyan); }
236    .news-log .msg strong { color: var(--text); }
237
238    /* ── publications ────────────────────────────────── */
239    .pub-list { list-style: none; }
240    .pub-list > li {
241      padding: 16px 0;
242      border-bottom: 1px solid var(--border);
243    }
244    .pub-list > li:last-child { border-bottom: none; }
245
246    .pub-item { display: block; }
247    /* interactive terminal input */
248    .term-input-row {
249      display: flex; align-items: center; gap: 6px;
250      border-top: 1px solid var(--border);
251      padding: 10px 18px;
252    }
253    .term-input-row .prompt { color: var(--green); flex-shrink: 0; }
254    .term-input {
255      flex: 1; background: none; border: none; outline: none;
256      color: var(--text); font-family: var(--font); font-size: 13px;
257      caret-color: var(--green);
258    }
259    .term-output-area {
260      padding: 0 18px;
261      font-size: 13px;
262      line-height: 1.75;
263    }
264    .term-output-area .out-line {
265      display: flex; align-items: flex-start; gap: 8px;
266      padding: 2px 0;
267      animation: fadeIn .15s ease;
268    }
269    @keyframes fadeIn { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
270    .out-line .cc-logo {
271      height: 16px; width: 16px; border-radius: 3px; flex-shrink: 0; margin-top: 3px;
272      background: #d97706;
273      display: flex; align-items: center; justify-content: center;
274      font-size: 9px; font-weight: 900; color: #fff; letter-spacing: -.5px;
275    }
276    .out-line .out-text { color: var(--muted); }
277    .out-line .echo-cmd { color: var(--muted); opacity: .55; }
278
279    /* company logos */
280    .intern-logos {
281      display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
282      padding: 12px 18px 14px;
283    }
284    .intern-logos .co {
285      display: flex; align-items: center; gap: 6px;
286      font-size: 11px; color: var(--muted);
287      border: 1px solid var(--border); border-radius: 7px;
288      padding: 4px 10px 4px 6px;
289    }
290    .intern-logos .co img {
291      height: 18px; width: auto; object-fit: contain;
292      filter: grayscale(30%); opacity: .85;
293    }
294    html[data-theme="dark"] .intern-logos .co img { filter: grayscale(20%) brightness(1.1); }
295
296    .pub-meta { flex: 1; min-width: 0; }
297    .pub-title { font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.45; margin-bottom: 3px; }
298    .pub-title a { color: var(--text); }
299    .pub-title a:hover { color: var(--accent); text-decoration: none; }
300    .pub-authors { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
301    .pub-authors .me { color: var(--text); text-decoration: underline; }
302    .pub-venue {
303      font-size: 10px; font-weight: 700; letter-spacing: .04em;
304      display: inline-block; padding: 1px 7px; border-radius: 5px;
305      background: color-mix(in oklab, var(--accent) 12%, transparent);
306      color: var(--accent); margin-bottom: 5px;
307    }
308    .pub-venue.neurips { background: color-mix(in oklab, var(--purple) 12%, transparent); color: var(--purple); }
309    .pub-venue.iclr    { background: color-mix(in oklab, var(--green) 12%, transparent);  color: var(--green);  }
310    .pub-venue.emnlp   { background: color-mix(in oklab, var(--yellow) 12%, transparent); color: var(--yellow); }
311    .pub-venue.workshop{ background: color-mix(in oklab, var(--muted) 20%, transparent);  color: var(--muted);  }
312
313    .pub-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
314    .ptag {
315      font-size: 10px; font-weight: 500; padding: 1px 6px; border-radius: 4px;
316      border: 1px solid var(--border); background: transparent; color: var(--muted);
317    }
318
319    .pub-links { font-size: 12px; }
320    .pub-links a { color: var(--muted); margin-right: 10px; }
321
322    /* highlight */
323    .pub-item.highlight {
324      background: color-mix(in oklab, var(--yellow) 5%, var(--surface));
325      border-left: 3px solid var(--yellow);
326      border-radius: 6px;
327      padding: 10px 10px 10px 12px;
328      margin-left: -15px;
329    }
330    .highlight-badge {
331      font-size: 10px; font-weight: 600; letter-spacing: .04em;
332      color: var(--yellow); margin-left: 8px;
333      opacity: .85;
334    }
335    .pub-links a:hover { color: var(--accent); text-decoration: none; }
336
337    /* ── reviewer grid ───────────────────────────────── */
338    .review-grid {
339      display: flex;
340      gap: 8px;
341      flex-wrap: wrap;
342      padding: 14px 18px;
343    }
344    .review-grid .rv {
345      font-size: 11px;
346      padding: 3px 10px;
347      border: 1px solid var(--border);
348      border-radius: 6px;
349      color: var(--muted);
350    }
351
352    /* ── cat modal ───────────────────────────────────── */
353    .cat-modal {
354      position: fixed; inset: 0;
355      background: rgba(0,0,0,.65);
356      backdrop-filter: blur(4px);
357      display: flex; align-items: center; justify-content: center;
358      z-index: 100;
359      opacity: 0; pointer-events: none; transition: opacity .2s;
360    }
361    .cat-modal.open { opacity: 1; pointer-events: all; }
362    .cat-window {
363      background: var(--surface);
364      border: 1px solid var(--border);
365      border-radius: var(--radius);
366      width: min(400px, 90vw);
367      overflow: hidden;
368      transform: scale(.95); transition: transform .2s;
369    }
370    .cat-modal.open .cat-window { transform: scale(1); }
371    .cat-term-body { padding: 10px 16px 4px; font-size: 12px; color: var(--muted); }
372    .cat-term-body .typed { color: var(--text); }
373    .cat-img-wrap {
374      padding: 0 16px;
375      overflow: hidden;
376      max-height: 0; transition: max-height .4s ease, padding .3s ease;
377      position: relative;
378    }
379    .cat-img-wrap.show { max-height: 340px; padding: 0 16px 14px; }
380    .cat-img-wrap img {
381      width: 100%; border-radius: 8px; border: 1px solid var(--border); display: block;
382      transition: opacity .25s ease;
383    }
384    .cat-img-wrap img.fade { opacity: 0; }
385    .cat-footer {
386      padding: 8px 16px 12px;
387      font-size: 11px; color: var(--muted);
388      display: flex; justify-content: space-between; align-items: center;
389      gap: 8px;
390    }
391    .cat-nav { display: flex; align-items: center; gap: 6px; }
392    .cat-nav-btn {
393      background: none; border: 1px solid var(--border); color: var(--muted);
394      font-family: var(--font); font-size: 12px; padding: 2px 8px;
395      border-radius: 5px; cursor: pointer; line-height: 1.4;
396    }
397    .cat-nav-btn:hover { border-color: var(--accent); color: var(--accent); }
398    .cat-nav-btn:disabled { opacity: .3; cursor: default; }
399    .cat-counter { font-size: 11px; color: var(--muted); min-width: 32px; text-align: center; }
400    .cat-close {
401      background: none; border: 1px solid var(--border); color: var(--muted);
402      font-family: var(--font); font-size: 11px; padding: 3px 10px;
403      border-radius: 5px; cursor: pointer;
404    }
405    .cat-close:hover { border-color: var(--red); color: var(--red); }
406
407    /* ── aim trainer game ────────────────────────────── */
408    .game-modal {
409      position: fixed; inset: 0;
410      background: rgba(0,0,0,.75);
411      backdrop-filter: blur(6px);
412      display: flex; align-items: center; justify-content: center;
413      z-index: 200;
414      opacity: 0; pointer-events: none; transition: opacity .2s;
415    }
416    .game-modal.open { opacity: 1; pointer-events: all; }
417    .game-window {
418      background: var(--surface);
419      border: 1px solid var(--border);
420      border-radius: var(--radius);
421      overflow: hidden;
422      transform: scale(.95); transition: transform .2s;
423      display: flex; flex-direction: column;
424      width: min(900px, 96vw);
425    }
426    #gameCanvas { width: 100%; height: auto; }
427    .game-modal.open .game-window { transform: scale(1); }
428    #gameCanvas {
429      display: block;
430      cursor: none;
431      background: #0a0e14;
432    }
433    .game-footer {
434      padding: 8px 16px;
435      font-size: 11px; color: var(--muted);
436      display: flex; justify-content: space-between; align-items: center;
437      border-top: 1px solid var(--border);
438    }
439    .game-close {
440      background: none; border: 1px solid var(--border); color: var(--muted);
441      font-family: var(--font); font-size: 11px; padding: 3px 10px;
442      border-radius: 5px; cursor: pointer;
443    }
444    .game-close:hover { border-color: var(--red); color: var(--red); }
445
446    /* ── footer ──────────────────────────────────────── */
447    footer {
448      margin-top: 48px; padding-top: 16px;
449      border-top: 1px solid var(--border);
450      font-size: 11px; color: var(--muted);
451      display: flex; justify-content: space-between;
452    }
453
454    @media (max-width: 520px) {
455      .hero { grid-template-columns: 1fr; }
456      .portrait-wrap { width: 88px; height: 88px; }
457    }
458  </style>
459</head>
460<body>
461<div class="page">
462
463  <!-- top bar -->
464  <div class="topbar">
465    <div class="path">
466      <span class="slash">~/</span><span class="seg">haotian-xue</span>
467    </div>
468    <div class="top-right">
469      <button class="icon-btn" id="themeBtn">light</button>
470    </div>
471  </div>
472
473  <!-- ── HERO ─────────────────────────────────────────── -->
474  <div class="hero">
475    <div style="position:relative; width:148px; height:148px; flex-shrink:0; z-index:1; overflow:visible">
476      <div class="portrait-wrap" id="portrait" title="click to meet my cat">
477        <img class="main" src="static/image/photos/haotian_2026.png" alt="Haotian Xue" />
478        <img class="cat"  src="static/image/photos/pudding/pudding1.png" alt="Haotian's cat" />
479        <div class="cat-badge" id="catBadge">$ cat cat.jpg</div>
480      </div>
481      <div class="cat-hint" id="catHint">
482        <span class="cat-hint-arrow">↑</span>
483        <span class="cat-hint-label">meet my cat 🐱</span>
484      </div>
485    </div>
486    <div class="bio">
487      <h1>Haotian Xue</h1>
488      <div class="pron">/haʊtiˈæn ʃweɪ/ &nbsp;·&nbsp; 薛昊天</div>
489      <p class="tagline">
490        4th-year PhD · Machine Learning<br>
491        <a href="https://ml.gatech.edu/">Georgia Tech</a> &nbsp;·&nbsp;
492        advised by <a href="https://yongxin.ae.gatech.edu/">Yongxin Chen</a>
493      </p>
494      <div class="links-row">
495        <a href="https://scholar.google.com/citations?hl=en&user=wnweG9UAAAAJ">
496          <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M12 24a7 7 0 1 1 0-14 7 7 0 0 1 0 14zm0-24L0 9.5h4.5C5 14.7 8.1 19 12 19c.9 0 1.8-.2 2.5-.5V24l3.5-3.5V19c2.7-2 4.5-5.1 4.5-8.5H24z"/></svg>
497          Scholar
498        </a>
499        <a href="https://github.com/xavihart">
500          <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577v-2.165c-3.338.726-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.756-1.333-1.756-1.09-.745.083-.73.083-.73 1.205.085 1.84 1.237 1.84 1.237 1.07 1.835 2.807 1.305 3.492.998.108-.776.42-1.305.763-1.605-2.665-.3-5.467-1.332-5.467-5.93 0-1.31.468-2.38 1.235-3.22-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.3 1.23A11.5 11.5 0 0 1 12 6.8a11.5 11.5 0 0 1 3.007.404c2.29-1.552 3.297-1.23 3.297-1.23.653 1.652.242 2.873.12 3.176.77.84 1.232 1.91 1.232 3.22 0 4.61-2.807 5.625-5.48 5.92.43.37.823 1.102.823 2.222v3.293c0 .322.216.694.825.577C20.565 21.796 24 17.298 24 12 24 5.37 18.627 0 12 0z"/></svg>
501          GitHub
502        </a>
503        <a href="https://twitter.com/Haotianxue_GT">
504          <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
505          X
506        </a>
507        <a href="static/docs/cv.pdf">
508          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14,2 14,8 20,8"/></svg>
509          CV
510        </a>
511      </div>
512      <div class="email">htxue.ai <span>[at]</span> gatech <span>[dot]</span> edu</div>
513    </div>
514  </div>
515
516  <!-- ── JOB MARKET BANNER ────────────────────────────────── -->
517  <div style="
518    border-left: 3px solid var(--red);
519    background: color-mix(in oklab, var(--red) 7%, var(--surface));
520    border-radius: 6px;
521    padding: 10px 16px;
522    margin-bottom: 20px;
523    font-size: 13px;
524    line-height: 1.6;
525  ">
526    I will be on the <strong>2027 job market</strong>. Open to
527    <span style="color:var(--text)">
527Research Scientist / Research Engineer / MTS</span>
528    positions related to <span style="color:var(--text)">Generative AI</span> and
529    <span style="color:var(--text)">Multimodal LLMs</span>.
530    Feel free to reach out — <a href="mailto:[email protected]">[email protected]</a>
531  </div>
532
533  <!-- ── ABOUT ─────────────────────────────────────────── -->
534  <div class="sec-label">about</div>
535  <div class="term" style="margin-bottom:32px">
536    <div class="term-bar">
537      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
538      <div class="term-title">haotian@gt — zsh</div>
539    </div>
540    <div class="term-body" style="padding-bottom:8px">
541      <div><span class="prompt">➜</span> <span class="cmd">cat research.json</span></div>
542      <div style="margin-top:4px">{</div>
543      <div style="padding-left:20px"><span class="key">"focus"</span><span class="val">:&nbsp;[</span>
544        <span class="ptag diffusion" style="margin:0 3px">video diffusion</span>
545        <span class="ptag diffusion" style="margin:0 3px">world models</span>
546        <span class="ptag robotics" style="margin:0 3px">embodied AI</span>
547        <span class="ptag safety" style="margin:0 3px">safe generative AI</span>
548      <span class="val"> ],</span></div>
549      <div style="padding-left:20px"><span class="key">"edu"</span><span class="val">:&nbsp;</span><span class="val">"B.E. CS (Zhiyuan Honors) · SJTU 2022"</span></div>
550      <div>}</div>
551      <div style="margin-top:10px"><span class="prompt">➜</span> <span class="cmd">cat interests.md</span></div>
552      <div style="margin-top:4px; padding-left:4px; line-height:2; font-family: system-ui, -apple-system, sans-serif; font-size: 13px;">
553        <div><span class="val" style="font-family:var(--font)">▸</span> Video diffusion and world models &nbsp;<a href="https://xavihart.github.io/mogan/">MoGAN</a><span class="val">[ECCV'26]</span> <a href="https://xavihart.github.io/ACWM-Phys">ACWM-Phys</a><span class="val">[arXiv]</span></div>
554        <div><span class="val" style="font-family:var(--font)">▸</span> Multi-modal Large Language Models &nbsp;<a href="https://github.com/xavihart/PIO">PIO-Bench</a><span class="val">[TMLR'26]</span> <a href="https://lamawmouk.github.io/SGMRI-VQA">SGMRI-VQA</a><span class="val">[arXiv]</span></div>
555        <div><span class="val" style="font-family:var(--font)">▸</span> Embodied AI &nbsp;<a href="https://quest-model.github.io/">QueST</a><span class="val">[NeurIPS'24]</span></div>
556        <div><span class="val" style="font-family:var(--font)">▸</span> Safe generative AI &nbsp;<a href="https://github.com/xavihart/Diff-PGD">Diff-PGD</a><span class="val">[NeurIPS'23]</span> <a href="https://github.com/xavihart/Diff-Protect">SDS-Attack</a><span class="val">[ICLR'24]</span> <a href="https://github.com/xavihart/PDM-Pure">PDM-Pure</a><span class="val">[NeurIPSW]</span> <a href="https://sites.google.com/view/diffusion-policy-attacker">DP-Attacker</a><span class="val">[NeurIPS'24]</span> <a href="https://immune2v.github.io/">Immune2V</a><span class="val">[arXiv]</span></div>
557      </div>
558      <div style="margin-top:10px"><span class="prompt">➜</span> <span class="cmd">ls internships/</span></div>
559    </div>
560    <!-- company logos -->
561    <div class="intern-logos">
562      <div class="co">
563        <img src="static/image/logos/meta.jpg" alt="Meta" />
564        Meta MSL · 2026
565      </div>
566      <div class="co">
567        <img src="static/image/logos/Adobe_Firefly_Logo.svg.png" alt="Adobe" />
568        Adobe Firefly · 2025
569      </div>
570      <div class="co">
571        <img src="static/image/logos/235_Nvidia_logo-512.webp" alt="NVIDIA" />
572        NVIDIA DIR · 2024
573      </div>
574      <div class="co">
575        <img src="static/image/logos/microsoft.png" alt="MSRA" />
576        MSRA · 2021
577      </div>
578      <div class="co">
579        <img src="static/image/logos/mit.png" alt="MIT" />
580        MIT CSAIL · 2021
581      </div>
582    </div>
583    <!-- interactive input -->
584    <div class="term-output-area" id="termOutput"></div>
585    <div class="term-input-row">
586      <span class="prompt">➜</span>
587      <input class="term-input" id="termInput" placeholder="type a command…" autocomplete="off" spellcheck="false" />
588    </div>
589  </div>
590
591  <!-- ── NEWS ──────────────────────────────────────────── -->
592  <div class="sec-label">news</div>
593  <div class="term" style="margin-bottom:32px">
594    <div class="term-bar">
595      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
596      <div class="term-title">tail -f news.log</div>
597    </div>
598    <ul class="news-log" id="newsList">
599      <li><span class="ts">2026-06</span><span class="msg"><a href="https://xavihart.github.io/mogan/">MoGAN</a> accepted to <strong>ECCV 2026</strong>!</span></li>
600      <li><span class="ts">2026-05</span><span class="msg">We propose <a href="https://xavihart.github.io/ACWM-Phys">ACWM-Phys</a>, a physics-rich benchmark for action-conditioned video world models.</span></li>
601      <li><span class="ts">2026-04</span><span class="msg"><a href="https://github.com/xavihart/PIO">PIO-Bench</a> accepted to <strong>TMLR 2026</strong>!</span></li>
602      <li><span class="ts">2026-04</span><span class="msg">We propose <a href="https://immune2v.github.io/">Immune2V</a>, a protective watermark for Image-to-Video.</span></li>
603      <li><span class="ts">2026-04</span><span class="msg">I will be at <strong>Meta MSL</strong> working on World Model.</span></li>
604      <li><span class="ts">2026-01</span><span class="msg">LapFlow accepted to <strong>ICLR 2026</strong>. Laplacian multi-scale flow matching for generation.</span></li>
605      <li><span class="ts">2025-10</span><span class="msg">We propose <a href="https://xavihart.github.io/mogan/">MoGAN</a>, post-training for motion quality in few-step video diffusion.</span></li>
606      <li><span class="ts">2025-09</span><span class="msg">We propose <a href="https://github.com/xavihart/PIO">PIO-Bench</a>, a visual-grounding-centric benchmark for embodied VLM reasoning.</span></li>
607      <li><span class="ts">2025-04</span><span class="msg">Joining <strong>Adobe Firefly</strong> (Summer+Fall) for post-training on video diffusion.</span></li>
608      <li><span class="ts">2024-10</span><span class="msg"><strong>NeurIPS 2024 Scholar Award</strong> — see you in Vancouver!</span></li>
609      <li><span class="ts">2024-09</span><span class="msg">Three papers @ NeurIPS 2024: <a href="https://sites.google.com/view/diffusion-policy-attacker">DP-Attacker</a>, <a href="https://sbyebss.github.io/refdrop/">RefDrop</a>, <a href="https://quest-model.github.io/">QueST</a>.</span></li>
610      <li><span class="ts">2024-05</span><span class="msg">Summer intern at <a href="https://research.nvidia.com/labs/dir/">NVIDIA DIR Group</a>.</span></li>
611      <li><span class="ts">2024-03</span><span class="msg">ICLR 2024 Travel Award.</span></li>
612      <li><span class="ts">2024-01</span><span class="msg"><a href="https://github.com/xavihart/Diff-Protect">SDS-Attack</a> accepted to <strong>ICLR 2024</strong>.</span></li>
613      <li><span class="ts">2023-10</span><span class="msg">NeurIPS 2023 Scholar Award; invited reviewer for TPAMI.</span></li>
614      <li><span class="ts">2023-09</span><span class="msg"><a href="https://github.com/xavihart/Diff-PGD">Diff-PGD</a> and <a href="https://github.com/xavihart/3D-IntPhys">3D-IntPhys</a> accepted to <strong>NeurIPS 2023</strong>.</span></li>
615      <li><span class="ts">2022-12</span><span class="msg">Top Reviewer · NeurIPS 2022.</span></li>
616      <li><span class="ts">2022-10</span><span class="msg"><a href="https://aclanthology.org/2022.findings-emnlp.173/">Distance-Transformer</a> accepted to <strong>EMNLP 2022</strong> Findings.</span></li>
617      <li><span class="ts">2022-08</span><span class="msg">Started PhD at ML@GT.</span></li>
618    </ul>
619  </div>
620
621  <!-- ── PUBLICATIONS ──────────────────────────────────── -->
622  <div class="sec-label">selected publications</div>
623  <div class="term" style="margin-bottom:32px">
624    <div class="term-bar">
625      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
626      <div class="term-title">publications.bib</div>
627    </div>
628    <div style="padding: 4px 18px 0">
629      <ul class="pub-list">
630
631        <li>
632          <div class="pub-item highlight">
633            <div class="pub-meta">
634              <div class="pub-title"><a href="https://xavihart.github.io/ACWM-Phys">ACWM-Phys: Investigating Generalized Physical Interaction in Action-Conditioned Video World Models</a></div>
635              <div class="pub-authors"><span class="me">
635Haotian Xue</span>, Yipu Chen*, Liqian Ma*, Zelin Zhao, Lama Moukheiber, Yuchen Zhu, Yongxin Chen</div>
636              <div><span class="pub-venue">arXiv 2026</span></div>
637              <div class="pub-tags">
638                <span class="ptag diffusion">Diffusion</span>
639                <span class="ptag video">Video</span>
640                <span class="ptag robotics">World Model</span>
641                <span class="ptag robotics">Benchmark</span>
642              </div>
643              <div class="pub-links">
644                <a href="https://arxiv.org/abs/2605.08567">arXiv</a>
645                <a href="https://xavihart.github.io/ACWM-Phys">project</a>
646                <a href="https://github.com/xavihart/ACWM-Phys-dev">code</a>
647              </div>
648            </div>
649          </div>
650        </li>
651
652        <li>
653          <div class="pub-item">
654            <div class="pub-meta">
655              <div class="pub-title">Immune2V: Image Immunization Against Dual-Stream Image-to-Video Generation</div>
656              <div class="pub-authors">Zeqian Long*, Ozgur Kara*, <span class="me">Haotian Xue*</span>, Yongxin Chen, James M. Rehg</div>
657              <div><span class="pub-venue">arXiv 2026</span></div>
658              <div class="pub-tags">
659                <span class="ptag safety">Safety</span>
660                <span class="ptag diffusion">Diffusion</span>
661                <span class="ptag video">Video</span>
662              </div>
663              <div class="pub-links"><a href="https://arxiv.org/abs/2604.10837">arXiv</a> <a href="https://immune2v.github.io/">project</a> <a href="https://github.com/Zeqian-Long/Immune2V">code</a></div>
664            </div>
665          </div>
666        </li>
667
668        <li>
669          <div class="pub-item">
670            <div class="pub-meta">
671              <div class="pub-title"><a href="https://lamawmouk.github.io/SGMRI-VQA">Beyond a Single Frame: Multi-Frame Spatially Grounded Reasoning Across Volumetric MRI</a></div>
672              <div class="pub-authors">Lama Moukheiber, Caleb M. Yeung, <span class="me">Haotian Xue</span>, Alec Helbling, Zelin Zhao, Yongxin Chen</div>
673              <div><span class="pub-venue">arXiv 2026</span></div>
674              <div class="pub-tags">
675                <span class="ptag vlm">VLM</span>
676                <span class="ptag vision">Vision</span>
677                <span class="ptag td">3D</span>
678              </div>
679              <div class="pub-links"><a href="https://arxiv.org/abs/2604.15808">arXiv</a> <a href="https://lamawmouk.github.io/SGMRI-VQA">project</a> <a href="https://github.com/lamawmouk/SGMRI-VQA">code</a></div>
680            </div>
681          </div>
682        </li>
683
684        <li>
685          <div class="pub-item highlight">
686            <div class="pub-meta">
687              <div class="pub-title"><a href="https://xavihart.github.io/mogan/">MoGAN: Improving Motion Quality in Video Diffusion via Few-Step Motion Adversarial Post-Training</a></div>
688              <div class="pub-authors"><span class="me">Haotian Xue</span>, Qi Chen, Zhonghao Wang, Xun Huang, Eli Shechtman, Jinrong Xie, Yongxin Chen</div>
689              <div><span class="pub-venue iclr">ECCV 2026</span></div>
690              <div class="pub-tags">
691                <span class="ptag diffusion">Diffusion</span>
692                <span class="ptag video">Video</span>
693              </div>
694              <div class="pub-links">
695                <a href="https://arxiv.org/abs/2511.21592">arXiv</a>
696                <a href="https://xavihart.github.io/mogan/">project</a>
697              </div>
698            </div>
699          </div>
700        </li>
701
702        <li>
703          <div class="pub-item">
704            <div class="pub-meta">
705              <div class="pub-title">Laplacian Multi-scale Flow Matching for Generative Modeling</div>
706              <div class="pub-authors">Zelin Zhao, Petr Molodyk, <span class="me">Haotian Xue</span>, Yongxin Chen</div>
707              <div><span class="pub-venue iclr">ICLR 2026</span></div>
708              <div class="pub-tags">
709                <span class="ptag diffusion">Diffusion</span>
710                <span class="ptag vision">Vision</span>
711              </div>
712              <div class="pub-links">
713                <a href="https://arxiv.org/abs/2511.21592">arXiv</a>
714              </div>
715            </div>
716          </div>
717        </li>
718
719        <li>
720          <div class="pub-item">
721            <div class="pub-meta">
722              <div class="pub-title"><a href="https://github.com/xavihart/PIO">Point-It-Out: Benchmarking Embodied Reasoning for VLMs in Multi-Stage Visual Grounding</a></div>
723              <div class="pub-authors"><span class="me">
723Haotian Xue</span>, Yunhao Ge, Yu Zeng, Max Li, Ming-Yu Liu, Yongxin Chen, Jiaojiao Fan</div>
724              <div><span class="pub-venue iclr">TMLR 2026</span></div>
725              <div class="pub-tags">
726                <span class="ptag vlm">VLM</span>
727                <span class="ptag robotics">Robotics</span>
728              </div>
729              <div class="pub-links">
730                <a href="https://arxiv.org/abs/2509.25794">arXiv</a>
731                <a href="https://github.com/xavihart/PIO/">code</a>
732              </div>
733            </div>
734          </div>
735        </li>
736
737        <li>
738          <div class="pub-item">
739            <div class="pub-meta">
740              <div class="pub-title"><a href="https://sbyebss.github.io/refdrop/">RefDrop: Controllable Consistency in Image or Video Generation via Reference Feature Guidance</a></div>
741              <div class="pub-authors">Jiaojiao Fan, <span class="me">Haotian Xue</span>, Qinsheng Zhang, Yongxin Chen</div>
742              <div><span class="pub-venue neurips">NeurIPS 2024</span></div>
743              <div class="pub-tags">
744                <span class="ptag diffusion">Diffusion</span>
745                <span class="ptag vision">Vision</span>
746              </div>
747              <div class="pub-links">
748                <a href="https://arxiv.org/abs/2405.17661">arXiv</a>
749                <a href="https://sbyebss.github.io/refdrop/">project</a>
750              </div>
751            </div>
752          </div>
753        </li>
754
755        <li>
756          <div class="pub-item">
757            <div class="pub-meta">
758              <div class="pub-title"><a href="https://quest-model.github.io/">QueST: Self-Supervised Skill Abstractions for Learning Continuous Control</a></div>
759              <div class="pub-authors">Arthava Mete, <span class="me">Haotian Xue</span>, Albert Wilcox, Yongxin Chen, Animesh Garg</div>
760              <div><span class="pub-venue neurips">NeurIPS 2024</span></div>
761              <div class="pub-tags">
762                <span class="ptag robotics">Robotics</span>
763                <span class="ptag vision">Vision</span>
764              </div>
765              <div class="pub-links">
766                <a href="https://arxiv.org/abs/2407.15840">arXiv</a>
767                <a href="https://quest-model.github.io/">project</a>
768                <a href="https://github.com/pairlab/QueST">code</a>
769              </div>
770            </div>
771          </div>
772        </li>
773
774        <li>
775          <div class="pub-item">
776            <div class="pub-meta">
777              <div class="pub-title"><a href="https://sites.google.com/view/diffusion-policy-attacker">Diffusion Policy Attacker: Crafting Adversarial Attacks for Diffusion-based Policies</a></div>
778              <div class="pub-authors">Yipu Chen*, <span class="me">Haotian Xue*</span>, Yongxin Chen</div>
779              <div><span class="pub-venue neurips">NeurIPS 2024</span></div>
780              <div class="pub-tags">
781                <span class="ptag diffusion">Diffusion</span>
782                <span class="ptag safety">Safety</span>
783              </div>
784              <div class="pub-links">
785                <a href="https://arxiv.org/abs/2405.19424">arXiv</a>
786                <a href="https://sites.google.com/view/diffusion-policy-attacker/">project</a>
787              </div>
788            </div>
789          </div>
790        </li>
791
792        <li>
793          <div class="pub-item">
794            <div class="pub-meta">
795              <div class="pub-title"><a href="https://github.com/xavihart/Diff-Protect">Towards More Effective Protection Against Diffusion-Based Mimicry with Score Distillation</a></div>
796              <div class="pub-authors"><span class="me">Haotian Xue</span>, Chumeng Liang*, Xiaoyu Wu*, Yongxin Chen</div>
797              <div><span class="pub-venue iclr">ICLR 2024</span></div>
798              <div class="pub-tags">
799                <span class="ptag diffusion">Diffusion</span>
800                <span class="ptag safety">Safety</span>
801              </div>
802              <div class="pub-links">
803                <a href="https://arxiv.org/abs/2311.12832">arXiv</a>
804                <a href="https://github.com/xavihart/Diff-Protect">code</a>
805                <a href="https://iclr.cc/media/iclr-2024/Slides/18759.pdf">poster</a>
806              </div>
807            </div>
808          </div>
809        </li>
810
811        <li>
812          <div class="pub-item">
813            <div class="pub-meta">
814              <div class="pub-title"><a href="https://github.com/xavihart/PDM-Pure">Pixel is a Barrier: Diffusion Models Are More Adversarially Robust Than We Think</a></div>
815              <div class="pub-authors"><span class="me">
815Haotian Xue</span>, Yongxin Chen</div>
816              <div><span class="pub-venue workshop">NeurIPSW: SafeGenAI 2024</span></div>
817              <div class="pub-tags">
818                <span class="ptag diffusion">Diffusion</span>
819                <span class="ptag safety">Safety</span>
820              </div>
821              <div class="pub-links">
822                <a href="https://arxiv.org/abs/2404.13320">arXiv</a>
823                <a href="https://github.com/xavihart/PDM-Pure">code</a>
824              </div>
825            </div>
826          </div>
827        </li>
828
829        <li>
830          <div class="pub-item">
831            <div class="pub-meta">
832              <div class="pub-title"><a href="https://github.com/xavihart/Diff-PGD">Diffusion-Based Adversarial Sample Generation for Improved Stealthiness and Controllability</a></div>
833              <div class="pub-authors"><span class="me">Haotian Xue</span>, Alexandre Araujo, Bin Hu, Yongxin Chen</div>
834              <div><span class="pub-venue neurips">NeurIPS 2023</span></div>
835              <div class="pub-tags">
836                <span class="ptag diffusion">Diffusion</span>
837                <span class="ptag safety">Safety</span>
838              </div>
839              <div class="pub-links">
840                <a href="https://arxiv.org/abs/2305.16494">arXiv</a>
841                <a href="https://github.com/xavihart/Diff-PGD">code</a>
842                <a href="static/docs/poster_diffpgd.pdf">poster</a>
843              </div>
844            </div>
845          </div>
846        </li>
847
848        <li>
849          <div class="pub-item">
850            <div class="pub-meta">
851              <div class="pub-title"><a href="https://arxiv.org/abs/2304.11470">3D-IntPhys: Towards More Generalized 3D-grounded Visual Intuitive Physics</a></div>
852              <div class="pub-authors"><span class="me">Haotian Xue</span>, Antonio Torralba, Joshua B. Tenenbaum, Daniel L. K. Yamins, Yunzhu Li, Hsiao-Yu Tung</div>
853              <div><span class="pub-venue neurips">NeurIPS 2023</span></div>
854              <div class="pub-tags">
855                <span class="ptag vision">Vision</span>
856                <span class="ptag td">3D</span>
857              </div>
858              <div class="pub-links">
859                <a href="https://arxiv.org/abs/2304.11470">arXiv</a>
860                <a href="static/docs/poster_3dintphys.pdf">poster</a>
861              </div>
862            </div>
863          </div>
864        </li>
865
866      </ul>
867      <div style="padding: 10px 0 14px; font-size: 11px; color: var(--muted);">
868        → <a href="https://scholar.google.com/citations?hl=en&user=wnweG9UAAAAJ">full list on Google Scholar</a>
869      </div>
870    </div>
871  </div>
872
873  <!-- ── REVIEWER ──────────────────────────────────────── -->
874  <div class="sec-label">service</div>
875  <div class="term" style="margin-bottom:0">
876    <div class="term-bar">
877      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
878      <div class="term-title">reviewer</div>
879    </div>
880    <div class="review-grid">
881      <span class="rv">NeurIPS</span>
882      <span class="rv">ICLR</span>
883      <span class="rv">ICML</span>
884      <span class="rv">CVPR</span>
885      <span class="rv">AISTATS</span>
886      <span class="rv">AAAI</span>
887      <span class="rv">TPAMI</span>
888      <span class="rv">TCSVT</span>
889    </div>
890  </div>
891
892  <!-- ── MISC ─────────────────────────────────────────── -->
893  <div class="sec-label" style="margin-top:32px">misc</div>
894  <div class="term" style="margin-bottom:0">
895    <div class="term-bar">
896      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
897      <div class="term-title">misc.txt</div>
898    </div>
899    <div class="term-body" style="line-height:2.1">
900      <div><span class="val">✈️  travel</span> &nbsp; 🇨🇳 🇺🇸 🇸🇪 🇦🇹 🇭🇺 🇫🇷 🇧🇪 🇨🇦</div>
901      <div><span class="val">🎹  piano &amp; music</span></div>
902      <div><span class="val">⚽  football (soccer)</span> &nbsp;· recently into 🏀 basketball &amp; 🎾 tennis</div>
903      <div><span class="val">
903🐱  cat</span> &nbsp; Pudding &nbsp;<span style="color:var(--muted);font-size:11px">— click portrait to meet him</span></div>
904    </div>
905  </div>
906
907  <footer>
908    <span>© Haotian Xue · 2025</span>
909  </footer>
910</div>
911
912<!-- ── CAT MODAL ─────────────────────────────────────────── -->
913<div class="cat-modal" id="catModal">
914  <div class="cat-window">
915    <div class="term-bar">
916      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
917      <div class="term-title">cat.sh</div>
918    </div>
919    <div class="cat-term-body">
920      <span class="prompt">➜</span>&nbsp;<span class="typed" id="typeTarget"></span><span id="typeCursor" style="color:var(--green)">▋</span>
921    </div>
922    <div class="cat-img-wrap" id="catImgWrap">
923      <img id="catImg" src="static/image/photos/pudding/pudding1.png" alt="Pudding" />
924    </div>
925    <div class="cat-footer">
926      <span id="catCaption" style="opacity:0;transition:opacity .4s .1s">🐱 Pudding</span>
927      <div class="cat-nav">
928        <button class="cat-nav-btn" id="catPrev">←</button>
929        <span class="cat-counter" id="catCounter">1 / 1</span>
930        <button class="cat-nav-btn" id="catNext">→</button>
931      </div>
932      <button class="cat-close" id="catClose">✕ close</button>
933    </div>
934  </div>
935</div>
936
937<!-- ── GAME MODAL ─────────────────────────────────────────── -->
938<div class="game-modal" id="gameModal">
939  <div class="game-window">
940    <div class="term-bar">
941      <div class="term-dots"><span class="r"></span><span class="y"></span><span class="g"></span></div>
942      <div class="term-title">aim_trainer.exe</div>
943    </div>
944    <canvas id="gameCanvas" width="900" height="560"></canvas>
945    <div class="game-footer">
946      <span id="gameStatus">type <b>game</b> in the terminal to launch</span>
947      <button class="game-close" id="gameClose">✕ close</button>
948    </div>
949  </div>
950</div>
951
952<script>
953  // theme
954  const root = document.documentElement;
955  const btn  = document.getElementById('themeBtn');
956  const saved = localStorage.getItem('theme') || 'dark';
957  root.setAttribute('data-theme', saved);
958  btn.textContent = saved === 'dark' ? 'light' : 'dark';
959  btn.addEventListener('click', () => {
960    const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
961    root.setAttribute('data-theme', next);
962    localStorage.setItem('theme', next);
963    btn.textContent = next === 'dark' ? 'light' : 'dark';
964  });
965
966  // portrait
967  const portrait = document.getElementById('portrait');
968  portrait.addEventListener('mouseenter', () => portrait.classList.add('revealed'));
969  portrait.addEventListener('mouseleave', () => portrait.classList.remove('revealed'));
970  const catHint = document.getElementById('catHint');
971  function dismissHint() { catHint.classList.add('hidden'); }
972
973  portrait.addEventListener('click', () => { dismissHint(); openCat(); });
974  document.getElementById('catBadge').addEventListener('click', e => { e.stopPropagation(); dismissHint(); openCat(); });
975
976  // cat modal
977  // ── Add more photos by dropping files into static/image/photos/pudding/
978  // ── then add their filenames to this array:
979  const PUDDING_PHOTOS = [
980    'static/image/photos/pudding/pudding1.png',
981    'static/image/photos/pudding/Weixin Image_20260414013420_35_17.png',
982    'static/image/photos/pudding/Weixin Image_20260414013423_36_17.png',
983    'static/image/photos/pudding/Weixin Image_20260414013435_37_17.jpg',
984    'static/image/photos/pudding/Weixin Image_20260414013440_38_17.jpg',
985    'static/image/photos/pudding/Weixin Image_20260414013445_39_17.jpg',
986    'static/image/photos/pudding/Weixin Image_20260414013453_40_17.jpg',
987  ];
988
989  const modal      = document.getElementById('catModal');
990  const imgWrap    = document.getElementById('catImgWrap');
991  const catImg     = document.getElementById('catImg');
992  const typeTarget = document.getElementById('typeTarget');
993  const typeCursor = document.getElementById('typeCursor');
994  const caption    = document.getElementById('catCaption');
995  const closeBtn   = document.getElementById('catClose');
996  const prevBtn    = document.getElementById('catPrev');
997  const nextBtn    = document.getElementById('catNext');
998  const counter    = document.getElementById('catCounter');
999  let catIdx = 0;
1000  let typing;
1001
1002  function updateNav() {
1003    counter.textContent = `${catIdx + 1} / ${PUDDING_PHOTOS.length}`;
1004    prevBtn.disabled = catIdx === 0;
1005    nextBtn.disabled = catIdx === PUDDING_PHOTOS.length - 1;
1006  }
1007
1008  function showPhoto(idx) {
1009    catImg.classList.add('fade');
1010    setTimeout(() => {
1011      catImg.src = PUDDING_PHOTOS[idx];
1012      catImg.classList.remove('fade');
1013      updateNav();
1014    }, 200);
1015  }
1016
1017  prevBtn.addEventListener('click', () => { if (catIdx > 0) showPhoto(--catIdx); });
1018  nextBtn.addEventListener('click', () => { if (catIdx < PUDDING_PHOTOS.length - 1) showPhoto(++catIdx); });
1019
1020  function openCat() {
1021    catIdx = 0;
1022    catImg.src = PUDDING_PHOTOS[0];
1023    updateNav();
1024    modal.classList.add('open');
1025    typeTarget.textContent = '';
1026    imgWrap.classList.remove('show');
1027    caption.style.opacity = '0';
1028    typeCursor.style.display = 'inline';
1029    let i = 0;
1030    clearInterval(typing);
1031    const cmd = `open pudding/${PUDDING_PHOTOS[0].split('/').pop()}`;
1032    typing = setInterval(() => {
1033      if (i < cmd.length) { typeTarget.textContent += cmd[i++]; }
1034      else {
1035        clearInterval(typing);
1036        typeCursor.style.display = 'none';
1037        setTimeout(() => {
1038          imgWrap.classList.add('show');
1039          caption.style.opacity = '1';
1040        }, 180);
1041      }
1042    }, 55);
1043  }
1044
1045  function closeCat() { modal.classList.remove('open'); clearInterval(typing); }
1046  closeBtn.addEventListener('click', closeCat);
1047  modal.addEventListener('click', e => { if (e.target === modal) closeCat(); });
1048  document.addEventListener('keydown', e => { if (e.key === 'Escape') closeCat(); });
1049
1050  // ── aim trainer game ─────────────────────────────────────
1051  (function () {
1052    const gameModal  = document.getElementById('gameModal');
1053    const canvas     = document.getElementById('gameCanvas');
1054    const ctx        = canvas.getContext('2d');
1055    const statusEl   = document.getElementById('gameStatus');
1056    const closeBtn   = document.getElementById('gameClose');
1057
1058    const TOTAL      = 20;
1059    const R_MIN      = 18, R_MAX = 36;
1060    const PAD        = 50;
1061
1062    let state = 'idle'; // idle | running | done
1063    let hardMode = false;
1064    let targets = [], cur = 0;
1065    let startTime, elapsed;
1066    let mouse = { x: canvas.width / 2, y: canvas.height / 2 };
1067    let flash = null;
1068    let misses = 0;
1069    let raf;
1070
1071    function randTarget() {
1072      const r = hardMode
1073        ? R_MIN * 0.7 + Math.random() * (R_MAX * 0.7 - R_MIN * 0.7)
1074        : R_MIN + Math.random() * (R_MAX - R_MIN);
1075      const speed = hardMode ? 1.8 + Math.random() * 2.2 : 0;
1076      const angle = Math.random() * Math.PI * 2;
1077      return {
1078        x: PAD + r + Math.random() * (canvas.width  - 2 * PAD - 2 * r),
1079        y: PAD + r + Math.random() * (canvas.height - 2 * PAD - 2 * r),
1080        r,
1081        vx: Math.cos(angle) * speed,
1082        vy: Math.sin(angle) * speed,
1083        born: performance.now(),
1084      };
1085    }
1086
1087    function startGame() {
1088      targets = Array.from({ length: TOTAL }, randTarget);
1089      cur = 0; misses = 0; flash = null;
1090      startTime = performance.now();
1091      state = 'running';
1092      const mode = hardMode ? '  [HARD — moving targets]' : '  ·  click!';
1093      statusEl.textContent = `target 1 / ${TOTAL}${mode}`;
1094      loop();
1095    }
1096
1097    function drawCrosshair(x, y) {
1098      const len = 14, gap = 5;
1099      ctx.save();
1100      ctx.strokeStyle = '#39d353';
1101      ctx.lineWidth = 1.5;
1102      ctx.shadowColor = '#39d353';
1103      ctx.shadowBlur = 6;
1104      // horizontal
1105      ctx.beginPath(); ctx.moveTo(x - len - gap, y); ctx.lineTo(x - gap, y); ctx.stroke();
1106      ctx.beginPath(); ctx.moveTo(x + gap, y);        ctx.lineTo(x + len + gap, y); ctx.stroke();
1107      // vertical
1108      ctx.beginPath(); ctx.moveTo(x, y - len - gap); ctx.lineTo(x, y - gap); ctx.stroke();
1109      ctx.beginPath(); ctx.moveTo(x, y + gap);        ctx.lineTo(x, y + len + gap); ctx.stroke();
1110      // center dot
1111      ctx.beginPath(); ctx.arc(x, y, 2, 0, Math.PI * 2);
1112      ctx.fillStyle = '#39d353'; ctx.shadowBlur = 4; ctx.fill();
1113      ctx.restore();
1114    }
1115
1116    function drawTarget(t) {
1117      const age = (performance.now() - t.born) / 1000;
1118      // pulse scale
1119      const scale = 1 + 0.06 * Math.sin(age * 6);
1120      const r = t.r * scale;
1121
1122      ctx.save();
1123      // outer ring
1124      ctx.beginPath(); ctx.arc(t.x, t.y, r, 0, Math.PI * 2);
1125      ctx.strokeStyle = '#ff5f57'; ctx.lineWidth = 2;
1126      ctx.shadowColor = '#ff5f57'; ctx.shadowBlur = 12; ctx.stroke();
1127      // inner fill
1128      ctx.beginPath(); ctx.arc(t.x, t.y, r * 0.55, 0, Math.PI * 2);
1129      ctx.fillStyle = 'rgba(255,95,87,0.18)'; ctx.shadowBlur = 0; ctx.fill();
1130      // center dot
1131      ctx.beginPath(); ctx.arc(t.x, t.y, 4, 0, Math.PI * 2);
1132      ctx.fillStyle = '#ff5f57'; ctx.fill();
1133      ctx.restore();
1134    }
1135
1136    function drawHUD() {
1137      if (state === 'idle') return;
1138      const now = performance.now();
1139      const secs = ((state === 'done' ? elapsed : now - startTime) / 1000).toFixed(2);
1140      ctx.save();
1141      ctx.font = '12px monospace';
1142      ctx.fillStyle = '#8b949e';
1143      ctx.textAlign = 'left';
1144      ctx.fillText(`time: ${secs}s`, 12, 20);
1145      ctx.textAlign = 'right';
1146      ctx.fillText(`targets: ${Math.min(cur, TOTAL)} / ${TOTAL}`, canvas.width - 12, 20);
1147      if (misses > 0) {
1148        ctx.textAlign = 'left';
1149        ctx.fillStyle = '#ff7b72';
1150        ctx.fillText(`miss: ${misses}`, 12, 36);
1151      }
1152      ctx.restore();
1153    }
1154
1155    function drawFlash() {
1156      if (!flash) return;
1157      const age = performance.now() - flash.t;
1158      if (age > 220) { flash = null; return; }
1159      const alpha = 1 - age / 220;
1160      const radius = 8 + age * 0.12;
1161      ctx.save();
1162      ctx.beginPath(); ctx.arc(flash.x, flash.y, radius, 0, Math.PI * 2);
1163      ctx.strokeStyle = `rgba(57,211,83,${alpha})`;
1164      ctx.lineWidth = 1.5; ctx.stroke();
1165      ctx.restore();
1166    }
1167
1168    function drawIdle() {
1169      ctx.clearRect(0, 0, canvas.width, canvas.height);
1170      ctx.save();
1171      ctx.fillStyle = '#30363d';
1172      ctx.font = '13px monospace';
1173      ctx.textAlign = 'center';
1174      ctx.fillText('type  game  in the terminal below', canvas.width / 2, canvas.height / 2 - 10);
1175      ctx.fillStyle = '#39d353';
1176      ctx.fillText('then click here to start', canvas.width / 2, canvas.height / 2 + 14);
1177      ctx.restore();
1178      drawCrosshair(mouse.x, mouse.y);
1179    }
1180
1181    function drawDone() {
1182      const secs = (elapsed / 1000).toFixed(2);
1183      const avg  = (elapsed / TOTAL / 1000).toFixed(2);
1184      ctx.clearRect(0, 0, canvas.width, canvas.height);
1185      ctx.save();
1186      ctx.textAlign = 'center';
1187      ctx.fillStyle = '#39d353';
1188      ctx.font = 'bold 18px monospace';
1189      ctx.fillText(`✓  completed${hardMode ? '  [HARD]' : ''}`, canvas.width / 2, canvas.height / 2 - 36);
1190      ctx.font = '13px monospace';
1191      ctx.fillStyle = '#e6edf3';
1192      ctx.fillText(`time: ${secs}s   ·   avg per target: ${avg}s   ·   misses: ${misses}`, canvas.width / 2, canvas.height / 2);
1193      ctx.fillStyle = '#8b949e';
1194      ctx.fillText('click to play again', canvas.width / 2, canvas.height / 2 + 28);
1195      ctx.restore();
1196      drawCrosshair(mouse.x, mouse.y);
1197    }
1198
1199    function moveTarget(t) {
1200      t.x += t.vx; t.y += t.vy;
1201      if (t.x - t.r < 0)            { t.x = t.r;               t.vx *= -1; }
1202      if (t.x + t.r > canvas.width) { t.x = canvas.width - t.r; t.vx *= -1; }
1203      if (t.y - t.r < 0)            { t.y = t.r;                t.vy *= -1; }
1204      if (t.y + t.r > canvas.height){ t.y = canvas.height - t.r; t.vy *= -1; }
1205    }
1206
1207    function loop() {
1208      if (state !== 'running') return;
1209      raf = requestAnimationFrame(loop);
1210      if (hardMode) moveTarget(targets[cur]);
1211      ctx.clearRect(0, 0, canvas.width, canvas.height);
1212      if (hardMode) {
1213        // faint trail hint
1214        ctx.save();
1215        ctx.strokeStyle = 'rgba(255,95,87,0.12)';
1216        ctx.lineWidth = 1;
1217        ctx.setLineDash([3, 6]);
1218        ctx.beginPath();
1219        ctx.arc(targets[cur].x, targets[cur].y, targets[cur].r + 8, 0, Math.PI * 2);
1220        ctx.stroke();
1221        ctx.restore();
1222      }
1223      drawTarget(targets[cur]);
1224      drawFlash();
1225      drawHUD();
1226      drawCrosshair(mouse.x, mouse.y);
1227    }
1228
1229    canvas.addEventListener('mousemove', e => {
1230      const rect = canvas.getBoundingClientRect();
1231      mouse.x = e.clientX - rect.left;
1232      mouse.y = e.clientY - rect.top;
1233      if (state === 'idle') drawIdle();
1234    });
1235
1236    canvas.addEventListener('click', e => {
1237      const rect = canvas.getBoundingClientRect();
1238      const mx = e.clientX - rect.left;
1239      const my = e.clientY - rect.top;
1240
1241      if (state === 'idle' || state === 'done') { startGame(); return; }
1242      if (state !== 'running') return;
1243
1244      const t = targets[cur];
1245      const dist = Math.hypot(mx - t.x, my - t.y);
1246      if (dist <= t.r) {
1247        flash = { x: t.x, y: t.y, t: performance.now() };
1248        cur++;
1249        if (cur >= TOTAL) {
1250          elapsed = performance.now() - startTime;
1251          state = 'done';
1252          cancelAnimationFrame(raf);
1253          statusEl.textContent = `done! ${(elapsed/1000).toFixed(2)}s for ${TOTAL} targets · click canvas to retry`;
1254          drawDone();
1255        } else {
1256          statusEl.textContent = `target ${cur + 1} / ${TOTAL}`;
1257        }
1258      } else {
1259        misses++;
1260      }
1261    });
1262
1263    closeBtn.addEventListener('click', () => {
1264      gameModal.classList.remove('open');
1265      state = 'idle';
1266      cancelAnimationFrame(raf);
1267    });
1268    gameModal.addEventListener('click', e => {
1269      if (e.target === gameModal) { gameModal.classList.remove('open'); state = 'idle'; cancelAnimationFrame(raf); }
1270    });
1271    document.addEventListener('keydown', e => {
1272      if (e.key === 'Escape' && gameModal.classList.contains('open')) {
1273        gameModal.classList.remove('open'); state = 'idle'; cancelAnimationFrame(raf);
1274      }
1275    });
1276
1277    // draw initial idle frame when modal opens
1278    window._openGame = function (hard) {
1279      hardMode = !!hard;
1280      gameModal.classList.add('open');
1281      document.getElementById('gameModal').querySelector('.term-title').textContent =
1282        hard ? 'aim_trainer.exe  --hard' : 'aim_trainer.exe';
1283      state = 'idle';
1284      cancelAnimationFrame(raf);
1285      drawIdle();
1286    };
1287  })();
1288
1289  // interactive terminal
1290  (function () {
1291    const input  = document.getElementById('termInput');
1292    const output = document.getElementById('termOutput');
1293    if (!input || !output) return;
1294
1295    const CC_LOGO = `<span class="cc-logo" title="Claude Code">C</span>`;
1296    const CC_MSG  = 'Claude Code has not implemented it for Me :(';
1297
1298    function pushLine(html) {
1299      const div = document.createElement('div');
1300      div.className = 'out-line';
1301      div.innerHTML = html;
1302      output.appendChild(div);
1303      output.scrollTop = output.scrollHeight;
1304    }
1305
1306    input.addEventListener('keydown', e => {
1307      if (e.key !== 'Enter') return;
1308      const cmd = input.value.trim().toLowerCase();
1309      input.value = '';
1310      if (!cmd) return;
1311      pushLine(`<span class="echo-cmd">$ ${cmd}</span>`);
1312      if (cmd === 'game' || cmd === 'game --hard') {
1313        const hard = cmd.includes('--hard');
1314        setTimeout(() => {
1315          pushLine(`<span class="out-text" style="color:var(--green)">launching aim_trainer.exe${hard ? ' --hard' : ''}…</span>`);
1316          setTimeout(() => window._openGame(hard), 400);
1317        }, 80);
1318      } else {
1319        setTimeout(() => {
1320          pushLine(`${CC_LOGO}<span class="out-text">${CC_MSG}</span>`);
1321        }, 120);
1322      }
1323    });
1324  })();
1325
1326  // news scroll — show first 5 rows then scroll
1327  (function () {
1328    const list = document.getElementById('newsList');
1329    if (!list) return;
1330    const items = Array.from(list.children);
1331    const h = items.slice(0, 5).reduce((acc, li) => acc + li.offsetHeight, 0);
1332    list.style.maxHeight = Math.ceil(h) + 'px';
1333  })();
1334</script>
1334
1335</body>
1336</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.