opencode-bioresearcher 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Convert a markdown research report to a standalone HTML file.
2
+ """Convert a markdown research report to a standalone, publication-grade HTML file.
3
3
 
4
4
  Usage:
5
5
  uv run --with markdown python scripts/markdown-to-html.py report.md -o report.html
@@ -7,11 +7,19 @@ Usage:
7
7
  Dependencies:
8
8
  - markdown (python-markdown), provided by `uv run --with markdown`
9
9
 
10
- Wraps the rendered markdown in a minimal GitHub-like inline-CSS page so the
11
- output is a single self-contained file with no external assets.
10
+ Features:
11
+ - 100% self-contained: zero external network assets (fonts, icons, CDN scripts).
12
+ - BioResearcher · Deep Research branding & metadata card.
13
+ - Responsive Table of Contents: sticky desktop sidebar and mobile slide-over drawer.
14
+ - Interactive in-text citations: superscript badges, floating boundary-aware
15
+ tooltips with tail flipping, auto-linkified DOIs, PMIDs, and NCT trial IDs.
16
+ - Print-ready typography and media queries.
12
17
  """
13
18
 
14
19
  import argparse
20
+ import base64
21
+ import html
22
+ import re
15
23
  import sys
16
24
  from pathlib import Path
17
25
 
@@ -24,47 +32,1888 @@ except ImportError:
24
32
  )
25
33
  sys.exit(2)
26
34
 
35
+ DEFAULT_ICON_DATA_URI = "data:image/jpeg;base64,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"
36
+
37
+
38
+ def get_brand_icon_uri() -> str:
39
+ """Retrieve brand icon data URI with local repository fallback."""
40
+ repo_icon = Path(__file__).resolve().parents[3] / "connector" / "workbuddy" / "icon.jpg"
41
+ if repo_icon.is_file():
42
+ try:
43
+ return f"data:image/jpeg;base64,{base64.b64encode(repo_icon.read_bytes()).decode('ascii')}"
44
+ except OSError:
45
+ pass
46
+ return DEFAULT_ICON_DATA_URI
47
+
48
+
27
49
  CSS = """
28
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
29
- font-size: 16px; line-height: 1.6; color: #1f2328; background: #ffffff;
30
- max-width: 980px; margin: 0 auto; padding: 32px 44px; }
31
- h1, h2, h3, h4, h5, h6 { font-weight: 600; line-height: 1.25; margin-top: 24px;
32
- margin-bottom: 16px; }
33
- h1 { font-size: 2em; border-bottom: 1px solid #d1d9e0; padding-bottom: .3em; }
34
- h2 { font-size: 1.5em; border-bottom: 1px solid #d1d9e0; padding-bottom: .3em; }
35
- h3 { font-size: 1.25em; }
36
- p { margin-top: 0; margin-bottom: 16px; }
37
- a { color: #0969da; text-decoration: none; }
38
- a:hover { text-decoration: underline; }
39
- code { font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
40
- font-size: 85%; padding: .2em .4em; background: #f0f2f5; border-radius: 6px; }
41
- pre { background: #f6f8fa; border-radius: 6px; padding: 16px; overflow: auto; }
42
- pre code { background: transparent; padding: 0; font-size: 85%; }
43
- blockquote { border-left: .25em solid #d1d9e0; color: #59636e; padding: 0 1em;
44
- margin: 0 0 16px 0; }
45
- table { border-collapse: collapse; margin-bottom: 16px; display: block;
46
- overflow: auto; max-width: 100%; }
47
- table th, table td { border: 1px solid #d1d9e0; padding: 6px 13px; }
48
- table tr:nth-child(2n) { background: #f6f8fa; }
49
- ul, ol { margin-bottom: 16px; padding-left: 2em; }
50
- hr { height: .25em; background: #d1d9e0; border: 0; margin: 24px 0; }
51
- """
50
+ :root {
51
+ --primary: #0284c7;
52
+ --primary-hover: #0369a1;
53
+ --primary-light: #f0f9ff;
54
+ --primary-border: #bae6fd;
55
+ --text-main: #0f172a;
56
+ --text-muted: #64748b;
57
+ --text-light: #94a3b8;
58
+ --bg-page: #f8fafc;
59
+ --bg-surface: #ffffff;
60
+ --border-color: #e2e8f0;
61
+ --border-subtle: #f1f5f9;
62
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
63
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
64
+ --sidebar-w: 270px;
65
+ }
66
+
67
+ *, *::before, *::after { box-sizing: border-box; }
68
+
69
+ html {
70
+ scroll-behavior: smooth;
71
+ }
72
+
73
+ body {
74
+ margin: 0;
75
+ padding: 0;
76
+ font-family: var(--font-sans);
77
+ font-size: 16px;
78
+ line-height: 1.65;
79
+ color: var(--text-main);
80
+ background-color: var(--bg-page);
81
+ -webkit-font-smoothing: antialiased;
82
+ }
83
+
84
+ /* ---------------- Layout ---------------- */
85
+ .report-wrapper {
86
+ display: flex;
87
+ max-width: 1280px;
88
+ margin: 0 auto;
89
+ padding: 24px 20px;
90
+ gap: 36px;
91
+ }
92
+
93
+ .report-sidebar {
94
+ width: var(--sidebar-w);
95
+ flex-shrink: 0;
96
+ position: sticky;
97
+ top: 24px;
98
+ max-height: calc(100vh - 48px);
99
+ overflow-y: auto;
100
+ background: var(--bg-surface);
101
+ border: 1px solid var(--border-color);
102
+ border-radius: 12px;
103
+ padding: 20px 16px;
104
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
105
+ }
106
+
107
+ .report-main {
108
+ flex: 1;
109
+ min-width: 0; /* Prevents flex child overflow from wide tables */
110
+ max-width: 900px;
111
+ }
112
+
113
+ /* ---------------- Header & Branding ---------------- */
114
+ .report-header-card {
115
+ background: var(--bg-surface);
116
+ border: 1px solid var(--border-color);
117
+ border-radius: 12px;
118
+ padding: 28px 32px;
119
+ margin-bottom: 28px;
120
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
121
+ }
122
+
123
+ .brand-badge-bar {
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: space-between;
127
+ margin-bottom: 16px;
128
+ }
129
+
130
+ .brand-badge {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ gap: 8px;
134
+ background: var(--primary-light);
135
+ border: 1px solid var(--primary-border);
136
+ color: var(--primary);
137
+ font-size: 13px;
138
+ font-weight: 600;
139
+ padding: 4px 10px;
140
+ border-radius: 20px;
141
+ }
142
+
143
+ .brand-logo-img {
144
+ width: 20px;
145
+ height: 20px;
146
+ border-radius: 5px;
147
+ object-fit: cover;
148
+ flex-shrink: 0;
149
+ border: 1px solid rgba(15, 23, 42, 0.08);
150
+ display: inline-block;
151
+ vertical-align: middle;
152
+ }
153
+
154
+ .sidebar-logo-img {
155
+ width: 22px;
156
+ height: 22px;
157
+ border-radius: 5px;
158
+ object-fit: cover;
159
+ flex-shrink: 0;
160
+ border: 1px solid rgba(15, 23, 42, 0.08);
161
+ }
162
+
163
+ .drawer-logo-img {
164
+ width: 22px;
165
+ height: 22px;
166
+ border-radius: 5px;
167
+ object-fit: cover;
168
+ flex-shrink: 0;
169
+ border: 1px solid rgba(15, 23, 42, 0.08);
170
+ vertical-align: middle;
171
+ margin-right: 8px;
172
+ }
173
+
174
+ .report-title {
175
+ margin: 0 0 16px 0;
176
+ font-size: 2rem;
177
+ font-weight: 700;
178
+ line-height: 1.25;
179
+ color: var(--text-main);
180
+ }
181
+
182
+ .meta-grid {
183
+ display: grid;
184
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
185
+ gap: 12px;
186
+ margin-top: 16px;
187
+ padding-top: 16px;
188
+ border-top: 1px solid var(--border-subtle);
189
+ }
190
+
191
+ .meta-item {
192
+ display: flex;
193
+ flex-direction: column;
194
+ font-size: 13px;
195
+ }
196
+
197
+ .meta-label {
198
+ font-weight: 600;
199
+ text-transform: uppercase;
200
+ letter-spacing: 0.05em;
201
+ font-size: 11px;
202
+ color: var(--text-muted);
203
+ margin-bottom: 2px;
204
+ }
205
+
206
+ .meta-value {
207
+ color: var(--text-main);
208
+ }
209
+
210
+ .meta-code {
211
+ font-family: var(--font-mono);
212
+ background: var(--border-subtle);
213
+ padding: 2px 6px;
214
+ border-radius: 4px;
215
+ font-size: 12px;
216
+ display: inline-block;
217
+ width: fit-content;
218
+ }
219
+
220
+ .meta-scope {
221
+ grid-column: 1 / -1;
222
+ line-height: 1.5;
223
+ }
224
+
225
+ /* ---------------- Main Content Body ---------------- */
226
+ .markdown-body {
227
+ background: var(--bg-surface);
228
+ border: 1px solid var(--border-color);
229
+ border-radius: 12px;
230
+ padding: 36px 40px;
231
+ box-shadow: 0 1px 3px rgba(0,0,0,0.04);
232
+ }
233
+
234
+ .markdown-body h1,
235
+ .markdown-body h2,
236
+ .markdown-body h3,
237
+ .markdown-body h4,
238
+ .markdown-body h5,
239
+ .markdown-body h6 {
240
+ font-weight: 600;
241
+ line-height: 1.3;
242
+ margin-top: 1.8em;
243
+ margin-bottom: 0.6em;
244
+ color: var(--text-main);
245
+ scroll-margin-top: 80px;
246
+ }
247
+
248
+ .markdown-body h1 {
249
+ font-size: 1.75rem;
250
+ border-bottom: 1px solid var(--border-color);
251
+ padding-bottom: 0.3em;
252
+ }
253
+
254
+ .markdown-body h2 {
255
+ font-size: 1.4rem;
256
+ border-bottom: 1px solid var(--border-color);
257
+ padding-bottom: 0.3em;
258
+ color: #1e293b;
259
+ }
260
+
261
+ .markdown-body h3 {
262
+ font-size: 1.15rem;
263
+ color: #334155;
264
+ }
265
+
266
+ .markdown-body p {
267
+ margin-top: 0;
268
+ margin-bottom: 1.1em;
269
+ }
270
+
271
+ .markdown-body a {
272
+ color: var(--primary);
273
+ text-decoration: none;
274
+ }
275
+
276
+ .markdown-body a:hover {
277
+ text-decoration: underline;
278
+ }
279
+
280
+ .markdown-body ul, .markdown-body ol {
281
+ margin-top: 0;
282
+ margin-bottom: 1.2em;
283
+ padding-left: 1.6em;
284
+ }
285
+
286
+ .markdown-body li {
287
+ margin-bottom: 0.35em;
288
+ }
289
+
290
+ .markdown-body hr {
291
+ height: 1px;
292
+ background: var(--border-color);
293
+ border: 0;
294
+ margin: 2.2em 0;
295
+ }
296
+
297
+ .markdown-body blockquote {
298
+ margin: 1.2em 0;
299
+ padding: 0.6em 1.2em;
300
+ border-left: 4px solid var(--primary-border);
301
+ background: var(--primary-light);
302
+ color: #334155;
303
+ border-radius: 0 8px 8px 0;
304
+ }
305
+
306
+ /* ---------------- Tables ---------------- */
307
+ .markdown-body table {
308
+ width: 100%;
309
+ border-collapse: collapse;
310
+ margin: 1.4em 0;
311
+ font-size: 14px;
312
+ display: block;
313
+ overflow-x: auto;
314
+ border: 1px solid var(--border-color);
315
+ border-radius: 8px;
316
+ }
317
+
318
+ .markdown-body th, .markdown-body td {
319
+ padding: 8px 14px;
320
+ border: 1px solid var(--border-color);
321
+ text-align: left;
322
+ }
323
+
324
+ .markdown-body th {
325
+ background: #f8fafc;
326
+ font-weight: 600;
327
+ color: #475569;
328
+ white-space: nowrap;
329
+ }
330
+
331
+ .markdown-body tr:nth-child(even) td {
332
+ background: #fbfcfd;
333
+ }
334
+
335
+ .markdown-body tr:hover td {
336
+ background: #f1f5f9;
337
+ }
338
+
339
+ /* ---------------- Code ---------------- */
340
+ .markdown-body code {
341
+ font-family: var(--font-mono);
342
+ font-size: 85%;
343
+ background: #f1f5f9;
344
+ padding: 0.2em 0.4em;
345
+ border-radius: 4px;
346
+ color: #0f172a;
347
+ }
348
+
349
+ .markdown-body pre {
350
+ background: #0f172a;
351
+ color: #f8fafc;
352
+ padding: 16px;
353
+ border-radius: 8px;
354
+ overflow-x: auto;
355
+ margin: 1.2em 0;
356
+ }
357
+
358
+ .markdown-body pre code {
359
+ background: transparent;
360
+ color: inherit;
361
+ padding: 0;
362
+ font-size: 90%;
363
+ }
364
+
365
+ /* ---------------- Table of Contents (Sidebar) ---------------- */
366
+ .sidebar-header {
367
+ display: flex;
368
+ align-items: center;
369
+ justify-content: space-between;
370
+ margin-bottom: 14px;
371
+ padding-bottom: 12px;
372
+ border-bottom: 1px solid var(--border-color);
373
+ }
374
+
375
+ .sidebar-brand {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 6px;
379
+ font-weight: 700;
380
+ font-size: 14px;
381
+ color: var(--text-main);
382
+ }
383
+
384
+ .sidebar-badge {
385
+ font-size: 11px;
386
+ color: var(--primary);
387
+ background: var(--primary-light);
388
+ border: 1px solid var(--primary-border);
389
+ padding: 2px 6px;
390
+ border-radius: 10px;
391
+ font-weight: 600;
392
+ }
393
+
394
+ .toc-title {
395
+ font-size: 12px;
396
+ font-weight: 700;
397
+ text-transform: uppercase;
398
+ letter-spacing: 0.05em;
399
+ color: var(--text-muted);
400
+ margin-bottom: 10px;
401
+ }
402
+
403
+ .toc-container .toc ul {
404
+ list-style: none;
405
+ padding-left: 0;
406
+ margin: 0;
407
+ }
408
+
409
+ .toc-container .toc ul ul {
410
+ padding-left: 14px;
411
+ }
412
+
413
+ .toc-container .toc li {
414
+ margin-bottom: 4px;
415
+ }
416
+
417
+ .toc-container .toc a {
418
+ display: block;
419
+ font-size: 13px;
420
+ color: var(--text-muted);
421
+ padding: 4px 8px;
422
+ border-radius: 6px;
423
+ line-height: 1.4;
424
+ text-decoration: none;
425
+ transition: all 0.15s ease;
426
+ }
427
+
428
+ .toc-container .toc a:hover {
429
+ color: var(--primary);
430
+ background: var(--primary-light);
431
+ }
432
+
433
+ .toc-container .toc a.active {
434
+ color: var(--primary);
435
+ background: var(--primary-light);
436
+ font-weight: 600;
437
+ border-left: 3px solid var(--primary);
438
+ border-radius: 0 6px 6px 0;
439
+ padding-left: 7px;
440
+ }
441
+
442
+ /* ---------------- Mobile TOC Drawer & Toggle ---------------- */
443
+ .mobile-toc-btn {
444
+ display: none;
445
+ position: fixed;
446
+ bottom: 24px;
447
+ right: 24px;
448
+ z-index: 999;
449
+ align-items: center;
450
+ gap: 8px;
451
+ background: var(--primary);
452
+ color: #ffffff;
453
+ border: none;
454
+ border-radius: 30px;
455
+ padding: 10px 18px;
456
+ font-size: 14px;
457
+ font-weight: 600;
458
+ box-shadow: 0 4px 14px rgba(2, 132, 199, 0.35);
459
+ cursor: pointer;
460
+ transition: transform 0.15s ease;
461
+ }
462
+
463
+ .mobile-toc-btn:active {
464
+ transform: scale(0.96);
465
+ }
466
+
467
+ .mobile-toc-backdrop {
468
+ display: none;
469
+ position: fixed;
470
+ inset: 0;
471
+ background: rgba(15, 23, 42, 0.45);
472
+ backdrop-filter: blur(2px);
473
+ z-index: 1000;
474
+ opacity: 0;
475
+ transition: opacity 0.2s ease;
476
+ }
477
+
478
+ .mobile-toc-drawer {
479
+ position: fixed;
480
+ top: 0;
481
+ right: 0;
482
+ bottom: 0;
483
+ width: 320px;
484
+ max-width: 85vw;
485
+ background: var(--bg-surface);
486
+ z-index: 1001;
487
+ box-shadow: -4px 0 20px rgba(0,0,0,0.15);
488
+ transform: translateX(100%);
489
+ transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
490
+ display: flex;
491
+ flex-direction: column;
492
+ }
493
+
494
+ .mobile-toc-drawer.open {
495
+ transform: translateX(0);
496
+ }
497
+
498
+ .mobile-toc-backdrop.open {
499
+ display: block;
500
+ opacity: 1;
501
+ }
502
+
503
+ .drawer-header {
504
+ display: flex;
505
+ align-items: center;
506
+ justify-content: space-between;
507
+ padding: 18px 20px;
508
+ border-bottom: 1px solid var(--border-color);
509
+ }
510
+
511
+ .drawer-title {
512
+ font-weight: 700;
513
+ font-size: 16px;
514
+ color: var(--text-main);
515
+ }
516
+
517
+ .drawer-close {
518
+ background: transparent;
519
+ border: none;
520
+ font-size: 24px;
521
+ color: var(--text-muted);
522
+ cursor: pointer;
523
+ padding: 4px 8px;
524
+ }
525
+
526
+ .drawer-toc {
527
+ flex: 1;
528
+ overflow-y: auto;
529
+ padding: 16px 20px;
530
+ }
531
+
532
+ .drawer-toc .toc ul {
533
+ list-style: none;
534
+ padding-left: 0;
535
+ margin: 0;
536
+ }
537
+
538
+ .drawer-toc .toc ul ul {
539
+ padding-left: 14px;
540
+ }
541
+
542
+ .drawer-toc .toc li {
543
+ margin-bottom: 6px;
544
+ }
545
+
546
+ .drawer-toc .toc a {
547
+ display: block;
548
+ font-size: 14px;
549
+ color: var(--text-muted);
550
+ padding: 6px 8px;
551
+ border-radius: 6px;
552
+ text-decoration: none;
553
+ }
554
+
555
+ .drawer-toc .toc a.active {
556
+ color: var(--primary);
557
+ background: var(--primary-light);
558
+ font-weight: 600;
559
+ }
560
+
561
+ /* ---------------- Citations & Tooltips ---------------- */
562
+ .cite-ref a {
563
+ scroll-margin-top: 120px;
564
+ outline: none;
565
+ }
566
+
567
+ .cite-ref a.cite-pulse {
568
+ animation: cite-return-pulse 2s cubic-bezier(0.2, 0, 0, 1) forwards;
569
+ }
570
+
571
+ @keyframes cite-return-pulse {
572
+ 0% {
573
+ background-color: #fef08a;
574
+ color: #854d0e;
575
+ box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.4), 0 0 12px rgba(245, 158, 11, 0.25);
576
+ border-color: #f59e0b;
577
+ }
578
+ 60% {
579
+ background-color: #fef9c3;
580
+ box-shadow: 0 0 0 6px rgba(245, 158, 11, 0.15);
581
+ }
582
+ 100% {
583
+ background-color: var(--primary-light);
584
+ color: var(--primary);
585
+ box-shadow: none;
586
+ border-color: var(--primary-border);
587
+ }
588
+ }
589
+
590
+ .cite-caller-block-highlight {
591
+ animation: cite-block-glow 1.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
592
+ }
593
+
594
+ @keyframes cite-block-glow {
595
+ 0% {
596
+ background-color: rgba(254, 240, 138, 0.4);
597
+ box-shadow: 0 0 0 8px rgba(254, 240, 138, 0.4);
598
+ border-radius: 6px;
599
+ }
600
+ 60% {
601
+ background-color: rgba(254, 240, 138, 0.15);
602
+ box-shadow: 0 0 0 8px rgba(254, 240, 138, 0.15);
603
+ }
604
+ 100% {
605
+ background-color: transparent;
606
+ box-shadow: 0 0 0 0 transparent;
607
+ }
608
+ }
609
+
610
+ @media (prefers-reduced-motion: reduce) {
611
+ html { scroll-behavior: auto !important; }
612
+ .cite-ref a.cite-pulse {
613
+ animation: none;
614
+ background-color: #fef08a;
615
+ transition: background-color 1s ease;
616
+ }
617
+ .cite-caller-block-highlight {
618
+ animation: none;
619
+ background-color: transparent;
620
+ }
621
+ .cite-return-chip {
622
+ transition: none;
623
+ }
624
+ }
625
+
626
+ /* ---------------- Floating Return Chip ---------------- */
627
+ .cite-return-chip {
628
+ position: fixed;
629
+ bottom: 28px;
630
+ left: 50%;
631
+ transform: translateX(-50%) translateY(100px);
632
+ z-index: 998;
633
+ display: flex;
634
+ align-items: center;
635
+ background: #0f172a;
636
+ color: #f8fafc;
637
+ padding: 4px 6px 4px 14px;
638
+ border-radius: 30px;
639
+ box-shadow: 0 10px 25px -5px rgba(15, 23, 42, 0.3), 0 8px 10px -6px rgba(15, 23, 42, 0.3);
640
+ border: 1px solid rgba(255, 255, 255, 0.15);
641
+ opacity: 0;
642
+ pointer-events: none;
643
+ transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
644
+ }
645
+
646
+ .cite-return-chip.visible {
647
+ transform: translateX(-50%) translateY(0);
648
+ opacity: 1;
649
+ pointer-events: auto;
650
+ }
651
+
652
+ .cite-return-btn {
653
+ background: transparent;
654
+ border: none;
655
+ color: #ffffff;
656
+ font-family: var(--font-sans);
657
+ font-size: 13px;
658
+ font-weight: 600;
659
+ display: flex;
660
+ align-items: center;
661
+ gap: 8px;
662
+ cursor: pointer;
663
+ padding: 6px 4px;
664
+ }
665
+
666
+ .cite-return-btn:hover .return-text {
667
+ text-decoration: underline;
668
+ }
669
+
670
+ .return-arrow {
671
+ font-size: 14px;
672
+ color: #38bdf8;
673
+ }
674
+
675
+ .return-badge {
676
+ font-family: var(--font-mono);
677
+ color: #38bdf8;
678
+ font-size: 12px;
679
+ }
680
+
681
+ .cite-return-dismiss {
682
+ background: transparent;
683
+ border: none;
684
+ color: #94a3b8;
685
+ font-size: 18px;
686
+ line-height: 1;
687
+ cursor: pointer;
688
+ padding: 6px 10px;
689
+ margin-left: 4px;
690
+ border-radius: 50%;
691
+ display: flex;
692
+ align-items: center;
693
+ justify-content: center;
694
+ }
695
+
696
+ .cite-return-dismiss:hover {
697
+ color: #ffffff;
698
+ background: rgba(255, 255, 255, 0.1);
699
+ }
700
+
701
+ .ref-backlink, .ref-backlinks {
702
+ font-family: var(--font-sans);
703
+ font-size: 12px;
704
+ margin-left: 8px;
705
+ display: inline-flex;
706
+ align-items: center;
707
+ flex-wrap: wrap;
708
+ gap: 4px;
709
+ vertical-align: middle;
710
+ }
711
+
712
+ .ref-occ-prefix {
713
+ color: var(--text-light);
714
+ font-size: 13px;
715
+ margin-right: 2px;
716
+ user-select: none;
717
+ }
718
+
719
+ .ref-occ-others {
720
+ display: inline-flex;
721
+ align-items: center;
722
+ flex-wrap: wrap;
723
+ gap: 4px;
724
+ }
725
+
726
+ .ref-occ-pill {
727
+ display: inline-flex;
728
+ align-items: center;
729
+ justify-content: center;
730
+ gap: 4px;
731
+ min-width: 24px;
732
+ height: 24px;
733
+ padding: 0 6px;
734
+ background: var(--primary-light);
735
+ border: 1px solid var(--primary-border);
736
+ border-radius: 6px;
737
+ font-size: 11px;
738
+ font-weight: 500;
739
+ color: var(--primary);
740
+ text-decoration: none;
741
+ position: relative;
742
+ transition: all 0.15s ease;
743
+ vertical-align: middle;
744
+ outline: none;
745
+ }
746
+
747
+ .ref-occ-pill:hover {
748
+ background: #e0f2fe;
749
+ border-color: #7dd3fc;
750
+ color: var(--primary-hover);
751
+ text-decoration: none;
752
+ }
753
+
754
+ .ref-occ-pill:focus-visible {
755
+ box-shadow: 0 0 0 2px var(--primary);
756
+ }
757
+
758
+ /* Touch target expansion (44x44px minimum for mobile compliance) */
759
+ .ref-occ-pill::before {
760
+ content: '';
761
+ position: absolute;
762
+ top: 50%;
763
+ left: 50%;
764
+ transform: translate(-50%, -50%);
765
+ min-width: 44px;
766
+ min-height: 44px;
767
+ }
768
+
769
+ /* Active Origin Pill (One-Click Return to calling text) */
770
+ .ref-occ-pill.ref-occ-active {
771
+ background: var(--primary);
772
+ color: #ffffff;
773
+ border-color: var(--primary-hover);
774
+ font-weight: 600;
775
+ padding: 0 8px;
776
+ box-shadow: 0 1px 4px rgba(2, 132, 199, 0.35);
777
+ animation: occ-active-bounce 0.4s cubic-bezier(0.16, 1, 0.3, 1);
778
+ }
779
+
780
+ @keyframes occ-active-bounce {
781
+ 0% { transform: scale(0.92); }
782
+ 60% { transform: scale(1.04); }
783
+ 100% { transform: scale(1); }
784
+ }
785
+
786
+ .ref-occ-pill.ref-occ-active:hover {
787
+ background: var(--primary-hover);
788
+ color: #ffffff;
789
+ }
790
+
791
+ .ref-occ-pill.ref-occ-active .pill-arrow {
792
+ font-size: 12px;
793
+ }
794
+
795
+ .ref-occ-pill.ref-occ-active .pill-sec {
796
+ max-width: 150px;
797
+ overflow: hidden;
798
+ text-overflow: ellipsis;
799
+ white-space: nowrap;
800
+ }
801
+
802
+ .ref-occ-pill.ref-occ-active .pill-occ {
803
+ opacity: 0.85;
804
+ font-size: 10px;
805
+ font-weight: normal;
806
+ }
807
+ .cite-ref {
808
+ font-size: 0.75em;
809
+ line-height: 0;
810
+ vertical-align: super;
811
+ margin: 0 1.5px;
812
+ }
813
+
814
+ .cite-ref a {
815
+ display: inline-flex;
816
+ align-items: center;
817
+ justify-content: center;
818
+ min-width: 1.35em;
819
+ height: 1.35em;
820
+ padding: 0 4px;
821
+ background: var(--primary-light);
822
+ color: var(--primary);
823
+ border: 1px solid var(--primary-border);
824
+ border-radius: 4px;
825
+ font-weight: 600;
826
+ font-family: var(--font-mono);
827
+ font-size: 0.85em;
828
+ text-decoration: none;
829
+ cursor: pointer;
830
+ transition: all 0.15s ease;
831
+ }
832
+
833
+ .cite-ref a:hover {
834
+ background: var(--primary);
835
+ color: #ffffff;
836
+ border-color: var(--primary);
837
+ box-shadow: 0 2px 4px rgba(2, 132, 199, 0.25);
838
+ text-decoration: none;
839
+ }
840
+
841
+ .cite-tooltip {
842
+ position: absolute;
843
+ z-index: 9999;
844
+ max-width: 440px;
845
+ min-width: 260px;
846
+ width: max-content;
847
+ padding: 12px 16px;
848
+ background: var(--bg-surface);
849
+ border: 1px solid var(--border-color);
850
+ border-radius: 8px;
851
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
852
+ font-size: 13px;
853
+ line-height: 1.5;
854
+ color: var(--text-main);
855
+ display: none;
856
+ opacity: 0;
857
+ pointer-events: none;
858
+ transition: opacity 0.15s ease;
859
+ }
860
+
861
+ .cite-tooltip.visible {
862
+ display: block;
863
+ pointer-events: auto;
864
+ }
865
+
866
+ .cite-tooltip.fade-in {
867
+ opacity: 1;
868
+ }
869
+
870
+ .cite-tooltip.fade-out {
871
+ opacity: 0;
872
+ }
873
+
874
+ .cite-header {
875
+ display: flex;
876
+ align-items: center;
877
+ justify-content: space-between;
878
+ margin-bottom: 6px;
879
+ gap: 8px;
880
+ }
881
+
882
+ .cite-tooltip .cite-label {
883
+ font-size: 11px;
884
+ font-weight: 700;
885
+ color: var(--text-muted);
886
+ text-transform: uppercase;
887
+ letter-spacing: 0.05em;
888
+ margin-bottom: 0;
889
+ }
890
+
891
+ .cite-jump-link {
892
+ font-size: 11px;
893
+ color: var(--primary);
894
+ text-decoration: none;
895
+ font-weight: 500;
896
+ white-space: nowrap;
897
+ }
898
+
899
+ .cite-jump-link:hover {
900
+ text-decoration: underline;
901
+ }
902
+
903
+ .cite-tail {
904
+ position: absolute;
905
+ top: -6px;
906
+ left: 50%;
907
+ transform: translateX(-50%);
908
+ width: 0;
909
+ height: 0;
910
+ border-left: 6px solid transparent;
911
+ border-right: 6px solid transparent;
912
+ border-bottom: 6px solid var(--border-color);
913
+ }
914
+
915
+ .cite-tail::after {
916
+ content: '';
917
+ position: absolute;
918
+ top: 1px;
919
+ left: -5px;
920
+ width: 0;
921
+ height: 0;
922
+ border-left: 5px solid transparent;
923
+ border-right: 5px solid transparent;
924
+ border-bottom: 5px solid var(--bg-surface);
925
+ }
926
+
927
+ .cite-tail.flip {
928
+ top: auto;
929
+ bottom: -6px;
930
+ border-bottom: none;
931
+ border-top: 6px solid var(--border-color);
932
+ }
933
+
934
+ .cite-tail.flip::after {
935
+ top: auto;
936
+ bottom: 1px;
937
+ border-bottom: none;
938
+ border-top: 5px solid var(--bg-surface);
939
+ }
940
+
941
+ .cite-tooltip a.ref-link {
942
+ color: var(--primary);
943
+ text-decoration: none;
944
+ font-weight: 500;
945
+ }
946
+
947
+ .cite-tooltip a.ref-link:hover {
948
+ text-decoration: underline;
949
+ }
950
+
951
+ /* ---------------- Bibliography / References Section ---------------- */
952
+ .references-container {
953
+ margin-top: 1.2em;
954
+ display: flex;
955
+ flex-direction: column;
956
+ gap: 12px;
957
+ }
958
+
959
+ .ref-item {
960
+ display: flex;
961
+ align-items: flex-start;
962
+ gap: 10px;
963
+ font-size: 14px;
964
+ line-height: 1.55;
965
+ padding: 10px 14px;
966
+ border-radius: 8px;
967
+ background: #fbfcfd;
968
+ border: 1px solid var(--border-subtle);
969
+ scroll-margin-top: 80px;
970
+ transition: background-color 0.3s ease;
971
+ }
972
+
973
+ .ref-item:hover {
974
+ border-color: var(--border-color);
975
+ }
976
+
977
+ .ref-item:target {
978
+ animation: ref-target-pulse 2.2s ease-out;
979
+ border-color: var(--primary-border);
980
+ }
981
+
982
+ @keyframes ref-target-pulse {
983
+ 0% { background-color: #fef08a; }
984
+ 60% { background-color: #fef9c3; }
985
+ 100% { background-color: #fbfcfd; }
986
+ }
987
+
988
+ .ref-num {
989
+ font-family: var(--font-mono);
990
+ font-weight: 600;
991
+ color: var(--primary);
992
+ font-size: 13px;
993
+ flex-shrink: 0;
994
+ padding-top: 1px;
995
+ }
996
+
997
+ .ref-body {
998
+ flex: 1;
999
+ color: #334155;
1000
+ word-break: break-word;
1001
+ }
1002
+
1003
+ /* ---------------- Responsive Styles ---------------- */
1004
+ @media (max-width: 1024px) {
1005
+ .report-sidebar {
1006
+ display: none;
1007
+ }
1008
+ .report-wrapper {
1009
+ padding: 16px;
1010
+ }
1011
+ .report-header-card, .markdown-body {
1012
+ padding: 24px 20px;
1013
+ }
1014
+ .mobile-toc-btn {
1015
+ display: inline-flex;
1016
+ }
1017
+ .cite-return-chip {
1018
+ left: auto;
1019
+ transform: translateY(100px);
1020
+ right: 20px;
1021
+ bottom: calc(76px + env(safe-area-inset-bottom, 0px));
1022
+ max-width: calc(100vw - 40px);
1023
+ }
1024
+ .cite-return-chip.visible {
1025
+ transform: translateY(0);
1026
+ }
1027
+ }
1028
+
1029
+ @media (max-width: 640px) {
1030
+ .report-title {
1031
+ font-size: 1.5rem;
1032
+ }
1033
+ .meta-grid {
1034
+ grid-template-columns: 1fr;
1035
+ }
1036
+ .cite-tooltip {
1037
+ max-width: calc(100vw - 32px);
1038
+ }
1039
+ }
1040
+
1041
+ /* ---------------- Print Styles ---------------- */
1042
+ @media print {
1043
+ @page { margin: 1.5cm; }
1044
+ body {
1045
+ background: #ffffff !important;
1046
+ color: #000000 !important;
1047
+ font-size: 11pt;
1048
+ }
1049
+ .report-sidebar,
1050
+ .mobile-toc-btn,
1051
+ .mobile-toc-backdrop,
1052
+ .mobile-toc-drawer,
1053
+ #cite-tooltip,
1054
+ #cite-return-chip,
1055
+ .ref-backlink,
1056
+ .ref-backlinks,
1057
+ .ref-occ-pill {
1058
+ display: none !important;
1059
+ }
1060
+ .report-wrapper {
1061
+ max-width: 100% !important;
1062
+ padding: 0 !important;
1063
+ }
1064
+ .report-header-card, .markdown-body {
1065
+ border: none !important;
1066
+ box-shadow: none !important;
1067
+ padding: 0 !important;
1068
+ margin-bottom: 24px !important;
1069
+ }
1070
+ a {
1071
+ color: #000000 !important;
1072
+ text-decoration: underline !important;
1073
+ }
1074
+ .ref-item {
1075
+ background: transparent !important;
1076
+ border: none !important;
1077
+ padding: 4px 0 !important;
1078
+ break-inside: avoid;
1079
+ page-break-inside: avoid;
1080
+ }
1081
+ table, pre, blockquote {
1082
+ break-inside: avoid;
1083
+ page-break-inside: avoid;
1084
+ }
1085
+ h1, h2, h3 {
1086
+ break-after: avoid;
1087
+ page-break-after: avoid;
1088
+ }
1089
+ }
1090
+ """.strip()
1091
+
1092
+ SVG_DNA_ICON = """<svg class="brand-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M4.5 3a1.5 1.5 0 0 0-1.5 1.5v.38c0 .4.16.78.44 1.06L7.38 10l-3.94 4.06a1.5 1.5 0 0 0-.44 1.06v.38a1.5 1.5 0 0 0 1.5 1.5h.38c.4 0 .78-.16 1.06-.44L10 12.62l3.94 4.06c.28.28.66.44 1.06.44h.38a1.5 1.5 0 0 0 1.5-1.5v-.38c0-.4-.16-.78-.44-1.06L12.5 10l4.06-3.94c.28-.28.44-.66.44-1.06V4.5A1.5 1.5 0 0 0 15.5 3h-.38c-.4 0-.78.16-1.06.44L10 7.38 6.06 3.44A1.5 1.5 0 0 0 5.02 3H4.5zM12 2a1 1 0 0 1 1 1v1.17l2.29-2.29A2.5 2.5 0 0 1 17.06 1H18.5A2.5 2.5 0 0 1 21 3.5v1.44c0 .66-.26 1.3-.73 1.77L16.41 10l3.86 3.86c.47.47.73 1.11.73 1.77v1.87a2.5 2.5 0 0 1-2.5 2.5h-1.44a2.5 2.5 0 0 1-1.77-.73L13 16.9V21a1 1 0 1 1-2 0v-4.1l-2.29 2.37a2.5 2.5 0 0 1-1.77.73H5.5A2.5 2.5 0 0 1 3 17.5v-1.87c0-.66.26-1.3.73-1.77L7.59 10 3.73 6.14A2.5 2.5 0 0 1 3 4.37V2.5A2.5 2.5 0 0 1 5.5 0h1.44c.66 0 1.3.26 1.77.73L11 3.03V2a1 1 0 0 1 1-1z"/></svg>"""
1093
+ SVG_LIST_ICON = """<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>"""
1094
+
1095
+ CLIENT_SCRIPT = r"""
1096
+ (function() {
1097
+ var contentEl = document.getElementById('content');
1098
+ if (!contentEl) return;
1099
+
1100
+ function esc(s) {
1101
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1102
+ }
1103
+
1104
+ function formatReferenceHtml(str) {
1105
+ var placeholders = [];
1106
+ function pushPh(h) { var idx = placeholders.length; placeholders.push(h); return '%%%PH_' + idx + '%%%'; }
1107
+
1108
+ // 1. DOIs
1109
+ var s = str.replace(/\bdoi:\s*(10\.\d{4,9}\/[^\s;,]+)/gi, function(_, doi) {
1110
+ var cleanDoi = doi.replace(/[.,;:!?)\]]+$/, '');
1111
+ return pushPh('<a class="ref-link" href="https://doi.org/' + esc(cleanDoi) + '" target="_blank" rel="noopener noreferrer">doi:' + esc(cleanDoi) + '</a>');
1112
+ });
1113
+
1114
+ // 2. PMIDs
1115
+ s = s.replace(/\bPMID:\s*(\d+)/gi, function(_, id) {
1116
+ return pushPh('<a class="ref-link" href="https://pubmed.ncbi.nlm.nih.gov/' + id + '/" target="_blank" rel="noopener noreferrer">PMID: ' + id + '</a>');
1117
+ });
1118
+
1119
+ // 3. ClinicalTrials.gov NCT IDs
1120
+ s = s.replace(/\b(NCT\d{8})\b/gi, function(_, nct) {
1121
+ return pushPh('<a class="ref-link" href="https://clinicaltrials.gov/study/' + nct + '" target="_blank" rel="noopener noreferrer">' + nct + '</a>');
1122
+ });
1123
+
1124
+ // 4. URLs
1125
+ s = s.replace(/(https?:\/\/[^\s<>"]+)/g, function(url) {
1126
+ var cleanUrl = url.replace(/[.,;:!?)\]]+$/, '');
1127
+ var trailing = url.slice(cleanUrl.length);
1128
+ return pushPh('<a class="ref-link" href="' + esc(cleanUrl) + '" target="_blank" rel="noopener noreferrer">' + esc(cleanUrl) + '</a>') + esc(trailing);
1129
+ });
1130
+
1131
+ s = esc(s).replace(/%%%PH_(\d+)%%%/g, function(_, idx) {
1132
+ return placeholders[parseInt(idx, 10)];
1133
+ });
1134
+ return s;
1135
+ }
1136
+
1137
+ // ---- 1. Process Bibliography ----
1138
+ var headings = contentEl.querySelectorAll('h1, h2, h3, h4, h5, h6');
1139
+ var refHeading = null;
1140
+ for (var i = 0; i < headings.length; i++) {
1141
+ if (/^(?:\d+[\.\s]+)?(?:references?|bibliography|literature cited|citations|参考文献)[\s:]*$/i.test(headings[i].textContent.trim())) {
1142
+ refHeading = headings[i];
1143
+ break;
1144
+ }
1145
+ }
1146
+
1147
+ var refsMap = new Map();
1148
+ var refElements = [];
1149
+
1150
+ if (refHeading) {
1151
+ var refLevel = parseInt(refHeading.tagName.charAt(1), 10);
1152
+ var next = refHeading.nextElementSibling;
1153
+ while (next) {
1154
+ if (/^H[1-6]$/i.test(next.tagName)) {
1155
+ if (parseInt(next.tagName.charAt(1), 10) <= refLevel) break;
1156
+ }
1157
+ refElements.push(next);
1158
+ next = next.nextElementSibling;
1159
+ }
1160
+
1161
+ var rawEntries = [];
1162
+ refElements.forEach(function(el) {
1163
+ if (el.tagName === 'OL' || el.tagName === 'UL') {
1164
+ el.querySelectorAll('li').forEach(function(li, idx) {
1165
+ var text = li.textContent.trim();
1166
+ var m = text.match(/^\[(\d+)\]\s*([\s\S]*)/);
1167
+ if (m) {
1168
+ rawEntries.push({ num: parseInt(m[1], 10), body: m[2].trim() });
1169
+ } else {
1170
+ rawEntries.push({ num: idx + 1, body: text });
1171
+ }
1172
+ });
1173
+ } else {
1174
+ var text = el.textContent;
1175
+ var regex = /(?:^|\n)\s*\[(\d+)\]\s*([\s\S]*?)(?=(?:\n\s*\[\d+\]|$))/g;
1176
+ var match, found = false;
1177
+ while ((match = regex.exec(text)) !== null) {
1178
+ found = true;
1179
+ rawEntries.push({ num: parseInt(match[1], 10), body: match[2].trim() });
1180
+ }
1181
+ if (!found && text.trim()) {
1182
+ var m = text.trim().match(/^\[(\d+)\]\s*([\s\S]*)/);
1183
+ if (m) rawEntries.push({ num: parseInt(m[1], 10), body: m[2].trim() });
1184
+ }
1185
+ }
1186
+ });
1187
+
1188
+ if (rawEntries.length > 0) {
1189
+ var container = document.createElement('div');
1190
+ container.className = 'references-container';
1191
+ rawEntries.forEach(function(item) {
1192
+ refsMap.set(item.num, item.body);
1193
+ var div = document.createElement('div');
1194
+ div.className = 'ref-item';
1195
+ div.id = 'ref-' + item.num;
1196
+ div.innerHTML = '<span class="ref-num">[' + item.num + ']</span><div class="ref-body">' + formatReferenceHtml(item.body) + '</div>';
1197
+ container.appendChild(div);
1198
+ });
1199
+ refElements[0].parentNode.insertBefore(container, refElements[0]);
1200
+ refElements.forEach(function(e) { e.remove(); });
1201
+ }
1202
+ }
1203
+
1204
+ // ---- 2. In-Text Citation Replacement & Caller Identity Tracking ----
1205
+ var forbidden = new Set(['CODE', 'PRE', 'A', 'SCRIPT', 'STYLE', 'TEXTAREA', 'NAV', 'BUTTON']);
1206
+ function isForbidden(n) {
1207
+ var p = n.parentElement;
1208
+ while (p && p !== contentEl) {
1209
+ if (forbidden.has(p.tagName) || (p.classList && p.classList.contains('references-container'))) return true;
1210
+ p = p.parentElement;
1211
+ }
1212
+ return false;
1213
+ }
1214
+
1215
+ var walker = document.createTreeWalker(contentEl, NodeFilter.SHOW_TEXT);
1216
+ var textNodes = [];
1217
+ while (walker.nextNode()) {
1218
+ if (!isForbidden(walker.currentNode)) {
1219
+ textNodes.push(walker.currentNode);
1220
+ }
1221
+ }
1222
+
1223
+ var citeRegex = /\[(\d+(?:\s*[-–—]\s*\d+)?(?:\s*[,,]\s*\d+(?:\s*[-–—]\s*\d+)?)*)\]/g;
1224
+ var refCallMap = new Map();
1225
+
1226
+ textNodes.forEach(function(node) {
1227
+ var text = node.textContent;
1228
+ if (!citeRegex.test(text)) return;
1229
+ citeRegex.lastIndex = 0;
1230
+
1231
+ var frag = document.createDocumentFragment();
1232
+ var lastIdx = 0, m;
1233
+
1234
+ while ((m = citeRegex.exec(text)) !== null) {
1235
+ var nums = [];
1236
+ m[1].split(/[,,]/).forEach(function(part) {
1237
+ part = part.trim();
1238
+ var rm = part.match(/^(\d+)\s*[-–—]\s*(\d+)$/);
1239
+ if (rm) {
1240
+ var start = parseInt(rm[1], 10);
1241
+ var end = parseInt(rm[2], 10);
1242
+ for (var k = start; k <= end; k++) nums.push(k);
1243
+ } else {
1244
+ var single = parseInt(part, 10);
1245
+ if (!isNaN(single)) nums.push(single);
1246
+ }
1247
+ });
1248
+
1249
+ if (refsMap.size > 0 && !nums.some(function(n) { return refsMap.has(n); })) {
1250
+ continue;
1251
+ }
1252
+
1253
+ if (m.index > lastIdx) {
1254
+ frag.appendChild(document.createTextNode(text.slice(lastIdx, m.index)));
1255
+ }
1256
+
1257
+ var sup = document.createElement('sup');
1258
+ sup.className = 'cite-ref';
1259
+ nums.forEach(function(num, idx) {
1260
+ if (idx > 0) sup.appendChild(document.createTextNode(', '));
1261
+ var a = document.createElement('a');
1262
+ a.textContent = num;
1263
+
1264
+ var existingCalls = refCallMap.get(num) || [];
1265
+ var callId = 'cite-call-' + num + '-' + (existingCalls.length + 1);
1266
+ existingCalls.push(callId);
1267
+ refCallMap.set(num, existingCalls);
1268
+
1269
+ a.id = callId;
1270
+ a.setAttribute('data-call-id', callId);
1271
+ a.setAttribute('data-ref', num);
1272
+ a.href = '#ref-' + num;
1273
+ sup.appendChild(a);
1274
+ });
1275
+ frag.appendChild(sup);
1276
+ lastIdx = citeRegex.lastIndex;
1277
+ }
1278
+
1279
+ if (lastIdx === 0) return;
1280
+
1281
+ if (lastIdx < text.length) {
1282
+ frag.appendChild(document.createTextNode(text.slice(lastIdx)));
1283
+ }
1284
+ node.parentNode.replaceChild(frag, node);
1285
+ });
1286
+
1287
+ // ---- 3. Single-Pass Document Indexer: Map Citations to Headings & Excerpts ----
1288
+ var callMetaMap = new Map();
1289
+ var headings = Array.from(contentEl.querySelectorAll('h1, h2, h3, h4, h5, h6'));
1290
+ var mainHeadings = headings.filter(function(h) {
1291
+ return !h.closest('.references-container') && h !== refHeading;
1292
+ });
1293
+ var allCiteLinks = Array.from(contentEl.querySelectorAll('.cite-ref a'));
1294
+
1295
+ function extractSentenceSnippet(blockEl, targetLink) {
1296
+ if (!blockEl) return '';
1297
+ var fullText = blockEl.textContent.replace(/\s+/g, ' ').trim();
1298
+ var linkText = targetLink.textContent.trim();
1299
+ var linkIdx = fullText.indexOf('[' + linkText + ']');
1300
+ if (linkIdx === -1) linkIdx = fullText.indexOf(linkText);
1301
+ if (linkIdx === -1) return fullText.slice(0, 80) + '...';
1302
+ var start = Math.max(0, linkIdx - 40);
1303
+ var end = Math.min(fullText.length, linkIdx + 40);
1304
+ var snippet = fullText.slice(start, end).trim();
1305
+ if (start > 0) snippet = '…' + snippet;
1306
+ if (end < fullText.length) snippet = snippet + '…';
1307
+ return snippet;
1308
+ }
1309
+
1310
+ var hIdx = 0;
1311
+ var currentHeading = { id: '', text: 'Overview', cleanText: 'Overview', shortSec: 'Overview' };
1312
+
1313
+ allCiteLinks.forEach(function(link) {
1314
+ while (hIdx < mainHeadings.length &&
1315
+ (mainHeadings[hIdx].compareDocumentPosition(link) & Node.DOCUMENT_POSITION_FOLLOWING)) {
1316
+ var rawTitle = mainHeadings[hIdx].textContent.trim();
1317
+ var cleanTitle = rawTitle.replace(/^[#\d\.\s]+/, '') || rawTitle || 'Section';
1318
+ var numMatch = rawTitle.match(/^(\d+(?:\.\d+)*)/);
1319
+ var shortSec = numMatch ? ('§' + numMatch[1]) : (cleanTitle.length > 16 ? cleanTitle.slice(0, 14) + '…' : cleanTitle);
1320
+ currentHeading = {
1321
+ id: mainHeadings[hIdx].id,
1322
+ text: rawTitle,
1323
+ cleanText: cleanTitle,
1324
+ shortSec: shortSec
1325
+ };
1326
+ hIdx++;
1327
+ }
1328
+
1329
+ var block = link.closest('p, li, td, th, blockquote') || link.parentElement;
1330
+ var snippet = extractSentenceSnippet(block, link);
1331
+
1332
+ callMetaMap.set(link.id, {
1333
+ callId: link.id,
1334
+ refNum: parseInt(link.getAttribute('data-ref'), 10),
1335
+ headingId: currentHeading.id,
1336
+ headingText: currentHeading.text,
1337
+ shortSec: currentHeading.shortSec,
1338
+ snippet: snippet
1339
+ });
1340
+ });
1341
+
1342
+ // ---- 4. Intelligent In-Situ Reference Backlinks with Unified Active Origin Pill ----
1343
+ function renderReferenceBacklinks(num, activeCallId) {
1344
+ var item = document.getElementById('ref-' + num);
1345
+ if (!item) return;
1346
+ var bodyEl = item.querySelector('.ref-body');
1347
+ if (!bodyEl) return;
1348
+ var calls = refCallMap.get(num);
1349
+ if (!calls || calls.length === 0) return;
1350
+
1351
+ var existingBl = bodyEl.querySelector('.ref-backlinks');
1352
+ if (existingBl) existingBl.remove();
1353
+
1354
+ var span = document.createElement('span');
1355
+ span.className = 'ref-backlinks';
1356
+
1357
+ if (calls.length === 1) {
1358
+ var cid = calls[0];
1359
+ var meta = callMetaMap.get(cid) || { headingText: 'Section', shortSec: 'Section', snippet: '' };
1360
+ var isActive = (activeCallId === cid);
1361
+ var bl = document.createElement('a');
1362
+ bl.className = 'ref-occ-pill' + (isActive ? ' ref-occ-active' : '');
1363
+ bl.href = '#' + cid;
1364
+ bl.setAttribute('data-call-id', cid);
1365
+ bl.setAttribute('aria-label', (isActive ? 'Return to reading in ' : 'Jump to citation in ') + meta.headingText + (meta.snippet ? ': ' + meta.snippet : ''));
1366
+ bl.title = (isActive ? 'Return to text in ' : 'Citation in ') + meta.headingText + (meta.snippet ? ':\n"' + meta.snippet + '"' : '');
1367
+ if (isActive) bl.setAttribute('aria-current', 'location');
1368
+ bl.innerHTML = '<span class="pill-arrow" aria-hidden="true">↩</span> <span class="pill-sec">' + esc(meta.shortSec) + '</span>';
1369
+ span.appendChild(bl);
1370
+ } else {
1371
+ var activeIdx = calls.indexOf(activeCallId);
1372
+ if (activeIdx !== -1) {
1373
+ var activeMeta = callMetaMap.get(activeCallId) || { headingText: 'Section', shortSec: 'Section', snippet: '' };
1374
+ var actPill = document.createElement('a');
1375
+ actPill.className = 'ref-occ-pill ref-occ-active';
1376
+ actPill.href = '#' + activeCallId;
1377
+ actPill.setAttribute('data-call-id', activeCallId);
1378
+ actPill.setAttribute('aria-current', 'location');
1379
+ actPill.setAttribute('aria-label', 'Return to reading in ' + activeMeta.headingText + ' (occurrence ' + (activeIdx + 1) + ' of ' + calls.length + '): ' + activeMeta.snippet);
1380
+ actPill.title = 'Return to reading in ' + activeMeta.headingText + ' (occurrence ' + (activeIdx + 1) + '):\n"' + activeMeta.snippet + '"';
1381
+ actPill.innerHTML = '<span class="pill-arrow" aria-hidden="true">↩</span> <span class="pill-sec">' + esc(activeMeta.shortSec) + '</span> <span class="pill-occ">(' + (activeIdx + 1) + ')</span>';
1382
+ span.appendChild(actPill);
1383
+
1384
+ var othersWrap = document.createElement('span');
1385
+ othersWrap.className = 'ref-occ-others';
1386
+ othersWrap.setAttribute('aria-label', 'Other occurrences in report:');
1387
+ calls.forEach(function(cid, idx) {
1388
+ if (idx === activeIdx) return;
1389
+ var m = callMetaMap.get(cid) || { headingText: 'Section', shortSec: 'Section', snippet: '' };
1390
+ var link = document.createElement('a');
1391
+ link.className = 'ref-occ-pill';
1392
+ link.href = '#' + cid;
1393
+ link.setAttribute('data-call-id', cid);
1394
+ link.setAttribute('aria-label', 'Occurrence ' + (idx + 1) + ' in ' + m.headingText + ': ' + m.snippet);
1395
+ link.title = 'Occurrence ' + (idx + 1) + ' in ' + m.headingText + (m.snippet ? ':\n"' + m.snippet + '"' : '');
1396
+ link.textContent = (idx + 1);
1397
+ othersWrap.appendChild(link);
1398
+ });
1399
+ span.appendChild(othersWrap);
1400
+ } else {
1401
+ var prefix = document.createElement('span');
1402
+ prefix.className = 'ref-occ-prefix';
1403
+ prefix.innerHTML = '<span aria-hidden="true">↩</span>';
1404
+ span.appendChild(prefix);
1405
+
1406
+ calls.forEach(function(cid, idx) {
1407
+ var m = callMetaMap.get(cid) || { headingText: 'Section', shortSec: 'Section', snippet: '' };
1408
+ var link = document.createElement('a');
1409
+ link.className = 'ref-occ-pill';
1410
+ link.href = '#' + cid;
1411
+ link.setAttribute('data-call-id', cid);
1412
+ link.setAttribute('aria-label', 'Occurrence ' + (idx + 1) + ' in ' + m.headingText + ': ' + m.snippet);
1413
+ link.title = 'Occurrence ' + (idx + 1) + ' in ' + m.headingText + (m.snippet ? ':\n"' + m.snippet + '"' : '');
1414
+ link.textContent = (idx + 1);
1415
+ span.appendChild(link);
1416
+ });
1417
+ }
1418
+ }
1419
+ bodyEl.appendChild(span);
1420
+ }
1421
+
1422
+ refsMap.forEach(function(_, num) {
1423
+ renderReferenceBacklinks(num, null);
1424
+ });
1425
+
1426
+ // ---- 3. Interactive Singleton Tooltip Engine ----
1427
+ var tooltip = document.getElementById('cite-tooltip');
1428
+ var tooltipTail = tooltip ? tooltip.querySelector('.cite-tail') : null;
1429
+ var tooltipBody = tooltip ? tooltip.querySelector('.cite-body') : null;
1430
+ var tooltipLabel = tooltip ? tooltip.querySelector('.cite-label') : null;
1431
+ var tooltipJump = tooltip ? tooltip.querySelector('.cite-jump-link') : null;
1432
+ var activeTrigger = null;
1433
+ var showTimer = null, hideTimer = null;
1434
+
1435
+ function showTooltip(triggerEl) {
1436
+ if (!tooltip || !tooltipBody) return;
1437
+ clearTimeout(hideTimer); hideTimer = null;
1438
+ var num = triggerEl.getAttribute('data-ref');
1439
+ var refText = refsMap.get(parseInt(num, 10));
1440
+ if (!refText) return;
1441
+
1442
+ if (tooltipLabel) tooltipLabel.textContent = 'Reference [' + num + ']';
1443
+ if (tooltipJump) tooltipJump.href = '#ref-' + num;
1444
+ tooltipBody.innerHTML = formatReferenceHtml(refText);
1445
+
1446
+ tooltip.style.left = '0px';
1447
+ tooltip.style.top = '0px';
1448
+ tooltip.classList.add('visible');
1449
+ tooltip.classList.remove('fade-out');
1450
+
1451
+ var r = triggerEl.getBoundingClientRect();
1452
+ var tr = tooltip.getBoundingClientRect();
1453
+ var sy = window.scrollY || document.documentElement.scrollTop;
1454
+ var sx = window.scrollX || document.documentElement.scrollLeft;
1455
+ var clientW = document.documentElement.clientWidth;
1456
+
1457
+ var left = r.left + sx + (r.width / 2) - (tr.width / 2);
1458
+ if (left < sx + 12) left = sx + 12;
1459
+ if (left + tr.width > sx + clientW - 12) left = sx + clientW - tr.width - 12;
1460
+
1461
+ var top = r.bottom + sy + 6;
1462
+ var above = false;
1463
+ if (top + tr.height > window.innerHeight + sy - 12) {
1464
+ top = r.top + sy - tr.height - 6;
1465
+ above = true;
1466
+ }
1467
+ if (above && top < sy + 6) top = sy + 6;
1468
+
1469
+ if (tooltipTail) {
1470
+ var tailLeft = (r.left + sx + r.width / 2) - left;
1471
+ tailLeft = Math.max(14, Math.min(tr.width - 14, tailLeft));
1472
+ tooltipTail.style.left = tailLeft + 'px';
1473
+ tooltipTail.style.transform = 'none';
1474
+ if (above) tooltipTail.classList.add('flip');
1475
+ else tooltipTail.classList.remove('flip');
1476
+ }
1477
+
1478
+ tooltip.style.left = left + 'px';
1479
+ tooltip.style.top = top + 'px';
1480
+ requestAnimationFrame(function() { tooltip.classList.add('fade-in'); });
1481
+ activeTrigger = triggerEl;
1482
+ }
1483
+
1484
+ function hideTooltip() {
1485
+ if (!tooltip) return;
1486
+ tooltip.classList.remove('fade-in');
1487
+ tooltip.classList.add('fade-out');
1488
+ setTimeout(function() {
1489
+ if (tooltip.classList.contains('fade-out')) {
1490
+ tooltip.classList.remove('visible', 'fade-out');
1491
+ activeTrigger = null;
1492
+ }
1493
+ }, 150);
1494
+ }
1495
+
1496
+ document.addEventListener('mouseenter', function(e) {
1497
+ var a = e.target.closest('.cite-ref a');
1498
+ if (!a) return;
1499
+ clearTimeout(hideTimer);
1500
+ clearTimeout(showTimer);
1501
+ showTimer = setTimeout(function() { showTooltip(a); }, 200);
1502
+ }, true);
1503
+
1504
+ document.addEventListener('mouseleave', function(e) {
1505
+ var a = e.target.closest('.cite-ref a');
1506
+ if (!a) return;
1507
+ clearTimeout(showTimer);
1508
+ hideTimer = setTimeout(hideTooltip, 220);
1509
+ }, true);
1510
+
1511
+ if (tooltip) {
1512
+ tooltip.addEventListener('mouseenter', function() {
1513
+ clearTimeout(hideTimer);
1514
+ });
1515
+ tooltip.addEventListener('mouseleave', function() {
1516
+ hideTimer = setTimeout(hideTooltip, 220);
1517
+ });
1518
+ }
1519
+
1520
+ // ---- 5. Floating Return Chip & Reorientation Engine ----
1521
+ var chipEl = document.getElementById('cite-return-chip');
1522
+ var chipBtn = document.getElementById('cite-return-btn');
1523
+ var chipBadge = document.getElementById('cite-return-badge');
1524
+ var chipDismiss = document.getElementById('cite-return-dismiss');
1525
+ var activeReturnTarget = null;
1526
+
1527
+ function showReturnChip(callId, refNum) {
1528
+ if (!chipEl) return;
1529
+ activeReturnTarget = callId;
1530
+ var meta = callMetaMap.get(callId);
1531
+ var labelText = 'Return to text';
1532
+ if (meta && meta.shortSec) {
1533
+ labelText = 'Return to ' + meta.shortSec;
1534
+ }
1535
+ var returnTextEl = chipBtn ? chipBtn.querySelector('.return-text') : null;
1536
+ if (returnTextEl) returnTextEl.textContent = labelText;
1537
+ if (chipBadge) chipBadge.textContent = '[' + refNum + ']';
1538
+ chipEl.classList.add('visible');
1539
+ chipEl.setAttribute('aria-hidden', 'false');
1540
+ }
1541
+
1542
+ function hideReturnChip() {
1543
+ if (!chipEl) return;
1544
+ chipEl.classList.remove('visible');
1545
+ chipEl.setAttribute('aria-hidden', 'true');
1546
+ activeReturnTarget = null;
1547
+ }
1548
+
1549
+ function triggerPulse(el) {
1550
+ el.classList.remove('cite-pulse');
1551
+ void el.offsetWidth;
1552
+ el.classList.add('cite-pulse');
1553
+ }
1554
+
1555
+ function triggerBlockGlow(el) {
1556
+ el.classList.remove('cite-caller-block-highlight');
1557
+ void el.offsetWidth;
1558
+ el.classList.add('cite-caller-block-highlight');
1559
+ setTimeout(function() {
1560
+ el.classList.remove('cite-caller-block-highlight');
1561
+ }, 2000);
1562
+ }
1563
+
1564
+ function jumpToCallingText(callId) {
1565
+ var el = document.getElementById(callId);
1566
+ if (!el) return;
1567
+ hideReturnChip();
1568
+
1569
+ var parentDetails = el.closest('details');
1570
+ if (parentDetails) parentDetails.open = true;
1571
+
1572
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
1573
+ triggerPulse(el);
1574
+
1575
+ var parentBlock = el.closest('p, li, td, th, blockquote') || el.parentElement;
1576
+ if (parentBlock) triggerBlockGlow(parentBlock);
1577
+
1578
+ el.setAttribute('tabindex', '-1');
1579
+ el.focus({ preventScroll: true });
1580
+
1581
+ var refNum = el.getAttribute('data-ref');
1582
+ if (refNum) {
1583
+ setTimeout(function() {
1584
+ renderReferenceBacklinks(parseInt(refNum, 10), null);
1585
+ }, 600);
1586
+ }
1587
+
1588
+ if (history.state && history.state.citeOrigin === callId) {
1589
+ history.back();
1590
+ } else {
1591
+ history.replaceState(null, '', window.location.pathname + window.location.search);
1592
+ }
1593
+ }
1594
+
1595
+ document.addEventListener('click', function(e) {
1596
+ var a = e.target.closest('.cite-ref a');
1597
+ if (a) {
1598
+ if (e.pointerType === 'touch' && (!activeTrigger || activeTrigger !== a || !tooltip.classList.contains('fade-in'))) {
1599
+ e.preventDefault();
1600
+ showTooltip(a);
1601
+ } else {
1602
+ e.preventDefault();
1603
+ var callId = a.id;
1604
+ var refNum = a.getAttribute('data-ref');
1605
+ var meta = callMetaMap.get(callId);
1606
+ history.replaceState({ citeOrigin: callId }, '');
1607
+ history.pushState({ citeOrigin: callId, refId: 'ref-' + refNum, refNum: refNum, headingText: meta ? meta.headingText : '' }, '', '#ref-' + refNum);
1608
+ renderReferenceBacklinks(parseInt(refNum, 10), callId);
1609
+ var targetRef = document.getElementById('ref-' + refNum);
1610
+ if (targetRef) targetRef.scrollIntoView({ behavior: 'smooth' });
1611
+ showReturnChip(callId, refNum);
1612
+ hideTooltip();
1613
+ }
1614
+ return;
1615
+ }
1616
+
1617
+ var jumpLink = e.target.closest('.cite-jump-link');
1618
+ if (jumpLink && activeTrigger) {
1619
+ e.preventDefault();
1620
+ var jCallId = activeTrigger.id;
1621
+ var jRefNum = activeTrigger.getAttribute('data-ref');
1622
+ var jMeta = callMetaMap.get(jCallId);
1623
+ history.replaceState({ citeOrigin: jCallId }, '');
1624
+ history.pushState({ citeOrigin: jCallId, refId: 'ref-' + jRefNum, refNum: jRefNum, headingText: jMeta ? jMeta.headingText : '' }, '', '#ref-' + jRefNum);
1625
+ renderReferenceBacklinks(parseInt(jRefNum, 10), jCallId);
1626
+ var targetRefJump = document.getElementById('ref-' + jRefNum);
1627
+ if (targetRefJump) targetRefJump.scrollIntoView({ behavior: 'smooth' });
1628
+ showReturnChip(jCallId, jRefNum);
1629
+ hideTooltip();
1630
+ return;
1631
+ }
1632
+
1633
+ var backlink = e.target.closest('.ref-occ-pill, .ref-backlink');
1634
+ if (backlink) {
1635
+ e.preventDefault();
1636
+ var targetId = backlink.getAttribute('data-call-id');
1637
+ if (targetId) {
1638
+ jumpToCallingText(targetId);
1639
+ }
1640
+ return;
1641
+ }
1642
+
1643
+ if (!e.target.closest('#cite-tooltip')) {
1644
+ hideTooltip();
1645
+ }
1646
+ });
1647
+
1648
+ if (chipBtn) {
1649
+ chipBtn.addEventListener('click', function() {
1650
+ if (activeReturnTarget) jumpToCallingText(activeReturnTarget);
1651
+ });
1652
+ }
1653
+
1654
+ if (chipDismiss) {
1655
+ chipDismiss.addEventListener('click', hideReturnChip);
1656
+ }
1657
+
1658
+ window.addEventListener('popstate', function(e) {
1659
+ if (e.state && e.state.citeOrigin) {
1660
+ if (e.state.refId && window.location.hash === '#' + e.state.refId) {
1661
+ renderReferenceBacklinks(parseInt(e.state.refNum, 10), e.state.citeOrigin);
1662
+ showReturnChip(e.state.citeOrigin, e.state.refNum || '');
1663
+ } else {
1664
+ var el = document.getElementById(e.state.citeOrigin);
1665
+ if (el) {
1666
+ var parentDetails = el.closest('details');
1667
+ if (parentDetails) parentDetails.open = true;
1668
+ el.scrollIntoView({ behavior: 'smooth', block: 'center' });
1669
+ triggerPulse(el);
1670
+ var parentBlock = el.closest('p, li, td, th, blockquote') || el.parentElement;
1671
+ if (parentBlock) triggerBlockGlow(parentBlock);
1672
+ el.setAttribute('tabindex', '-1');
1673
+ el.focus({ preventScroll: true });
1674
+ }
1675
+ hideReturnChip();
1676
+ }
1677
+ } else {
1678
+ hideReturnChip();
1679
+ }
1680
+ });
1681
+
1682
+ // ---- 4. ScrollSpy Engine ----
1683
+ var headingsList = Array.from(contentEl.querySelectorAll('h1, h2, h3'));
1684
+ var tocLinks = Array.from(document.querySelectorAll('.report-sidebar .toc a, .drawer-toc .toc a'));
1685
+
1686
+ function updateScrollSpy() {
1687
+ if (!headingsList.length || !tocLinks.length) return;
1688
+ var scrollY = window.scrollY || document.documentElement.scrollTop;
1689
+ var isAtBottom = (window.innerHeight + scrollY) >= (document.documentElement.scrollHeight - 36);
1690
+
1691
+ var currentId = '';
1692
+ if (isAtBottom) {
1693
+ currentId = headingsList[headingsList.length - 1].id;
1694
+ } else {
1695
+ for (var i = headingsList.length - 1; i >= 0; i--) {
1696
+ if (headingsList[i].offsetTop <= scrollY + 95) {
1697
+ currentId = headingsList[i].id;
1698
+ break;
1699
+ }
1700
+ }
1701
+ }
1702
+
1703
+ tocLinks.forEach(function(link) {
1704
+ var match = link.getAttribute('href') === '#' + currentId;
1705
+ link.classList.toggle('active', match);
1706
+ if (match && link.closest('.report-sidebar')) {
1707
+ link.scrollIntoView({ block: 'nearest', inline: 'nearest' });
1708
+ }
1709
+ });
1710
+
1711
+ if (refHeading && activeReturnTarget && scrollY < refHeading.offsetTop - 350) {
1712
+ hideReturnChip();
1713
+ }
1714
+ }
1715
+ window.addEventListener('scroll', updateScrollSpy, { passive: true });
1716
+ updateScrollSpy();
1717
+
1718
+ // ---- 5. Mobile TOC Drawer Controls ----
1719
+ var drawerToggle = document.getElementById('mobile-toc-toggle');
1720
+ var drawer = document.getElementById('mobile-toc-drawer');
1721
+ var backdrop = document.getElementById('mobile-toc-backdrop');
1722
+ var drawerClose = document.getElementById('drawer-close-btn');
1723
+
1724
+ function openDrawer() {
1725
+ if (drawer) drawer.classList.add('open');
1726
+ if (backdrop) backdrop.classList.add('open');
1727
+ document.body.style.overflow = 'hidden';
1728
+ }
1729
+
1730
+ function closeDrawer() {
1731
+ if (drawer) drawer.classList.remove('open');
1732
+ if (backdrop) backdrop.classList.remove('open');
1733
+ document.body.style.overflow = '';
1734
+ }
1735
+
1736
+ if (drawerToggle) drawerToggle.addEventListener('click', openDrawer);
1737
+ if (drawerClose) drawerClose.addEventListener('click', closeDrawer);
1738
+ if (backdrop) backdrop.addEventListener('click', closeDrawer);
1739
+
1740
+ document.querySelectorAll('.drawer-toc a').forEach(function(link) {
1741
+ link.addEventListener('click', function() {
1742
+ closeDrawer();
1743
+ });
1744
+ });
1745
+
1746
+ document.addEventListener('keydown', function(e) {
1747
+ if (e.key === 'Escape') {
1748
+ closeDrawer();
1749
+ hideTooltip();
1750
+ hideReturnChip();
1751
+ }
1752
+ });
1753
+ })();
1754
+ """.strip()
1755
+
1756
+
1757
+ def parse_metadata(raw_text: str) -> tuple[str, dict[str, str], str]:
1758
+ """Extract report title, metadata fields, and cleaned markdown text."""
1759
+ lines = raw_text.splitlines()
1760
+ title = ""
1761
+ metadata = {}
1762
+ cleaned_lines = []
1763
+
1764
+ # Check first few lines for # Title and Generated: line
1765
+ skip_indices = set()
1766
+ for idx, line in enumerate(lines[:6]):
1767
+ stripped = line.strip()
1768
+ if stripped.startswith("# ") and not title:
1769
+ title = stripped[2:].strip()
1770
+ skip_indices.add(idx)
1771
+ elif re.search(r"Generated:\s*", stripped, re.IGNORECASE) and "|" in stripped:
1772
+ skip_indices.add(idx)
1773
+ # Parse key-value tokens separated by pipe
1774
+ parts = [p.strip() for p in stripped.split("|")]
1775
+ for p in parts:
1776
+ if ":" in p:
1777
+ k, v = p.split(":", 1)
1778
+ metadata[k.strip().lower()] = v.strip()
1779
+
1780
+ for idx, line in enumerate(lines):
1781
+ if idx not in skip_indices:
1782
+ cleaned_lines.append(line)
1783
+
1784
+ return title, metadata, "\n".join(cleaned_lines)
1785
+
1786
+
1787
+ def build_header_card(title: str, metadata: dict[str, str], icon_uri: str) -> str:
1788
+ """Construct publication-grade header hero card."""
1789
+ items = []
1790
+ if "topic" in metadata:
1791
+ items.append(f'<div class="meta-item"><span class="meta-label">Topic</span><span class="meta-code">{html.escape(metadata["topic"])}</span></div>')
1792
+ if "generated" in metadata:
1793
+ items.append(f'<div class="meta-item"><span class="meta-label">Generated</span><span class="meta-value">{html.escape(metadata["generated"])}</span></div>')
1794
+ if "scope" in metadata:
1795
+ items.append(f'<div class="meta-item meta-scope"><span class="meta-label">Research Scope</span><span class="meta-value">{html.escape(metadata["scope"])}</span></div>')
1796
+
1797
+ meta_html = f'<div class="meta-grid">{"".join(items)}</div>' if items else ""
1798
+
1799
+ return f"""
1800
+ <header class="report-header-card">
1801
+ <div class="brand-badge-bar">
1802
+ <div class="brand-badge">
1803
+ <img class="brand-logo-img" src="{icon_uri}" alt="" aria-hidden="true">
1804
+ <span>BioResearcher · Deep Research</span>
1805
+ </div>
1806
+ </div>
1807
+ <h1 class="report-title">{html.escape(title)}</h1>
1808
+ {meta_html}
1809
+ </header>
1810
+ """.strip()
52
1811
 
53
1812
 
54
1813
  def convert(markdown_path: Path, title: str) -> str:
55
- text = markdown_path.read_text(encoding="utf-8")
56
- body = md_lib.markdown(text, extensions=["tables", "fenced_code", "sane_lists"])
57
- return (
58
- "<!DOCTYPE html>\n"
59
- '<html lang="en">\n<head>\n<meta charset="utf-8">\n'
60
- f"<title>{title}</title>\n"
61
- f"<style>{CSS}</style>\n</head>\n<body>\n{body}\n</body>\n</html>\n"
1814
+ raw_text = markdown_path.read_text(encoding="utf-8")
1815
+ doc_title, metadata, cleaned_markdown = parse_metadata(raw_text)
1816
+ effective_title = doc_title or title
1817
+
1818
+ md = md_lib.Markdown(
1819
+ extensions=["tables", "fenced_code", "sane_lists", "toc"],
1820
+ extension_configs={
1821
+ "toc": {
1822
+ "toc_depth": "2-3",
1823
+ "title": "",
1824
+ "permalink": False,
1825
+ }
1826
+ },
62
1827
  )
1828
+ body_html = md.convert(cleaned_markdown)
1829
+ toc_html = md.toc or ""
1830
+
1831
+ icon_uri = get_brand_icon_uri()
1832
+ header_card = build_header_card(effective_title, metadata, icon_uri)
1833
+
1834
+ return f"""<!DOCTYPE html>
1835
+ <html lang="en">
1836
+ <head>
1837
+ <meta charset="utf-8">
1838
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1839
+ <title>{html.escape(effective_title)}</title>
1840
+ <link rel="icon" type="image/jpeg" href="{icon_uri}">
1841
+ <style>{CSS}</style>
1842
+ </head>
1843
+ <body>
1844
+
1845
+ <div class="report-wrapper">
1846
+ <aside class="report-sidebar">
1847
+ <div class="sidebar-header">
1848
+ <div class="sidebar-brand">
1849
+ <img class="sidebar-logo-img" src="{icon_uri}" alt="" aria-hidden="true">
1850
+ <span>BioResearcher</span>
1851
+ </div>
1852
+ <span class="sidebar-badge">Deep Research</span>
1853
+ </div>
1854
+ <div class="toc-title">Table of Contents</div>
1855
+ <nav class="toc-container">
1856
+ {toc_html}
1857
+ </nav>
1858
+ </aside>
1859
+
1860
+ <main class="report-main">
1861
+ {header_card}
1862
+ <article id="content" class="markdown-body">
1863
+ {body_html}
1864
+ </article>
1865
+ </main>
1866
+ </div>
1867
+
1868
+ <button class="mobile-toc-btn" id="mobile-toc-toggle" aria-label="Toggle Table of Contents">
1869
+ {SVG_LIST_ICON}
1870
+ <span>Contents</span>
1871
+ </button>
1872
+
1873
+ <div class="mobile-toc-backdrop" id="mobile-toc-backdrop"></div>
1874
+ <div class="mobile-toc-drawer" id="mobile-toc-drawer">
1875
+ <div class="drawer-header">
1876
+ <div class="drawer-title">
1877
+ <img class="drawer-logo-img" src="{icon_uri}" alt="" aria-hidden="true">
1878
+ <span>Table of Contents</span>
1879
+ </div>
1880
+ <button class="drawer-close" id="drawer-close-btn" aria-label="Close Table of Contents">&times;</button>
1881
+ </div>
1882
+ <nav class="drawer-toc">
1883
+ {toc_html}
1884
+ </nav>
1885
+ </div>
1886
+
1887
+ <div id="cite-tooltip" class="cite-tooltip" role="tooltip" aria-hidden="true">
1888
+ <div class="cite-tail"></div>
1889
+ <div class="cite-header">
1890
+ <span class="cite-label">Reference</span>
1891
+ <a class="cite-jump-link" href="#">Jump to reference &darr;</a>
1892
+ </div>
1893
+ <div class="cite-body"></div>
1894
+ </div>
1895
+
1896
+ <aside id="cite-return-chip" class="cite-return-chip" aria-live="polite" aria-hidden="true">
1897
+ <button type="button" class="cite-return-btn" id="cite-return-btn">
1898
+ <span aria-hidden="true" class="return-arrow">&#x21A9;</span>
1899
+ <span class="return-text">Back to text</span>
1900
+ <span class="return-badge" id="cite-return-badge">[1]</span>
1901
+ </button>
1902
+ <button type="button" class="cite-return-dismiss" id="cite-return-dismiss" aria-label="Dismiss return prompt">&times;</button>
1903
+ </aside>
1904
+
1905
+ <script>
1906
+ {CLIENT_SCRIPT}
1907
+ </script>
1908
+
1909
+ </body>
1910
+ </html>
1911
+ """
63
1912
 
64
1913
 
65
1914
  def main() -> None:
66
1915
  parser = argparse.ArgumentParser(
67
- description="Convert a markdown file to standalone GitHub-like HTML."
1916
+ description="Convert a markdown file to standalone publication-grade HTML."
68
1917
  )
69
1918
  parser.add_argument("input", help="input markdown file")
70
1919
  parser.add_argument("-o", "--output", help="output HTML file (default: input with .html)")