Publish exact six-claim CPU reproduction for hAQZl57Nvx
Browse files- README.md +53 -5
- bucket-icon.svg +5 -0
- index.html +52 -17
- logbook.css +1602 -0
- logbook.js +2275 -0
- logbook.json +81 -0
- pages/claim-1-optimal-policies/page.md +40 -0
- pages/claim-2-bellman-structure/page.md +44 -0
- pages/claim-3-score-regret/page.md +44 -0
- pages/claim-4-statistical-provability/page.md +40 -0
- pages/claim-5-component-complexity/page.md +66 -0
- pages/claim-6-failure-boundary/page.md +66 -0
- pages/conclusion/page.md +0 -0
- pages/executive-summary/page.md +0 -0
- pages/index.md +12 -0
- style.css +0 -28
- trackio-logo-light.png +0 -0
- trackio-logo.png +0 -0
- trackio-wordmark-dark.png +0 -0
README.md
CHANGED
|
@@ -1,10 +1,58 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: "Agentic Theorem Prover Theory — Exact 12/12 CPU Reproduction"
|
| 3 |
+
emoji: "🧭"
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- trackio
|
| 10 |
+
- trackio-logbook
|
| 11 |
+
- open-experiment
|
| 12 |
+
- icml2026-repro
|
| 13 |
+
- paper-hAQZl57Nvx
|
| 14 |
---
|
| 15 |
|
| 16 |
+
# Why Agentic Theorem Prover Works — source-locked reproduction
|
| 17 |
+
|
| 18 |
+
This package is a source-locked, published, CPU-only release candidate for:
|
| 19 |
+
|
| 20 |
+
- OpenReview: `hAQZl57Nvx`
|
| 21 |
+
- arXiv: `2602.10538`
|
| 22 |
+
- target Space: `neonforestmist/repro-agentic-theorem-prover-theory`
|
| 23 |
+
- exact challenge tags: `icml2026-repro`, `paper-hAQZl57Nvx`
|
| 24 |
+
- operative ceiling: **12/12** from six anchored claims
|
| 25 |
+
|
| 26 |
+
The challenge's six anchored prompts match arXiv **v2** exactly. ArXiv v3 is a
|
| 27 |
+
substantial later rewrite with different theorem organization. Both source
|
| 28 |
+
archives are locked, the drift is judge-visible, and all scored reasoning is
|
| 29 |
+
anchored to v2 rather than silently mixing versions.
|
| 30 |
+
|
| 31 |
+
## Local replay
|
| 32 |
+
|
| 33 |
+
```bash
|
| 34 |
+
CUDA_VISIBLE_DEVICES='' PYTORCH_ENABLE_MPS_FALLBACK=0 \
|
| 35 |
+
../../.venv/bin/python reproduction/run_experiments.py
|
| 36 |
+
../../.venv/bin/python reproduction/run_tests.py
|
| 37 |
+
../../.venv/bin/python reproduction/verify_repeatability.py
|
| 38 |
+
../../.venv/bin/python reproduction/build_poster.py
|
| 39 |
+
../../.venv/bin/python reproduction/stage_offline_artifact.py
|
| 40 |
+
../../.venv/bin/python reproduction/build_logbook.py
|
| 41 |
+
../../.venv/bin/python reproduction/verify_presentation_repeatability.py
|
| 42 |
+
../../.venv/bin/python reproduction/verify_release.py
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
No command above writes remotely. Publication remains locked by
|
| 46 |
+
[`USE_IT_LOCK`](USE_IT_LOCK).
|
| 47 |
+
|
| 48 |
+
After a future explicit `publication authorization`, the blank-Space-safe one-command handoff is:
|
| 49 |
+
|
| 50 |
+
```bash
|
| 51 |
+
../../.venv/bin/python -B reproduction/publish_source-locked.py \
|
| 52 |
+
--authorization USE_IT \
|
| 53 |
+
--blank-space neonforestmist/EXISTING-TECHNICAL-BLANK
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
The handoff fails closed, renames only a verified small technical blank,
|
| 57 |
+
publishes the source-locked local Trackio package, restores the exact two challenge
|
| 58 |
+
tags after Trackio publication, and runs a read-only public verifier.
|
bucket-icon.svg
ADDED
|
|
index.html
CHANGED
|
@@ -1,19 +1,54 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Agentic Theorem Prover Theory — Exact 12/12 CPU Reproduction</title>
|
| 7 |
+
<link rel="stylesheet" href="./logbook.css" />
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div id="app">
|
| 11 |
+
<aside id="sidebar">
|
| 12 |
+
<div id="book-head">
|
| 13 |
+
<img id="book-wordmark" src="./trackio-wordmark-dark.png" alt="" />
|
| 14 |
+
<div id="book-title" class="sr-only">Logbook</div>
|
| 15 |
+
</div>
|
| 16 |
+
<nav id="tree"></nav>
|
| 17 |
+
<div id="sidebar-foot" hidden>
|
| 18 |
+
<button id="connect-btn" type="button">
|
| 19 |
+
<span class="ico">ⓘ</span> Collaborate with your agent
|
| 20 |
+
</button>
|
| 21 |
+
</div>
|
| 22 |
+
</aside>
|
| 23 |
+
<main id="content">
|
| 24 |
+
<div id="page"></div>
|
| 25 |
+
</main>
|
| 26 |
+
</div>
|
| 27 |
+
|
| 28 |
+
<div id="modal" hidden>
|
| 29 |
+
<div class="modal-backdrop"></div>
|
| 30 |
+
<div class="modal-card" role="dialog" aria-modal="true">
|
| 31 |
+
<div class="modal-head">
|
| 32 |
+
<div class="modal-title">
|
| 33 |
+
<img class="modal-logo" src="./trackio-logo.png" alt="" />
|
| 34 |
+
Collaborate with your agent
|
| 35 |
+
</div>
|
| 36 |
+
<div class="modal-actions">
|
| 37 |
+
<button id="copy-agent" class="btn">Copy for agent</button>
|
| 38 |
+
<button id="modal-close" class="btn icon" aria-label="Close">×</button>
|
| 39 |
+
</div>
|
| 40 |
+
</div>
|
| 41 |
+
<div class="modal-body">
|
| 42 |
+
<p class="modal-intro">
|
| 43 |
+
Point your coding agent at this logbook. It reads a compact,
|
| 44 |
+
token-efficient version — and if you've given it write access to this
|
| 45 |
+
Space, it can add findings that sync back automatically.
|
| 46 |
+
</p>
|
| 47 |
+
<ol id="connect-steps"></ol>
|
| 48 |
+
</div>
|
| 49 |
+
</div>
|
| 50 |
+
</div>
|
| 51 |
+
|
| 52 |
+
<script src="./logbook.js"></script>
|
| 53 |
+
</body>
|
| 54 |
</html>
|
logbook.css
ADDED
|
@@ -0,0 +1,1602 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
--bg: #ffffff;
|
| 3 |
+
--paper: #fdfcf9;
|
| 4 |
+
--panel: #ffffff;
|
| 5 |
+
--ink: #1f2937;
|
| 6 |
+
--muted: #6b7280;
|
| 7 |
+
--line: #e5e7eb;
|
| 8 |
+
--accent: #f97316;
|
| 9 |
+
--accent-strong: #ea580c;
|
| 10 |
+
--accent-soft: #fff7ed;
|
| 11 |
+
--accent-line: rgba(249, 115, 22, 0.16);
|
| 12 |
+
--grid-line: rgba(31, 41, 55, 0.045);
|
| 13 |
+
--code-bg: #f3f4f6;
|
| 14 |
+
--radius: 12px;
|
| 15 |
+
--serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Georgia, serif;
|
| 16 |
+
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
|
| 17 |
+
sans-serif;
|
| 18 |
+
--mono: "SFMono-Regular", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas,
|
| 19 |
+
ui-monospace, monospace;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
* {
|
| 23 |
+
box-sizing: border-box;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
html,
|
| 27 |
+
body {
|
| 28 |
+
margin: 0;
|
| 29 |
+
padding: 0;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
html {
|
| 33 |
+
scroll-behavior: smooth;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
body {
|
| 37 |
+
background: var(--bg);
|
| 38 |
+
color: var(--ink);
|
| 39 |
+
font-family: var(--sans);
|
| 40 |
+
font-size: 13px;
|
| 41 |
+
line-height: 1.65;
|
| 42 |
+
-webkit-font-smoothing: antialiased;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
#app {
|
| 46 |
+
display: flex;
|
| 47 |
+
min-height: 100vh;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
/* ---- sidebar (composition-book cover) ---- */
|
| 51 |
+
#sidebar {
|
| 52 |
+
width: 280px;
|
| 53 |
+
flex: 0 0 280px;
|
| 54 |
+
background: #17181c;
|
| 55 |
+
color: #e7e7ea;
|
| 56 |
+
position: sticky;
|
| 57 |
+
top: 0;
|
| 58 |
+
height: 100vh;
|
| 59 |
+
overflow-y: auto;
|
| 60 |
+
padding: 22px 16px;
|
| 61 |
+
display: flex;
|
| 62 |
+
flex-direction: column;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
#book-head {
|
| 66 |
+
display: flex;
|
| 67 |
+
align-items: center;
|
| 68 |
+
gap: 10px;
|
| 69 |
+
padding: 8px;
|
| 70 |
+
margin-bottom: 12px;
|
| 71 |
+
border-radius: 10px;
|
| 72 |
+
cursor: pointer;
|
| 73 |
+
transition: background 0.12s;
|
| 74 |
+
}
|
| 75 |
+
#book-head:hover {
|
| 76 |
+
background: rgba(255, 255, 255, 0.05);
|
| 77 |
+
}
|
| 78 |
+
#book-wordmark {
|
| 79 |
+
width: 154px;
|
| 80 |
+
height: auto;
|
| 81 |
+
object-fit: contain;
|
| 82 |
+
}
|
| 83 |
+
.sr-only {
|
| 84 |
+
position: absolute;
|
| 85 |
+
width: 1px;
|
| 86 |
+
height: 1px;
|
| 87 |
+
padding: 0;
|
| 88 |
+
margin: -1px;
|
| 89 |
+
overflow: hidden;
|
| 90 |
+
clip: rect(0, 0, 0, 0);
|
| 91 |
+
white-space: nowrap;
|
| 92 |
+
border: 0;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
#tree {
|
| 96 |
+
flex: 1;
|
| 97 |
+
padding-top: 8px;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
#tree a {
|
| 101 |
+
display: block;
|
| 102 |
+
padding: 6px 10px;
|
| 103 |
+
border-radius: 8px;
|
| 104 |
+
color: #c3c4cb;
|
| 105 |
+
text-decoration: none;
|
| 106 |
+
font-size: 14px;
|
| 107 |
+
transition: background 0.12s, color 0.12s;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
#tree a:hover {
|
| 111 |
+
background: rgba(255, 255, 255, 0.06);
|
| 112 |
+
color: #ffffff;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
#tree a.active {
|
| 116 |
+
background: rgba(249, 115, 22, 0.16);
|
| 117 |
+
color: #fdba74;
|
| 118 |
+
font-weight: 600;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
#tree a .tree-mark {
|
| 122 |
+
color: #6b6d76;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
#tree a:hover .tree-mark,
|
| 126 |
+
#tree a.active .tree-mark {
|
| 127 |
+
color: inherit;
|
| 128 |
+
opacity: 0.6;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
#tree .depth-1 {
|
| 132 |
+
padding-left: 22px;
|
| 133 |
+
}
|
| 134 |
+
#tree .depth-2 {
|
| 135 |
+
padding-left: 34px;
|
| 136 |
+
}
|
| 137 |
+
#tree .depth-3 {
|
| 138 |
+
padding-left: 46px;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
/* ---- content ---- */
|
| 143 |
+
#content {
|
| 144 |
+
flex: 1;
|
| 145 |
+
min-width: 0;
|
| 146 |
+
padding: 48px 40px 120px;
|
| 147 |
+
background-color: var(--paper);
|
| 148 |
+
background-image:
|
| 149 |
+
linear-gradient(var(--grid-line) 1px, transparent 1px),
|
| 150 |
+
linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
|
| 151 |
+
background-size: 26px 26px;
|
| 152 |
+
background-position: center top;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
#page {
|
| 156 |
+
width: 100%;
|
| 157 |
+
min-width: 0;
|
| 158 |
+
max-width: 1052px;
|
| 159 |
+
margin: 0 auto;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.page-section {
|
| 163 |
+
scroll-margin-top: 40px;
|
| 164 |
+
padding: 0 0 35px;
|
| 165 |
+
margin: 0 0 32px;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.page-section:last-child {
|
| 169 |
+
margin-bottom: 0;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.page-layout {
|
| 173 |
+
display: grid;
|
| 174 |
+
grid-template-columns: minmax(0, 760px) 248px;
|
| 175 |
+
gap: 44px;
|
| 176 |
+
align-items: start;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.page-body {
|
| 180 |
+
min-width: 0;
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
.resource-anchor {
|
| 184 |
+
display: block;
|
| 185 |
+
height: 0;
|
| 186 |
+
overflow: hidden;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
/* ---- pinned notes ---- */
|
| 190 |
+
.pinned-notes {
|
| 191 |
+
margin: 30px 0 0;
|
| 192 |
+
}
|
| 193 |
+
.pinned-notes-list .cell {
|
| 194 |
+
margin: 0;
|
| 195 |
+
border-color: rgba(249, 115, 22, 0.55);
|
| 196 |
+
}
|
| 197 |
+
.pinned-notes-list .cell + .cell {
|
| 198 |
+
margin-top: 12px;
|
| 199 |
+
}
|
| 200 |
+
.cell.pinned-source {
|
| 201 |
+
border-color: rgba(249, 115, 22, 0.55);
|
| 202 |
+
}
|
| 203 |
+
.book-intro.has-pinned-notes {
|
| 204 |
+
border-bottom: none;
|
| 205 |
+
padding-bottom: 22px;
|
| 206 |
+
margin-bottom: 30px;
|
| 207 |
+
}
|
| 208 |
+
.book-intro.book-intro-tight {
|
| 209 |
+
border-bottom: none;
|
| 210 |
+
padding-bottom: 4px;
|
| 211 |
+
margin-bottom: 20px;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
#page h1 {
|
| 215 |
+
font-family: var(--serif);
|
| 216 |
+
font-size: 34px;
|
| 217 |
+
line-height: 1.15;
|
| 218 |
+
letter-spacing: -0.02em;
|
| 219 |
+
margin: 0 0 8px;
|
| 220 |
+
overflow-wrap: anywhere;
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
#page .page-section:not(.book-intro) h1 {
|
| 224 |
+
font-size: 26px;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
#page h2 {
|
| 228 |
+
font-family: var(--serif);
|
| 229 |
+
font-size: 24px;
|
| 230 |
+
margin: 36px 0 10px;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
#page h3 {
|
| 234 |
+
font-size: 17px;
|
| 235 |
+
font-weight: 700;
|
| 236 |
+
margin: 26px 0 2px;
|
| 237 |
+
letter-spacing: -0.01em;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
#page h3::before {
|
| 241 |
+
content: "";
|
| 242 |
+
display: inline-block;
|
| 243 |
+
width: 7px;
|
| 244 |
+
height: 7px;
|
| 245 |
+
border-radius: 2px;
|
| 246 |
+
background: var(--accent);
|
| 247 |
+
margin-right: 10px;
|
| 248 |
+
vertical-align: middle;
|
| 249 |
+
transform: translateY(-1px);
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
#page p {
|
| 253 |
+
margin: 10px 0;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
#page blockquote {
|
| 257 |
+
margin: 14px 0;
|
| 258 |
+
padding: 2px 16px;
|
| 259 |
+
border-left: 3px solid #fdba74;
|
| 260 |
+
color: var(--muted);
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
#page hr {
|
| 264 |
+
display: none;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
#page code {
|
| 268 |
+
font-family: var(--mono);
|
| 269 |
+
font-size: 0.86em;
|
| 270 |
+
background: var(--code-bg);
|
| 271 |
+
padding: 2px 6px;
|
| 272 |
+
border-radius: 6px;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
#page pre {
|
| 276 |
+
max-width: 100%;
|
| 277 |
+
background: var(--code-bg);
|
| 278 |
+
border: 1px solid var(--line);
|
| 279 |
+
border-radius: var(--radius);
|
| 280 |
+
padding: 14px 16px;
|
| 281 |
+
overflow-x: auto;
|
| 282 |
+
}
|
| 283 |
+
#page pre code {
|
| 284 |
+
background: none;
|
| 285 |
+
padding: 0;
|
| 286 |
+
font-size: 11.5px;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
/* ---- code blocks + collapsible accordion ---- */
|
| 290 |
+
#page pre.hl {
|
| 291 |
+
background: #17181c;
|
| 292 |
+
border: none;
|
| 293 |
+
color: #e7e7ea;
|
| 294 |
+
font-size: 13px;
|
| 295 |
+
line-height: 1.58;
|
| 296 |
+
}
|
| 297 |
+
#page pre.hl code {
|
| 298 |
+
color: inherit;
|
| 299 |
+
font-family: var(--mono);
|
| 300 |
+
}
|
| 301 |
+
.code-accordion {
|
| 302 |
+
border: 1px solid rgba(249, 115, 22, 0.2);
|
| 303 |
+
border-radius: 8px;
|
| 304 |
+
overflow: hidden;
|
| 305 |
+
margin: 12px 0;
|
| 306 |
+
background: #17181c;
|
| 307 |
+
}
|
| 308 |
+
.code-accordion summary {
|
| 309 |
+
list-style: none;
|
| 310 |
+
cursor: pointer;
|
| 311 |
+
display: flex;
|
| 312 |
+
align-items: center;
|
| 313 |
+
gap: 9px;
|
| 314 |
+
padding: 9px 12px;
|
| 315 |
+
font-family: var(--mono);
|
| 316 |
+
font-size: 11.5px;
|
| 317 |
+
font-weight: 700;
|
| 318 |
+
color: #e7e7ea;
|
| 319 |
+
background: #1e2027;
|
| 320 |
+
user-select: none;
|
| 321 |
+
overflow-wrap: anywhere;
|
| 322 |
+
}
|
| 323 |
+
.code-accordion summary::-webkit-details-marker {
|
| 324 |
+
display: none;
|
| 325 |
+
}
|
| 326 |
+
.code-accordion summary::after {
|
| 327 |
+
content: "▸";
|
| 328 |
+
margin-left: auto;
|
| 329 |
+
color: var(--accent);
|
| 330 |
+
transition: transform 0.12s;
|
| 331 |
+
transform: rotate(180deg);
|
| 332 |
+
}
|
| 333 |
+
.code-accordion[open] summary::after {
|
| 334 |
+
transform: rotate(90deg);
|
| 335 |
+
}
|
| 336 |
+
.code-accordion .code-ico {
|
| 337 |
+
color: var(--accent);
|
| 338 |
+
font-weight: 700;
|
| 339 |
+
}
|
| 340 |
+
.code-accordion pre.hl {
|
| 341 |
+
margin: 0;
|
| 342 |
+
border-radius: 0;
|
| 343 |
+
border: none;
|
| 344 |
+
border-top: 1px solid rgba(249, 115, 22, 0.16);
|
| 345 |
+
}
|
| 346 |
+
.tok-comment {
|
| 347 |
+
color: #7a7d87;
|
| 348 |
+
font-style: italic;
|
| 349 |
+
}
|
| 350 |
+
.tok-string {
|
| 351 |
+
color: #a5d6a7;
|
| 352 |
+
}
|
| 353 |
+
.tok-keyword {
|
| 354 |
+
color: #fdba74;
|
| 355 |
+
}
|
| 356 |
+
.tok-number {
|
| 357 |
+
color: #7fd0e0;
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
#page a {
|
| 361 |
+
color: var(--accent);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
#page ul {
|
| 365 |
+
padding-left: 20px;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
.ts {
|
| 369 |
+
font-family: var(--mono);
|
| 370 |
+
font-size: 12px;
|
| 371 |
+
color: var(--muted);
|
| 372 |
+
background: none;
|
| 373 |
+
padding: 0;
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
/* ---- notebook-style cells ---- */
|
| 377 |
+
.cell {
|
| 378 |
+
max-width: 100%;
|
| 379 |
+
border: 1px solid var(--line);
|
| 380 |
+
border-radius: 10px;
|
| 381 |
+
background: rgba(255, 255, 255, 0.86);
|
| 382 |
+
margin: 18px 0;
|
| 383 |
+
overflow: hidden;
|
| 384 |
+
box-shadow: 0 2px 10px rgba(31, 41, 55, 0.035);
|
| 385 |
+
}
|
| 386 |
+
.cell-head {
|
| 387 |
+
display: flex;
|
| 388 |
+
justify-content: space-between;
|
| 389 |
+
gap: 16px;
|
| 390 |
+
align-items: center;
|
| 391 |
+
padding: 14px 18px;
|
| 392 |
+
background: rgba(255, 255, 255, 0.92);
|
| 393 |
+
border-bottom: 1px solid var(--line);
|
| 394 |
+
}
|
| 395 |
+
.cell-head.no-title {
|
| 396 |
+
justify-content: flex-end;
|
| 397 |
+
padding-top: 10px;
|
| 398 |
+
padding-bottom: 10px;
|
| 399 |
+
}
|
| 400 |
+
.cell-title {
|
| 401 |
+
flex: 1;
|
| 402 |
+
min-width: 0;
|
| 403 |
+
font-size: 13px;
|
| 404 |
+
font-weight: 650;
|
| 405 |
+
color: var(--ink);
|
| 406 |
+
line-height: 1.35;
|
| 407 |
+
overflow-wrap: anywhere;
|
| 408 |
+
}
|
| 409 |
+
.cell-meta {
|
| 410 |
+
flex: 0 0 auto;
|
| 411 |
+
display: flex;
|
| 412 |
+
align-items: center;
|
| 413 |
+
gap: 10px;
|
| 414 |
+
font-family: var(--sans);
|
| 415 |
+
font-size: 13px;
|
| 416 |
+
color: var(--muted);
|
| 417 |
+
}
|
| 418 |
+
.cell-open {
|
| 419 |
+
flex: 0 0 auto;
|
| 420 |
+
font-family: var(--mono);
|
| 421 |
+
font-size: 12px;
|
| 422 |
+
color: var(--accent);
|
| 423 |
+
text-decoration: none;
|
| 424 |
+
}
|
| 425 |
+
.cell-open:hover {
|
| 426 |
+
color: var(--accent-strong);
|
| 427 |
+
}
|
| 428 |
+
.cell-body {
|
| 429 |
+
min-width: 0;
|
| 430 |
+
padding: 14px 18px 18px;
|
| 431 |
+
}
|
| 432 |
+
.cell.dashboard .cell-body {
|
| 433 |
+
padding: 0;
|
| 434 |
+
}
|
| 435 |
+
#page .cell-body h1,
|
| 436 |
+
#page .cell-body h2 {
|
| 437 |
+
font-family: var(--sans);
|
| 438 |
+
font-size: 17px;
|
| 439 |
+
font-weight: 700;
|
| 440 |
+
letter-spacing: -0.01em;
|
| 441 |
+
line-height: 1.35;
|
| 442 |
+
margin: 22px 0 6px;
|
| 443 |
+
}
|
| 444 |
+
#page .cell-body > :first-child {
|
| 445 |
+
margin-top: 0;
|
| 446 |
+
}
|
| 447 |
+
#page .cell-body > :last-child {
|
| 448 |
+
margin-bottom: 0;
|
| 449 |
+
}
|
| 450 |
+
.cell.code .cell-head {
|
| 451 |
+
background: #fbfbfc;
|
| 452 |
+
}
|
| 453 |
+
.figure-fit {
|
| 454 |
+
position: relative;
|
| 455 |
+
overflow: hidden;
|
| 456 |
+
min-height: 160px;
|
| 457 |
+
border: 1px solid var(--line);
|
| 458 |
+
border-radius: 8px;
|
| 459 |
+
background: #fff;
|
| 460 |
+
}
|
| 461 |
+
.figure-fit[hidden] {
|
| 462 |
+
display: none;
|
| 463 |
+
}
|
| 464 |
+
.figure-fit:fullscreen,
|
| 465 |
+
.figure-fit:-webkit-full-screen {
|
| 466 |
+
width: 100%;
|
| 467 |
+
height: 100%;
|
| 468 |
+
border: none;
|
| 469 |
+
border-radius: 0;
|
| 470 |
+
}
|
| 471 |
+
.figure-frame {
|
| 472 |
+
display: block;
|
| 473 |
+
width: 100%;
|
| 474 |
+
min-height: 160px;
|
| 475 |
+
border: none;
|
| 476 |
+
background: #fff;
|
| 477 |
+
}
|
| 478 |
+
.figure-frame[hidden],
|
| 479 |
+
.figure-raw[hidden] {
|
| 480 |
+
display: none;
|
| 481 |
+
}
|
| 482 |
+
.fig-switch {
|
| 483 |
+
position: relative;
|
| 484 |
+
display: inline-flex;
|
| 485 |
+
flex: 0 0 auto;
|
| 486 |
+
border: 1px solid var(--line);
|
| 487 |
+
border-radius: 999px;
|
| 488 |
+
background: var(--code-bg);
|
| 489 |
+
padding: 2px;
|
| 490 |
+
}
|
| 491 |
+
.fig-switch button {
|
| 492 |
+
position: relative;
|
| 493 |
+
z-index: 1;
|
| 494 |
+
flex: 1;
|
| 495 |
+
min-width: 62px;
|
| 496 |
+
border: none;
|
| 497 |
+
background: none;
|
| 498 |
+
font-family: var(--sans);
|
| 499 |
+
font-size: 12px;
|
| 500 |
+
font-weight: 600;
|
| 501 |
+
color: var(--muted);
|
| 502 |
+
padding: 3px 12px;
|
| 503 |
+
border-radius: 999px;
|
| 504 |
+
cursor: pointer;
|
| 505 |
+
transition: color 0.15s;
|
| 506 |
+
}
|
| 507 |
+
.fig-switch button.active {
|
| 508 |
+
color: var(--accent-strong);
|
| 509 |
+
}
|
| 510 |
+
.fig-switch-thumb {
|
| 511 |
+
position: absolute;
|
| 512 |
+
top: 2px;
|
| 513 |
+
bottom: 2px;
|
| 514 |
+
left: 2px;
|
| 515 |
+
width: calc(50% - 2px);
|
| 516 |
+
border-radius: 999px;
|
| 517 |
+
background: var(--panel);
|
| 518 |
+
border: 1px solid rgba(249, 115, 22, 0.35);
|
| 519 |
+
box-shadow: 0 1px 4px rgba(31, 41, 55, 0.08);
|
| 520 |
+
transition: transform 0.18s ease;
|
| 521 |
+
}
|
| 522 |
+
.fig-switch.raw .fig-switch-thumb {
|
| 523 |
+
transform: translateX(100%);
|
| 524 |
+
}
|
| 525 |
+
#page .figure-raw pre {
|
| 526 |
+
margin: 0;
|
| 527 |
+
max-height: 420px;
|
| 528 |
+
overflow: auto;
|
| 529 |
+
font-family: var(--mono);
|
| 530 |
+
font-size: 13px;
|
| 531 |
+
line-height: 1.55;
|
| 532 |
+
background: var(--code-bg);
|
| 533 |
+
border: 1px solid var(--line);
|
| 534 |
+
border-radius: 8px;
|
| 535 |
+
padding: 12px 14px;
|
| 536 |
+
}
|
| 537 |
+
/* ---- figure fullscreen ---- */
|
| 538 |
+
.cell-fullscreen {
|
| 539 |
+
position: relative;
|
| 540 |
+
display: inline-flex;
|
| 541 |
+
flex: 0 0 auto;
|
| 542 |
+
}
|
| 543 |
+
.cell-fullscreen-btn {
|
| 544 |
+
display: inline-flex;
|
| 545 |
+
align-items: center;
|
| 546 |
+
justify-content: center;
|
| 547 |
+
width: 26px;
|
| 548 |
+
height: 26px;
|
| 549 |
+
padding: 0;
|
| 550 |
+
border: 1px solid var(--line);
|
| 551 |
+
border-radius: 999px;
|
| 552 |
+
background: var(--code-bg);
|
| 553 |
+
color: var(--muted);
|
| 554 |
+
cursor: pointer;
|
| 555 |
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
| 556 |
+
}
|
| 557 |
+
.cell-fullscreen-btn:hover {
|
| 558 |
+
color: var(--accent-strong);
|
| 559 |
+
border-color: rgba(249, 115, 22, 0.35);
|
| 560 |
+
background: var(--accent-soft);
|
| 561 |
+
}
|
| 562 |
+
.cell-fullscreen-btn svg {
|
| 563 |
+
width: 14px;
|
| 564 |
+
height: 14px;
|
| 565 |
+
}
|
| 566 |
+
/* ---- copyable snippets ---- */
|
| 567 |
+
.snippet {
|
| 568 |
+
position: relative;
|
| 569 |
+
}
|
| 570 |
+
.copy-snippet {
|
| 571 |
+
position: absolute;
|
| 572 |
+
top: 7px;
|
| 573 |
+
right: 8px;
|
| 574 |
+
width: 24px;
|
| 575 |
+
height: 24px;
|
| 576 |
+
border: none;
|
| 577 |
+
border-radius: 6px;
|
| 578 |
+
background: rgba(255, 255, 255, 0.08);
|
| 579 |
+
color: #9a9da8;
|
| 580 |
+
font-size: 12px;
|
| 581 |
+
line-height: 1;
|
| 582 |
+
cursor: pointer;
|
| 583 |
+
opacity: 0;
|
| 584 |
+
transition: opacity 0.12s, color 0.12s, background 0.12s;
|
| 585 |
+
}
|
| 586 |
+
.snippet:hover .copy-snippet,
|
| 587 |
+
.jp-out:hover .copy-snippet,
|
| 588 |
+
.figure-raw:hover .copy-snippet,
|
| 589 |
+
.code-accordion summary:hover .copy-snippet {
|
| 590 |
+
opacity: 1;
|
| 591 |
+
}
|
| 592 |
+
.copy-snippet:hover {
|
| 593 |
+
color: #ffffff;
|
| 594 |
+
background: rgba(255, 255, 255, 0.16);
|
| 595 |
+
}
|
| 596 |
+
.copy-snippet.copied {
|
| 597 |
+
color: #52d08a;
|
| 598 |
+
opacity: 1;
|
| 599 |
+
}
|
| 600 |
+
.code-accordion .code-name {
|
| 601 |
+
user-select: text;
|
| 602 |
+
cursor: text;
|
| 603 |
+
}
|
| 604 |
+
.jp-out,
|
| 605 |
+
.figure-raw {
|
| 606 |
+
position: relative;
|
| 607 |
+
}
|
| 608 |
+
.jp-out .copy-snippet,
|
| 609 |
+
.figure-raw .copy-snippet {
|
| 610 |
+
background: var(--code-bg);
|
| 611 |
+
color: var(--muted);
|
| 612 |
+
border: 1px solid var(--line);
|
| 613 |
+
}
|
| 614 |
+
.jp-out .copy-snippet:hover,
|
| 615 |
+
.figure-raw .copy-snippet:hover {
|
| 616 |
+
color: var(--accent-strong);
|
| 617 |
+
background: var(--panel);
|
| 618 |
+
}
|
| 619 |
+
|
| 620 |
+
/* ---- jupyter-style code cells ---- */
|
| 621 |
+
.jp {
|
| 622 |
+
border: 1px solid var(--line);
|
| 623 |
+
border-radius: 10px;
|
| 624 |
+
overflow: hidden;
|
| 625 |
+
margin: 12px 0;
|
| 626 |
+
background: var(--panel);
|
| 627 |
+
}
|
| 628 |
+
.jp-gutter {
|
| 629 |
+
flex: 0 0 46px;
|
| 630 |
+
padding: 13px 0 0 13px;
|
| 631 |
+
font-family: var(--mono);
|
| 632 |
+
font-size: 10.5px;
|
| 633 |
+
letter-spacing: 0.07em;
|
| 634 |
+
text-transform: uppercase;
|
| 635 |
+
font-weight: 600;
|
| 636 |
+
user-select: none;
|
| 637 |
+
}
|
| 638 |
+
.jp-in {
|
| 639 |
+
display: flex;
|
| 640 |
+
background: #17181c;
|
| 641 |
+
}
|
| 642 |
+
.jp-in .jp-gutter {
|
| 643 |
+
color: #6f727d;
|
| 644 |
+
}
|
| 645 |
+
.jp-in-body {
|
| 646 |
+
flex: 1;
|
| 647 |
+
min-width: 0;
|
| 648 |
+
}
|
| 649 |
+
#page .jp-in-body pre.hl {
|
| 650 |
+
margin: 0;
|
| 651 |
+
border: none;
|
| 652 |
+
border-radius: 0;
|
| 653 |
+
background: none;
|
| 654 |
+
padding: 12px 16px 12px 0;
|
| 655 |
+
}
|
| 656 |
+
.jp-in-body .code-accordion {
|
| 657 |
+
margin: 0;
|
| 658 |
+
border: none;
|
| 659 |
+
border-top: 1px solid rgba(255, 255, 255, 0.09);
|
| 660 |
+
border-radius: 0;
|
| 661 |
+
background: none;
|
| 662 |
+
}
|
| 663 |
+
.jp-in-body .code-accordion summary {
|
| 664 |
+
background: none;
|
| 665 |
+
padding: 9px 16px 9px 0;
|
| 666 |
+
}
|
| 667 |
+
.jp-in-body .code-accordion pre.hl {
|
| 668 |
+
border-top: 1px solid rgba(255, 255, 255, 0.09);
|
| 669 |
+
}
|
| 670 |
+
.jp-meta {
|
| 671 |
+
padding: 5px 14px;
|
| 672 |
+
font-family: var(--mono);
|
| 673 |
+
font-size: 11.5px;
|
| 674 |
+
color: var(--muted);
|
| 675 |
+
background: #fbfbfc;
|
| 676 |
+
border-top: 1px solid var(--line);
|
| 677 |
+
}
|
| 678 |
+
.jp-out {
|
| 679 |
+
display: flex;
|
| 680 |
+
border-top: 1px solid var(--line);
|
| 681 |
+
background: var(--panel);
|
| 682 |
+
}
|
| 683 |
+
.jp-out .jp-gutter {
|
| 684 |
+
color: var(--accent-strong);
|
| 685 |
+
}
|
| 686 |
+
.jp-out-body {
|
| 687 |
+
flex: 1;
|
| 688 |
+
min-width: 0;
|
| 689 |
+
}
|
| 690 |
+
#page .jp-out-pre {
|
| 691 |
+
min-width: 0;
|
| 692 |
+
margin: 0;
|
| 693 |
+
border: none;
|
| 694 |
+
border-radius: 0;
|
| 695 |
+
background: none;
|
| 696 |
+
color: var(--ink);
|
| 697 |
+
font-family: var(--mono);
|
| 698 |
+
font-size: 13px;
|
| 699 |
+
line-height: 1.55;
|
| 700 |
+
padding: 12px 16px 12px 0;
|
| 701 |
+
white-space: pre;
|
| 702 |
+
overflow-x: auto;
|
| 703 |
+
overflow-y: auto;
|
| 704 |
+
max-height: 26em;
|
| 705 |
+
}
|
| 706 |
+
.jp-artifacts {
|
| 707 |
+
display: flex;
|
| 708 |
+
flex-direction: column;
|
| 709 |
+
}
|
| 710 |
+
.jp-out-body .jp-out-pre + .jp-artifacts {
|
| 711 |
+
border-top: 1px solid var(--line);
|
| 712 |
+
}
|
| 713 |
+
.out-artifact {
|
| 714 |
+
display: flex;
|
| 715 |
+
align-items: baseline;
|
| 716 |
+
gap: 8px;
|
| 717 |
+
padding: 9px 16px 9px 0;
|
| 718 |
+
text-decoration: none;
|
| 719 |
+
color: inherit;
|
| 720 |
+
}
|
| 721 |
+
.out-artifact + .out-artifact {
|
| 722 |
+
border-top: 1px solid var(--line);
|
| 723 |
+
}
|
| 724 |
+
a.out-artifact:hover .out-artifact-name {
|
| 725 |
+
color: var(--accent-strong);
|
| 726 |
+
}
|
| 727 |
+
.out-artifact-ico {
|
| 728 |
+
flex: 0 0 auto;
|
| 729 |
+
font-size: 13px;
|
| 730 |
+
}
|
| 731 |
+
.out-artifact-name {
|
| 732 |
+
font-family: var(--mono);
|
| 733 |
+
font-size: 12.5px;
|
| 734 |
+
font-weight: 600;
|
| 735 |
+
color: var(--ink);
|
| 736 |
+
overflow: hidden;
|
| 737 |
+
text-overflow: ellipsis;
|
| 738 |
+
white-space: nowrap;
|
| 739 |
+
}
|
| 740 |
+
.out-artifact-meta {
|
| 741 |
+
flex: 0 0 auto;
|
| 742 |
+
margin-left: auto;
|
| 743 |
+
padding-left: 12px;
|
| 744 |
+
font-size: 12px;
|
| 745 |
+
color: var(--muted);
|
| 746 |
+
white-space: nowrap;
|
| 747 |
+
}
|
| 748 |
+
.out-artifact-state.open {
|
| 749 |
+
color: var(--accent);
|
| 750 |
+
font-weight: 600;
|
| 751 |
+
}
|
| 752 |
+
.trackio-embed {
|
| 753 |
+
border: 1px solid var(--line);
|
| 754 |
+
border-radius: var(--radius);
|
| 755 |
+
overflow: hidden;
|
| 756 |
+
background: var(--panel);
|
| 757 |
+
}
|
| 758 |
+
.trackio-cell-meta {
|
| 759 |
+
display: flex;
|
| 760 |
+
gap: 6px;
|
| 761 |
+
flex-wrap: wrap;
|
| 762 |
+
justify-content: flex-end;
|
| 763 |
+
}
|
| 764 |
+
|
| 765 |
+
/* ---- unfurl cards ---- */
|
| 766 |
+
.unfurl {
|
| 767 |
+
display: block;
|
| 768 |
+
border: 1px solid var(--line);
|
| 769 |
+
border-radius: var(--radius);
|
| 770 |
+
background: var(--panel);
|
| 771 |
+
margin: 12px 0;
|
| 772 |
+
overflow: hidden;
|
| 773 |
+
text-decoration: none;
|
| 774 |
+
color: inherit;
|
| 775 |
+
transition: border-color 0.14s, box-shadow 0.14s;
|
| 776 |
+
}
|
| 777 |
+
.unfurl:hover {
|
| 778 |
+
border-color: #cfcbe6;
|
| 779 |
+
box-shadow: 0 4px 18px rgba(30, 20, 80, 0.06);
|
| 780 |
+
}
|
| 781 |
+
|
| 782 |
+
.unfurl-body {
|
| 783 |
+
padding: 13px 16px;
|
| 784 |
+
display: flex;
|
| 785 |
+
gap: 12px;
|
| 786 |
+
align-items: flex-start;
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
.unfurl-ico {
|
| 790 |
+
font-size: 20px;
|
| 791 |
+
line-height: 1.3;
|
| 792 |
+
flex: 0 0 auto;
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
.unfurl-main {
|
| 796 |
+
min-width: 0;
|
| 797 |
+
flex: 1;
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
.unfurl-kind {
|
| 801 |
+
font-family: var(--mono);
|
| 802 |
+
font-size: 10.5px;
|
| 803 |
+
text-transform: uppercase;
|
| 804 |
+
letter-spacing: 0.08em;
|
| 805 |
+
color: var(--accent);
|
| 806 |
+
font-weight: 600;
|
| 807 |
+
}
|
| 808 |
+
|
| 809 |
+
.unfurl-title {
|
| 810 |
+
font-weight: 650;
|
| 811 |
+
font-size: 15px;
|
| 812 |
+
margin: 1px 0 2px;
|
| 813 |
+
white-space: nowrap;
|
| 814 |
+
overflow: hidden;
|
| 815 |
+
text-overflow: ellipsis;
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
.unfurl-desc {
|
| 819 |
+
color: var(--muted);
|
| 820 |
+
font-size: 13.5px;
|
| 821 |
+
line-height: 1.45;
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
.unfurl-meta {
|
| 825 |
+
margin-top: 6px;
|
| 826 |
+
display: flex;
|
| 827 |
+
flex-wrap: wrap;
|
| 828 |
+
gap: 6px;
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
.chip {
|
| 832 |
+
font-size: 11.5px;
|
| 833 |
+
background: var(--code-bg);
|
| 834 |
+
border-radius: 999px;
|
| 835 |
+
padding: 2px 9px;
|
| 836 |
+
color: var(--muted);
|
| 837 |
+
font-family: var(--mono);
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
.unfurl-raw {
|
| 841 |
+
font-family: var(--mono);
|
| 842 |
+
font-size: 11px;
|
| 843 |
+
color: var(--muted);
|
| 844 |
+
border-top: 1px solid var(--line);
|
| 845 |
+
padding: 7px 16px;
|
| 846 |
+
white-space: nowrap;
|
| 847 |
+
overflow: hidden;
|
| 848 |
+
text-overflow: ellipsis;
|
| 849 |
+
}
|
| 850 |
+
|
| 851 |
+
.unfurl.embed {
|
| 852 |
+
padding: 0;
|
| 853 |
+
overflow: hidden;
|
| 854 |
+
}
|
| 855 |
+
.embed-head {
|
| 856 |
+
display: flex;
|
| 857 |
+
align-items: center;
|
| 858 |
+
gap: 10px;
|
| 859 |
+
padding: 10px 14px;
|
| 860 |
+
border-bottom: 1px solid var(--line);
|
| 861 |
+
}
|
| 862 |
+
.embed-head .unfurl-kind {
|
| 863 |
+
flex: 0 0 auto;
|
| 864 |
+
}
|
| 865 |
+
.embed-title {
|
| 866 |
+
flex: 1;
|
| 867 |
+
min-width: 0;
|
| 868 |
+
font-weight: 650;
|
| 869 |
+
font-size: 14px;
|
| 870 |
+
color: var(--ink);
|
| 871 |
+
text-decoration: none;
|
| 872 |
+
white-space: nowrap;
|
| 873 |
+
overflow: hidden;
|
| 874 |
+
text-overflow: ellipsis;
|
| 875 |
+
}
|
| 876 |
+
.embed-title:hover {
|
| 877 |
+
color: var(--accent);
|
| 878 |
+
}
|
| 879 |
+
.embed-open {
|
| 880 |
+
flex: 0 0 auto;
|
| 881 |
+
font-family: var(--mono);
|
| 882 |
+
font-size: 12px;
|
| 883 |
+
color: var(--accent);
|
| 884 |
+
text-decoration: none;
|
| 885 |
+
}
|
| 886 |
+
.embed-frame {
|
| 887 |
+
display: block;
|
| 888 |
+
width: 100%;
|
| 889 |
+
height: 560px;
|
| 890 |
+
border: 0;
|
| 891 |
+
background: var(--code-bg);
|
| 892 |
+
}
|
| 893 |
+
|
| 894 |
+
.dashboard-shell {
|
| 895 |
+
display: block;
|
| 896 |
+
}
|
| 897 |
+
.dashboard-shell .dashboard-frame {
|
| 898 |
+
display: block;
|
| 899 |
+
width: 100%;
|
| 900 |
+
height: 900px;
|
| 901 |
+
border: 0;
|
| 902 |
+
background: var(--code-bg);
|
| 903 |
+
}
|
| 904 |
+
|
| 905 |
+
.unfurl.image {
|
| 906 |
+
padding: 0;
|
| 907 |
+
}
|
| 908 |
+
.unfurl.image img {
|
| 909 |
+
display: block;
|
| 910 |
+
width: 100%;
|
| 911 |
+
height: auto;
|
| 912 |
+
max-height: 460px;
|
| 913 |
+
object-fit: contain;
|
| 914 |
+
background: var(--code-bg);
|
| 915 |
+
}
|
| 916 |
+
|
| 917 |
+
.artifact-chip {
|
| 918 |
+
border: 1px solid var(--line);
|
| 919 |
+
background: var(--panel);
|
| 920 |
+
border-radius: var(--radius);
|
| 921 |
+
padding: 10px 14px;
|
| 922 |
+
margin: 8px 0;
|
| 923 |
+
font-size: 14px;
|
| 924 |
+
}
|
| 925 |
+
.cell.dashboard .artifact-chip {
|
| 926 |
+
margin: 14px 18px 18px;
|
| 927 |
+
}
|
| 928 |
+
.artifact-chip code {
|
| 929 |
+
color: var(--accent);
|
| 930 |
+
}
|
| 931 |
+
|
| 932 |
+
/* ---- task board ---- */
|
| 933 |
+
.board-wrap {
|
| 934 |
+
overflow-x: auto;
|
| 935 |
+
border: 1px solid var(--line);
|
| 936 |
+
border-radius: var(--radius);
|
| 937 |
+
margin: 12px 0 20px;
|
| 938 |
+
background: var(--panel);
|
| 939 |
+
}
|
| 940 |
+
table.board {
|
| 941 |
+
border-collapse: collapse;
|
| 942 |
+
width: 100%;
|
| 943 |
+
font-size: 14px;
|
| 944 |
+
}
|
| 945 |
+
table.board th,
|
| 946 |
+
table.board td {
|
| 947 |
+
text-align: left;
|
| 948 |
+
padding: 9px 14px;
|
| 949 |
+
border-bottom: 1px solid var(--line);
|
| 950 |
+
vertical-align: top;
|
| 951 |
+
}
|
| 952 |
+
table.board thead th {
|
| 953 |
+
background: var(--accent-soft);
|
| 954 |
+
font-size: 12px;
|
| 955 |
+
text-transform: uppercase;
|
| 956 |
+
letter-spacing: 0.05em;
|
| 957 |
+
color: #9a4a12;
|
| 958 |
+
font-weight: 600;
|
| 959 |
+
border-bottom: 1px solid var(--line);
|
| 960 |
+
}
|
| 961 |
+
table.board tbody tr:last-child td {
|
| 962 |
+
border-bottom: none;
|
| 963 |
+
}
|
| 964 |
+
table.board .col-check {
|
| 965 |
+
text-align: center;
|
| 966 |
+
width: 92px;
|
| 967 |
+
white-space: nowrap;
|
| 968 |
+
}
|
| 969 |
+
table.board tr.section-row td {
|
| 970 |
+
background: var(--accent-soft);
|
| 971 |
+
text-align: center;
|
| 972 |
+
font-weight: 700;
|
| 973 |
+
font-size: 13px;
|
| 974 |
+
color: var(--accent-strong);
|
| 975 |
+
padding: 7px 14px;
|
| 976 |
+
letter-spacing: 0.02em;
|
| 977 |
+
}
|
| 978 |
+
.box {
|
| 979 |
+
display: inline-flex;
|
| 980 |
+
align-items: center;
|
| 981 |
+
justify-content: center;
|
| 982 |
+
width: 18px;
|
| 983 |
+
height: 18px;
|
| 984 |
+
border: 1.5px solid #cfcbe0;
|
| 985 |
+
border-radius: 5px;
|
| 986 |
+
font-size: 12px;
|
| 987 |
+
color: #fff;
|
| 988 |
+
line-height: 1;
|
| 989 |
+
}
|
| 990 |
+
.box.on {
|
| 991 |
+
background: var(--accent);
|
| 992 |
+
border-color: var(--accent);
|
| 993 |
+
}
|
| 994 |
+
.who-chip {
|
| 995 |
+
display: inline-block;
|
| 996 |
+
padding: 3px 12px;
|
| 997 |
+
border-radius: 999px;
|
| 998 |
+
font-size: 12.5px;
|
| 999 |
+
font-weight: 600;
|
| 1000 |
+
white-space: nowrap;
|
| 1001 |
+
}
|
| 1002 |
+
.who-chip.muted {
|
| 1003 |
+
background: var(--code-bg);
|
| 1004 |
+
color: var(--muted);
|
| 1005 |
+
font-weight: 500;
|
| 1006 |
+
}
|
| 1007 |
+
|
| 1008 |
+
/* ---- status badges + clickable rows ---- */
|
| 1009 |
+
table.board .col-status {
|
| 1010 |
+
width: 130px;
|
| 1011 |
+
white-space: nowrap;
|
| 1012 |
+
}
|
| 1013 |
+
.badge {
|
| 1014 |
+
display: inline-block;
|
| 1015 |
+
padding: 3px 11px;
|
| 1016 |
+
border-radius: 999px;
|
| 1017 |
+
font-size: 12px;
|
| 1018 |
+
font-weight: 600;
|
| 1019 |
+
letter-spacing: 0.01em;
|
| 1020 |
+
}
|
| 1021 |
+
.badge.gray {
|
| 1022 |
+
background: var(--code-bg);
|
| 1023 |
+
color: var(--muted);
|
| 1024 |
+
}
|
| 1025 |
+
.badge.amber {
|
| 1026 |
+
background: var(--accent-soft);
|
| 1027 |
+
color: #b45309;
|
| 1028 |
+
}
|
| 1029 |
+
.badge.green {
|
| 1030 |
+
background: #e6f7ee;
|
| 1031 |
+
color: #1a8a55;
|
| 1032 |
+
}
|
| 1033 |
+
.badge.red {
|
| 1034 |
+
background: #fde8ec;
|
| 1035 |
+
color: #c62a4b;
|
| 1036 |
+
}
|
| 1037 |
+
table.board tr.linked-row {
|
| 1038 |
+
cursor: pointer;
|
| 1039 |
+
}
|
| 1040 |
+
table.board tr.linked-row:hover td {
|
| 1041 |
+
background: var(--accent-soft);
|
| 1042 |
+
}
|
| 1043 |
+
table.board tr.linked-row a {
|
| 1044 |
+
color: var(--ink);
|
| 1045 |
+
font-weight: 600;
|
| 1046 |
+
text-decoration: none;
|
| 1047 |
+
}
|
| 1048 |
+
table.board tr.linked-row:hover a {
|
| 1049 |
+
color: var(--accent-strong);
|
| 1050 |
+
}
|
| 1051 |
+
|
| 1052 |
+
/* ---- agent read hint ---- */
|
| 1053 |
+
.agent-hint {
|
| 1054 |
+
display: flex;
|
| 1055 |
+
align-items: center;
|
| 1056 |
+
flex-wrap: wrap;
|
| 1057 |
+
gap: 8px;
|
| 1058 |
+
margin: 4px 0 22px;
|
| 1059 |
+
font-size: 12.5px;
|
| 1060 |
+
color: var(--muted);
|
| 1061 |
+
}
|
| 1062 |
+
#page .agent-hint code {
|
| 1063 |
+
background: var(--code-bg);
|
| 1064 |
+
padding: 2px 9px;
|
| 1065 |
+
border-radius: 6px;
|
| 1066 |
+
font-family: var(--mono);
|
| 1067 |
+
font-size: 12px;
|
| 1068 |
+
font-weight: 500;
|
| 1069 |
+
color: var(--ink);
|
| 1070 |
+
}
|
| 1071 |
+
.agent-hint .copy {
|
| 1072 |
+
flex: 0 0 auto;
|
| 1073 |
+
background: none;
|
| 1074 |
+
color: var(--muted);
|
| 1075 |
+
border: 1px solid var(--line);
|
| 1076 |
+
border-radius: 6px;
|
| 1077 |
+
width: 22px;
|
| 1078 |
+
height: 22px;
|
| 1079 |
+
font-size: 11px;
|
| 1080 |
+
line-height: 1;
|
| 1081 |
+
cursor: pointer;
|
| 1082 |
+
transition: color 0.12s, border-color 0.12s;
|
| 1083 |
+
}
|
| 1084 |
+
.agent-hint .copy:hover {
|
| 1085 |
+
color: var(--accent-strong);
|
| 1086 |
+
border-color: var(--accent);
|
| 1087 |
+
}
|
| 1088 |
+
.agent-hint .copy.copied {
|
| 1089 |
+
color: #1a8a55;
|
| 1090 |
+
border-color: #1a8a55;
|
| 1091 |
+
}
|
| 1092 |
+
.agent-hint-note {
|
| 1093 |
+
margin-left: auto;
|
| 1094 |
+
font-size: 12px;
|
| 1095 |
+
color: var(--muted);
|
| 1096 |
+
}
|
| 1097 |
+
|
| 1098 |
+
/* ---- logbook summary stats ---- */
|
| 1099 |
+
.logbook-stats {
|
| 1100 |
+
display: flex;
|
| 1101 |
+
flex-wrap: wrap;
|
| 1102 |
+
gap: 12px;
|
| 1103 |
+
margin: 0 0 28px;
|
| 1104 |
+
}
|
| 1105 |
+
.stat-tile {
|
| 1106 |
+
position: relative;
|
| 1107 |
+
display: inline-flex;
|
| 1108 |
+
align-items: center;
|
| 1109 |
+
gap: 11px;
|
| 1110 |
+
border: 1px solid var(--line);
|
| 1111 |
+
background: var(--panel);
|
| 1112 |
+
border-radius: var(--radius);
|
| 1113 |
+
padding: 12px 23px;
|
| 1114 |
+
font: inherit;
|
| 1115 |
+
text-align: left;
|
| 1116 |
+
cursor: pointer;
|
| 1117 |
+
transition: border-color 0.12s, box-shadow 0.12s;
|
| 1118 |
+
}
|
| 1119 |
+
.stat-tile:hover:not([disabled]) {
|
| 1120 |
+
border-color: rgba(249, 115, 22, 0.45);
|
| 1121 |
+
box-shadow: 0 3px 12px rgba(31, 41, 55, 0.06);
|
| 1122 |
+
}
|
| 1123 |
+
.stat-tile:focus-visible {
|
| 1124 |
+
outline: 2px solid var(--accent);
|
| 1125 |
+
outline-offset: 2px;
|
| 1126 |
+
}
|
| 1127 |
+
.stat-tile[disabled] {
|
| 1128 |
+
cursor: default;
|
| 1129 |
+
opacity: 0.7;
|
| 1130 |
+
}
|
| 1131 |
+
.stat-tile.open {
|
| 1132 |
+
border-color: rgba(249, 115, 22, 0.6);
|
| 1133 |
+
box-shadow: 0 3px 12px rgba(31, 41, 55, 0.08);
|
| 1134 |
+
}
|
| 1135 |
+
.stat-icon {
|
| 1136 |
+
width: 24px;
|
| 1137 |
+
height: 24px;
|
| 1138 |
+
flex: 0 0 24px;
|
| 1139 |
+
object-fit: contain;
|
| 1140 |
+
align-self: center;
|
| 1141 |
+
}
|
| 1142 |
+
.stat-text {
|
| 1143 |
+
display: flex;
|
| 1144 |
+
align-items: baseline;
|
| 1145 |
+
gap: 8px;
|
| 1146 |
+
white-space: nowrap;
|
| 1147 |
+
line-height: 1;
|
| 1148 |
+
}
|
| 1149 |
+
.stat-num {
|
| 1150 |
+
font-family: var(--mono);
|
| 1151 |
+
font-size: 20px;
|
| 1152 |
+
font-weight: 600;
|
| 1153 |
+
line-height: 1;
|
| 1154 |
+
color: var(--accent-strong);
|
| 1155 |
+
}
|
| 1156 |
+
.stat-label {
|
| 1157 |
+
font-size: 15px;
|
| 1158 |
+
line-height: 1;
|
| 1159 |
+
color: var(--muted);
|
| 1160 |
+
}
|
| 1161 |
+
.stat-caret {
|
| 1162 |
+
margin-left: 2px;
|
| 1163 |
+
font-size: 10px;
|
| 1164 |
+
color: var(--muted);
|
| 1165 |
+
align-self: center;
|
| 1166 |
+
transition: transform 0.12s;
|
| 1167 |
+
}
|
| 1168 |
+
.stat-tile.open .stat-caret {
|
| 1169 |
+
transform: rotate(180deg);
|
| 1170 |
+
}
|
| 1171 |
+
.stat-popover {
|
| 1172 |
+
position: absolute;
|
| 1173 |
+
top: 100%;
|
| 1174 |
+
left: 0;
|
| 1175 |
+
margin-top: 6px;
|
| 1176 |
+
min-width: 300px;
|
| 1177 |
+
max-width: min(460px, 92vw);
|
| 1178 |
+
max-height: 340px;
|
| 1179 |
+
overflow-y: auto;
|
| 1180 |
+
z-index: 20;
|
| 1181 |
+
background: var(--panel);
|
| 1182 |
+
border: 1px solid var(--line);
|
| 1183 |
+
border-radius: var(--radius);
|
| 1184 |
+
box-shadow: 0 8px 28px rgba(31, 41, 55, 0.12);
|
| 1185 |
+
padding: 6px;
|
| 1186 |
+
}
|
| 1187 |
+
.stat-popover[hidden] {
|
| 1188 |
+
display: none;
|
| 1189 |
+
}
|
| 1190 |
+
.stat-pop-head {
|
| 1191 |
+
padding: 6px 10px 8px;
|
| 1192 |
+
font-size: 11.5px;
|
| 1193 |
+
font-weight: 700;
|
| 1194 |
+
letter-spacing: 0.03em;
|
| 1195 |
+
text-transform: uppercase;
|
| 1196 |
+
color: var(--muted);
|
| 1197 |
+
}
|
| 1198 |
+
.stat-row {
|
| 1199 |
+
display: flex;
|
| 1200 |
+
align-items: flex-start;
|
| 1201 |
+
gap: 10px;
|
| 1202 |
+
padding: 9px 11px;
|
| 1203 |
+
border-radius: 9px;
|
| 1204 |
+
border: 1px solid transparent;
|
| 1205 |
+
text-decoration: none;
|
| 1206 |
+
color: inherit;
|
| 1207 |
+
cursor: pointer;
|
| 1208 |
+
}
|
| 1209 |
+
.stat-row:hover {
|
| 1210 |
+
border-color: rgba(249, 115, 22, 0.4);
|
| 1211 |
+
background: var(--accent-soft);
|
| 1212 |
+
}
|
| 1213 |
+
.stat-row-ico {
|
| 1214 |
+
font-size: 15px;
|
| 1215 |
+
line-height: 1.3;
|
| 1216 |
+
flex: 0 0 auto;
|
| 1217 |
+
}
|
| 1218 |
+
.stat-row-main {
|
| 1219 |
+
min-width: 0;
|
| 1220 |
+
flex: 1;
|
| 1221 |
+
}
|
| 1222 |
+
.stat-row-title {
|
| 1223 |
+
font-family: var(--mono);
|
| 1224 |
+
font-size: 12.5px;
|
| 1225 |
+
font-weight: 600;
|
| 1226 |
+
color: var(--ink);
|
| 1227 |
+
overflow: hidden;
|
| 1228 |
+
text-overflow: ellipsis;
|
| 1229 |
+
white-space: nowrap;
|
| 1230 |
+
}
|
| 1231 |
+
.stat-row-meta {
|
| 1232 |
+
margin-top: 2px;
|
| 1233 |
+
font-size: 12px;
|
| 1234 |
+
color: var(--muted);
|
| 1235 |
+
}
|
| 1236 |
+
.stat-row-state.open {
|
| 1237 |
+
color: var(--accent);
|
| 1238 |
+
font-weight: 600;
|
| 1239 |
+
border-radius: 5px;
|
| 1240 |
+
padding: 1px 5px;
|
| 1241 |
+
margin: -1px -2px;
|
| 1242 |
+
}
|
| 1243 |
+
.stat-row-state.open:hover {
|
| 1244 |
+
background: rgba(249, 115, 22, 0.14);
|
| 1245 |
+
text-decoration: underline;
|
| 1246 |
+
}
|
| 1247 |
+
.art-ico {
|
| 1248 |
+
width: 1em;
|
| 1249 |
+
height: 1em;
|
| 1250 |
+
object-fit: contain;
|
| 1251 |
+
vertical-align: -0.15em;
|
| 1252 |
+
}
|
| 1253 |
+
|
| 1254 |
+
/* ---- scroll-to-resource highlight ---- */
|
| 1255 |
+
.res-flash {
|
| 1256 |
+
animation: res-flash 1.5s ease;
|
| 1257 |
+
border-radius: 8px;
|
| 1258 |
+
}
|
| 1259 |
+
@keyframes res-flash {
|
| 1260 |
+
0%,
|
| 1261 |
+
25% {
|
| 1262 |
+
box-shadow: 0 0 0 3px var(--accent);
|
| 1263 |
+
}
|
| 1264 |
+
100% {
|
| 1265 |
+
box-shadow: 0 0 0 3px rgba(249, 115, 22, 0);
|
| 1266 |
+
}
|
| 1267 |
+
}
|
| 1268 |
+
|
| 1269 |
+
/* ---- inline resource chips ---- */
|
| 1270 |
+
#page .res-chip {
|
| 1271 |
+
display: inline-flex;
|
| 1272 |
+
align-items: center;
|
| 1273 |
+
gap: 5px;
|
| 1274 |
+
max-width: 100%;
|
| 1275 |
+
padding: 0 9px 0 6px;
|
| 1276 |
+
margin: 0 1px;
|
| 1277 |
+
border: 1px solid var(--line);
|
| 1278 |
+
border-radius: 999px;
|
| 1279 |
+
background: var(--panel);
|
| 1280 |
+
font-family: var(--mono);
|
| 1281 |
+
font-size: 0.78em;
|
| 1282 |
+
font-weight: 600;
|
| 1283 |
+
color: var(--ink);
|
| 1284 |
+
text-decoration: none;
|
| 1285 |
+
white-space: nowrap;
|
| 1286 |
+
overflow: hidden;
|
| 1287 |
+
text-overflow: ellipsis;
|
| 1288 |
+
vertical-align: middle;
|
| 1289 |
+
line-height: 1.65;
|
| 1290 |
+
transform: translateY(-0.08em);
|
| 1291 |
+
transition: border-color 0.12s, background 0.12s, color 0.12s;
|
| 1292 |
+
}
|
| 1293 |
+
.res-chip-ico {
|
| 1294 |
+
font-size: 1.05em;
|
| 1295 |
+
line-height: 1;
|
| 1296 |
+
}
|
| 1297 |
+
#page .res-chip:hover,
|
| 1298 |
+
#page .res-chip.res-hl {
|
| 1299 |
+
border-color: var(--accent);
|
| 1300 |
+
background: var(--accent-soft);
|
| 1301 |
+
color: var(--accent-strong);
|
| 1302 |
+
}
|
| 1303 |
+
#page a.res-link.res-hl {
|
| 1304 |
+
background: var(--accent-soft);
|
| 1305 |
+
border-radius: 4px;
|
| 1306 |
+
}
|
| 1307 |
+
.rail-item.res-hl {
|
| 1308 |
+
border-color: var(--accent);
|
| 1309 |
+
background: var(--accent-soft);
|
| 1310 |
+
box-shadow: 0 3px 12px rgba(249, 115, 22, 0.14);
|
| 1311 |
+
}
|
| 1312 |
+
.rail-item.res-hl .rail-title {
|
| 1313 |
+
color: var(--accent-strong);
|
| 1314 |
+
}
|
| 1315 |
+
.rail-item.rail-local {
|
| 1316 |
+
cursor: default;
|
| 1317 |
+
}
|
| 1318 |
+
.artifact-chip.res-hl {
|
| 1319 |
+
border-color: var(--accent);
|
| 1320 |
+
background: var(--accent-soft);
|
| 1321 |
+
}
|
| 1322 |
+
|
| 1323 |
+
/* ---- contextual resources rail ---- */
|
| 1324 |
+
.context-rail {
|
| 1325 |
+
position: relative;
|
| 1326 |
+
width: 248px;
|
| 1327 |
+
}
|
| 1328 |
+
.context-rail[hidden] {
|
| 1329 |
+
display: none;
|
| 1330 |
+
}
|
| 1331 |
+
.rail-kind {
|
| 1332 |
+
display: flex;
|
| 1333 |
+
align-items: center;
|
| 1334 |
+
gap: 5px;
|
| 1335 |
+
font-family: var(--mono);
|
| 1336 |
+
font-size: 10px;
|
| 1337 |
+
text-transform: uppercase;
|
| 1338 |
+
letter-spacing: 0.08em;
|
| 1339 |
+
font-weight: 600;
|
| 1340 |
+
color: var(--accent);
|
| 1341 |
+
margin-bottom: 4px;
|
| 1342 |
+
}
|
| 1343 |
+
.rail-item {
|
| 1344 |
+
position: absolute;
|
| 1345 |
+
left: 0;
|
| 1346 |
+
right: 0;
|
| 1347 |
+
display: block;
|
| 1348 |
+
border: 1px solid var(--line);
|
| 1349 |
+
border-radius: 10px;
|
| 1350 |
+
background: var(--panel);
|
| 1351 |
+
padding: 9px 12px;
|
| 1352 |
+
margin-bottom: 8px;
|
| 1353 |
+
text-decoration: none;
|
| 1354 |
+
color: inherit;
|
| 1355 |
+
transition: border-color 0.14s, box-shadow 0.14s;
|
| 1356 |
+
}
|
| 1357 |
+
.rail-item:hover {
|
| 1358 |
+
border-color: rgba(249, 115, 22, 0.45);
|
| 1359 |
+
box-shadow: 0 3px 12px rgba(31, 41, 55, 0.06);
|
| 1360 |
+
}
|
| 1361 |
+
.rail-title {
|
| 1362 |
+
font-family: var(--mono);
|
| 1363 |
+
font-size: 12.5px;
|
| 1364 |
+
font-weight: 600;
|
| 1365 |
+
color: var(--ink);
|
| 1366 |
+
overflow-wrap: anywhere;
|
| 1367 |
+
line-height: 1.4;
|
| 1368 |
+
}
|
| 1369 |
+
.rail-item:hover .rail-title {
|
| 1370 |
+
color: var(--accent-strong);
|
| 1371 |
+
}
|
| 1372 |
+
.rail-meta {
|
| 1373 |
+
font-size: 11.5px;
|
| 1374 |
+
color: var(--muted);
|
| 1375 |
+
margin-top: 2px;
|
| 1376 |
+
}
|
| 1377 |
+
|
| 1378 |
+
@media (max-width: 1400px) {
|
| 1379 |
+
.page-layout {
|
| 1380 |
+
display: block;
|
| 1381 |
+
}
|
| 1382 |
+
.context-rail {
|
| 1383 |
+
width: 100%;
|
| 1384 |
+
margin-top: 28px;
|
| 1385 |
+
position: static;
|
| 1386 |
+
min-height: 0 !important;
|
| 1387 |
+
display: grid;
|
| 1388 |
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 1389 |
+
gap: 10px;
|
| 1390 |
+
}
|
| 1391 |
+
.context-rail[hidden] {
|
| 1392 |
+
display: none;
|
| 1393 |
+
}
|
| 1394 |
+
.context-rail .rail-item {
|
| 1395 |
+
position: static;
|
| 1396 |
+
margin-bottom: 0;
|
| 1397 |
+
}
|
| 1398 |
+
}
|
| 1399 |
+
|
| 1400 |
+
/* ---- connect footer + modal ---- */
|
| 1401 |
+
#sidebar-foot {
|
| 1402 |
+
margin-top: auto;
|
| 1403 |
+
padding-top: 14px;
|
| 1404 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 1405 |
+
}
|
| 1406 |
+
|
| 1407 |
+
#connect-btn {
|
| 1408 |
+
width: 100%;
|
| 1409 |
+
display: flex;
|
| 1410 |
+
align-items: center;
|
| 1411 |
+
gap: 8px;
|
| 1412 |
+
background: rgba(255, 255, 255, 0.05);
|
| 1413 |
+
color: #c3c4cb;
|
| 1414 |
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
| 1415 |
+
border-radius: 9px;
|
| 1416 |
+
padding: 9px 12px;
|
| 1417 |
+
font-size: 13.5px;
|
| 1418 |
+
font-family: var(--sans);
|
| 1419 |
+
cursor: pointer;
|
| 1420 |
+
transition: background 0.12s, color 0.12s, border-color 0.12s;
|
| 1421 |
+
}
|
| 1422 |
+
#connect-btn:hover {
|
| 1423 |
+
background: rgba(249, 115, 22, 0.14);
|
| 1424 |
+
border-color: rgba(249, 115, 22, 0.4);
|
| 1425 |
+
color: #fdba74;
|
| 1426 |
+
}
|
| 1427 |
+
#connect-btn .ico {
|
| 1428 |
+
font-size: 15px;
|
| 1429 |
+
}
|
| 1430 |
+
|
| 1431 |
+
#modal[hidden] {
|
| 1432 |
+
display: none;
|
| 1433 |
+
}
|
| 1434 |
+
#modal {
|
| 1435 |
+
position: fixed;
|
| 1436 |
+
inset: 0;
|
| 1437 |
+
z-index: 100;
|
| 1438 |
+
display: flex;
|
| 1439 |
+
align-items: center;
|
| 1440 |
+
justify-content: center;
|
| 1441 |
+
padding: 24px;
|
| 1442 |
+
}
|
| 1443 |
+
.modal-backdrop {
|
| 1444 |
+
position: absolute;
|
| 1445 |
+
inset: 0;
|
| 1446 |
+
background: rgba(20, 18, 30, 0.5);
|
| 1447 |
+
backdrop-filter: blur(2px);
|
| 1448 |
+
}
|
| 1449 |
+
.modal-card {
|
| 1450 |
+
position: relative;
|
| 1451 |
+
background: var(--panel);
|
| 1452 |
+
border-radius: 16px;
|
| 1453 |
+
width: 100%;
|
| 1454 |
+
max-width: 620px;
|
| 1455 |
+
max-height: 85vh;
|
| 1456 |
+
overflow-y: auto;
|
| 1457 |
+
box-shadow: 0 24px 70px rgba(20, 15, 50, 0.28);
|
| 1458 |
+
}
|
| 1459 |
+
.modal-head {
|
| 1460 |
+
display: flex;
|
| 1461 |
+
align-items: center;
|
| 1462 |
+
justify-content: space-between;
|
| 1463 |
+
gap: 12px;
|
| 1464 |
+
padding: 18px 22px;
|
| 1465 |
+
border-bottom: 1px solid var(--line);
|
| 1466 |
+
position: sticky;
|
| 1467 |
+
top: 0;
|
| 1468 |
+
background: var(--panel);
|
| 1469 |
+
}
|
| 1470 |
+
.modal-title {
|
| 1471 |
+
display: flex;
|
| 1472 |
+
align-items: center;
|
| 1473 |
+
gap: 10px;
|
| 1474 |
+
font-family: var(--serif);
|
| 1475 |
+
font-size: 21px;
|
| 1476 |
+
letter-spacing: -0.01em;
|
| 1477 |
+
}
|
| 1478 |
+
.modal-logo {
|
| 1479 |
+
width: 26px;
|
| 1480 |
+
height: 26px;
|
| 1481 |
+
object-fit: contain;
|
| 1482 |
+
}
|
| 1483 |
+
.modal-actions {
|
| 1484 |
+
display: flex;
|
| 1485 |
+
align-items: center;
|
| 1486 |
+
gap: 8px;
|
| 1487 |
+
}
|
| 1488 |
+
.btn {
|
| 1489 |
+
font-family: var(--sans);
|
| 1490 |
+
font-size: 13.5px;
|
| 1491 |
+
font-weight: 600;
|
| 1492 |
+
border: 1px solid var(--line);
|
| 1493 |
+
background: var(--panel);
|
| 1494 |
+
color: var(--ink);
|
| 1495 |
+
border-radius: 9px;
|
| 1496 |
+
padding: 8px 13px;
|
| 1497 |
+
cursor: pointer;
|
| 1498 |
+
transition: background 0.12s, border-color 0.12s, color 0.12s;
|
| 1499 |
+
}
|
| 1500 |
+
.btn:hover {
|
| 1501 |
+
border-color: var(--accent);
|
| 1502 |
+
color: var(--accent-strong);
|
| 1503 |
+
}
|
| 1504 |
+
.btn.copied {
|
| 1505 |
+
border-color: #1a8a55;
|
| 1506 |
+
color: #1a8a55;
|
| 1507 |
+
}
|
| 1508 |
+
.btn.icon {
|
| 1509 |
+
font-size: 18px;
|
| 1510 |
+
line-height: 1;
|
| 1511 |
+
padding: 6px 11px;
|
| 1512 |
+
font-weight: 400;
|
| 1513 |
+
}
|
| 1514 |
+
.modal-body {
|
| 1515 |
+
padding: 20px 22px 26px;
|
| 1516 |
+
}
|
| 1517 |
+
.modal-intro {
|
| 1518 |
+
margin: 0 0 20px;
|
| 1519 |
+
color: var(--muted);
|
| 1520 |
+
line-height: 1.55;
|
| 1521 |
+
}
|
| 1522 |
+
#connect-steps {
|
| 1523 |
+
list-style: none;
|
| 1524 |
+
margin: 0;
|
| 1525 |
+
padding: 0;
|
| 1526 |
+
}
|
| 1527 |
+
#connect-steps li {
|
| 1528 |
+
margin-bottom: 18px;
|
| 1529 |
+
}
|
| 1530 |
+
.step-title {
|
| 1531 |
+
font-weight: 600;
|
| 1532 |
+
font-size: 14.5px;
|
| 1533 |
+
margin-bottom: 8px;
|
| 1534 |
+
}
|
| 1535 |
+
.codeblock {
|
| 1536 |
+
display: flex;
|
| 1537 |
+
align-items: center;
|
| 1538 |
+
gap: 8px;
|
| 1539 |
+
background: #17181c;
|
| 1540 |
+
border-radius: 10px;
|
| 1541 |
+
padding: 11px 12px 11px 15px;
|
| 1542 |
+
}
|
| 1543 |
+
.codeblock code {
|
| 1544 |
+
flex: 1;
|
| 1545 |
+
min-width: 0;
|
| 1546 |
+
overflow-x: auto;
|
| 1547 |
+
white-space: nowrap;
|
| 1548 |
+
font-family: var(--mono);
|
| 1549 |
+
font-size: 13px;
|
| 1550 |
+
color: #f0efff;
|
| 1551 |
+
background: none;
|
| 1552 |
+
padding: 0;
|
| 1553 |
+
}
|
| 1554 |
+
.codeblock .copy {
|
| 1555 |
+
flex: 0 0 auto;
|
| 1556 |
+
background: rgba(255, 255, 255, 0.08);
|
| 1557 |
+
color: #c3c4cb;
|
| 1558 |
+
border: 1px solid rgba(255, 255, 255, 0.14);
|
| 1559 |
+
border-radius: 7px;
|
| 1560 |
+
width: 30px;
|
| 1561 |
+
height: 30px;
|
| 1562 |
+
font-size: 14px;
|
| 1563 |
+
cursor: pointer;
|
| 1564 |
+
transition: background 0.12s, color 0.12s;
|
| 1565 |
+
}
|
| 1566 |
+
.codeblock .copy:hover {
|
| 1567 |
+
background: rgba(249, 115, 22, 0.2);
|
| 1568 |
+
color: #fdba74;
|
| 1569 |
+
}
|
| 1570 |
+
.codeblock .copy.copied {
|
| 1571 |
+
color: #52d08a;
|
| 1572 |
+
}
|
| 1573 |
+
|
| 1574 |
+
@media (max-width: 720px) {
|
| 1575 |
+
#app {
|
| 1576 |
+
flex-direction: column;
|
| 1577 |
+
}
|
| 1578 |
+
#sidebar {
|
| 1579 |
+
width: 100%;
|
| 1580 |
+
flex: none;
|
| 1581 |
+
height: auto;
|
| 1582 |
+
position: static;
|
| 1583 |
+
}
|
| 1584 |
+
#content {
|
| 1585 |
+
display: block;
|
| 1586 |
+
width: 100%;
|
| 1587 |
+
padding: 28px 20px 80px;
|
| 1588 |
+
overflow-x: hidden;
|
| 1589 |
+
}
|
| 1590 |
+
#page {
|
| 1591 |
+
width: 100%;
|
| 1592 |
+
max-width: 100%;
|
| 1593 |
+
}
|
| 1594 |
+
#page h1 {
|
| 1595 |
+
font-size: 30px;
|
| 1596 |
+
}
|
| 1597 |
+
.cell-head {
|
| 1598 |
+
align-items: flex-start;
|
| 1599 |
+
flex-direction: column;
|
| 1600 |
+
gap: 4px;
|
| 1601 |
+
}
|
| 1602 |
+
}
|
logbook.js
ADDED
|
@@ -0,0 +1,2275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
"use strict";
|
| 3 |
+
|
| 4 |
+
let MANIFEST = null;
|
| 5 |
+
const PAGE_CACHE = {};
|
| 6 |
+
const UNFURL_CACHE = {};
|
| 7 |
+
const LIVE_RELOAD_MS = 1500;
|
| 8 |
+
const FIGURE_FRAME_WINDOWS = new Set();
|
| 9 |
+
let FIGURE_NAVIGATION_READY = false;
|
| 10 |
+
|
| 11 |
+
function esc(s) {
|
| 12 |
+
return String(s)
|
| 13 |
+
.replace(/&/g, "&")
|
| 14 |
+
.replace(/</g, "<")
|
| 15 |
+
.replace(/>/g, ">")
|
| 16 |
+
.replace(/"/g, """)
|
| 17 |
+
.replace(/'/g, "'");
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
function flattenTree(node, depth, acc) {
|
| 21 |
+
acc.push({ node: node, depth: depth });
|
| 22 |
+
(node.children || []).forEach((c) => flattenTree(c, depth + 1, acc));
|
| 23 |
+
return acc;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
function findNode(node, slug) {
|
| 27 |
+
if (node.slug === slug) return node;
|
| 28 |
+
for (const c of node.children || []) {
|
| 29 |
+
const hit = findNode(c, slug);
|
| 30 |
+
if (hit) return hit;
|
| 31 |
+
}
|
| 32 |
+
return null;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* -------------------- minimal markdown -------------------- */
|
| 36 |
+
|
| 37 |
+
function inline(text) {
|
| 38 |
+
let t = esc(text);
|
| 39 |
+
t = t.replace(/`([^`]+)`/g, (_, c) => `<code>${c}</code>`);
|
| 40 |
+
t = t.replace(/\*\*([^*]+)\*\*/g, (_, c) => `<strong>${c}</strong>`);
|
| 41 |
+
t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, txt, url) => {
|
| 42 |
+
const safe = esc(url);
|
| 43 |
+
const attrs = /^https?:/.test(url) ? ' target="_blank" rel="noopener"' : "";
|
| 44 |
+
const item = /^https?:/.test(url) ? classifyResource(url) : null;
|
| 45 |
+
const data = item
|
| 46 |
+
? ` class="res-link" data-res-url="${esc(item.url)}"`
|
| 47 |
+
: "";
|
| 48 |
+
return `<a href="${safe}"${attrs}${data}>${txt}</a>`;
|
| 49 |
+
});
|
| 50 |
+
t = t.replace(/(^|[\s(])(https?:\/\/[^\s<>)"'`]+)/g, (m, pre, url) => {
|
| 51 |
+
let rest = "";
|
| 52 |
+
const cut = url.search(/"|'|<|>/);
|
| 53 |
+
if (cut !== -1) {
|
| 54 |
+
rest = url.slice(cut);
|
| 55 |
+
url = url.slice(0, cut);
|
| 56 |
+
}
|
| 57 |
+
const trailing = (url.match(/[.,;:!?`]+$/) || [""])[0];
|
| 58 |
+
const clean = trailing ? url.slice(0, -trailing.length) : url;
|
| 59 |
+
if (!clean) return m;
|
| 60 |
+
const item = classifyResource(clean);
|
| 61 |
+
if (item) return `${pre}${resChipHtml(item)}${trailing}${rest}`;
|
| 62 |
+
return `${pre}<a href="${clean}" target="_blank" rel="noopener">${clean}</a>${trailing}${rest}`;
|
| 63 |
+
});
|
| 64 |
+
return t;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
function resChipHtml(item) {
|
| 68 |
+
return (
|
| 69 |
+
`<a class="res-chip" href="${esc(item.url)}" target="_blank" ` +
|
| 70 |
+
`rel="noopener" data-res-url="${esc(item.url)}">` +
|
| 71 |
+
`<span class="res-chip-ico">${RESOURCE_ICONS[item.kind]}</span>` +
|
| 72 |
+
`${esc(item.id)}</a>`
|
| 73 |
+
);
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
const URL_ONLY = /^(https?:\/\/[^\s]+)$/;
|
| 77 |
+
const DETECTED_URL =
|
| 78 |
+
/(https?:\/\/[^\s<>)\]"'`]+|trackio-local-dashboard:\/\/[^\s<>)\]"'`]+|trackio-artifact:\/\/[^\s<>)\]"'`]+|trackio-local-path:\/\/[^\s<>)\]"'`]+)/g;
|
| 79 |
+
|
| 80 |
+
function renderMarkdown(md, container) {
|
| 81 |
+
const cellRe = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
|
| 82 |
+
const tokens = [];
|
| 83 |
+
let pos = 0;
|
| 84 |
+
let found = false;
|
| 85 |
+
let match;
|
| 86 |
+
while ((match = cellRe.exec(md))) {
|
| 87 |
+
found = true;
|
| 88 |
+
tokens.push({
|
| 89 |
+
kind: "md",
|
| 90 |
+
text: md.slice(pos, match.index + match[1].length),
|
| 91 |
+
});
|
| 92 |
+
tokens.push({
|
| 93 |
+
kind: "cell",
|
| 94 |
+
meta: parseCellMeta(match[2]),
|
| 95 |
+
body: match[3],
|
| 96 |
+
});
|
| 97 |
+
pos = match.index + match[0].length;
|
| 98 |
+
}
|
| 99 |
+
tokens.push({ kind: "md", text: found ? md.slice(pos) : md });
|
| 100 |
+
|
| 101 |
+
for (let i = 0; i < tokens.length; i++) {
|
| 102 |
+
const t = tokens[i];
|
| 103 |
+
if (t.kind === "md") {
|
| 104 |
+
renderMarkdownPlain(t.text, container);
|
| 105 |
+
continue;
|
| 106 |
+
}
|
| 107 |
+
if (t.consumed) continue;
|
| 108 |
+
if (t.meta.type === "code") {
|
| 109 |
+
const arts = [];
|
| 110 |
+
for (let j = i + 1; j < tokens.length; j++) {
|
| 111 |
+
const n = tokens[j];
|
| 112 |
+
if (n.kind === "md") {
|
| 113 |
+
if (n.text.trim() === "") continue;
|
| 114 |
+
break;
|
| 115 |
+
}
|
| 116 |
+
if (n.meta.type === "artifact") {
|
| 117 |
+
arts.push(n);
|
| 118 |
+
n.consumed = true;
|
| 119 |
+
continue;
|
| 120 |
+
}
|
| 121 |
+
break;
|
| 122 |
+
}
|
| 123 |
+
renderCell(t.meta, t.body, container, arts);
|
| 124 |
+
} else {
|
| 125 |
+
renderCell(t.meta, t.body, container);
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
function parseCellMeta(raw) {
|
| 131 |
+
try {
|
| 132 |
+
return JSON.parse(raw);
|
| 133 |
+
} catch (e) {
|
| 134 |
+
return { type: "markdown", title: "Note" };
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
function renderMarkdownPlain(md, container) {
|
| 139 |
+
const lines = md.replace(/<!--[\s\S]*?-->/g, "").split("\n");
|
| 140 |
+
let i = 0;
|
| 141 |
+
let para = [];
|
| 142 |
+
|
| 143 |
+
function flushPara() {
|
| 144 |
+
if (!para.length) return;
|
| 145 |
+
const joined = para.join(" ").trim();
|
| 146 |
+
para = [];
|
| 147 |
+
if (!joined) return;
|
| 148 |
+
if (/^trackio-artifact:\/\/\S+$/.test(joined)) return;
|
| 149 |
+
if (/^trackio-local-path:\/\/\S+$/.test(joined)) return;
|
| 150 |
+
if (joined.indexOf("📦 Artifact") !== -1) {
|
| 151 |
+
const div = document.createElement("div");
|
| 152 |
+
div.className = "artifact-chip";
|
| 153 |
+
div.innerHTML = ARTIFACT_ICON_IMG + inline(joined.replace(/📦\s*/, ""));
|
| 154 |
+
container.appendChild(div);
|
| 155 |
+
return;
|
| 156 |
+
}
|
| 157 |
+
if (URL_ONLY.test(joined) || IMG_PATH.test(joined)) {
|
| 158 |
+
const el = renderStandaloneUrl(joined);
|
| 159 |
+
if (el) container.appendChild(el);
|
| 160 |
+
return;
|
| 161 |
+
}
|
| 162 |
+
const p = document.createElement("p");
|
| 163 |
+
p.innerHTML = inline(joined);
|
| 164 |
+
container.appendChild(p);
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
while (i < lines.length) {
|
| 168 |
+
const line = lines[i];
|
| 169 |
+
const trimmed = line.trim();
|
| 170 |
+
|
| 171 |
+
if (trimmed === "") {
|
| 172 |
+
flushPara();
|
| 173 |
+
i++;
|
| 174 |
+
continue;
|
| 175 |
+
}
|
| 176 |
+
const fence = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
|
| 177 |
+
if (fence) {
|
| 178 |
+
flushPara();
|
| 179 |
+
const marker = fence[1][0];
|
| 180 |
+
const closeRe = new RegExp("^" + marker + "{" + fence[1].length + ",}\\s*$");
|
| 181 |
+
const info = fence[2].trim();
|
| 182 |
+
const buf = [];
|
| 183 |
+
i++;
|
| 184 |
+
while (i < lines.length && !closeRe.test(lines[i].trim())) {
|
| 185 |
+
buf.push(lines[i]);
|
| 186 |
+
i++;
|
| 187 |
+
}
|
| 188 |
+
i++;
|
| 189 |
+
const lang = (info.split(/\s+/)[0] || "").toLowerCase();
|
| 190 |
+
const tm = info.match(/title=(\S+)/);
|
| 191 |
+
container.appendChild(
|
| 192 |
+
renderCode(buf.join("\n"), lang, tm ? tm[1] : null)
|
| 193 |
+
);
|
| 194 |
+
continue;
|
| 195 |
+
}
|
| 196 |
+
if (trimmed === "---") {
|
| 197 |
+
flushPara();
|
| 198 |
+
container.appendChild(document.createElement("hr"));
|
| 199 |
+
i++;
|
| 200 |
+
continue;
|
| 201 |
+
}
|
| 202 |
+
const h = trimmed.match(/^(#{1,4})\s+(.*)$/);
|
| 203 |
+
if (h) {
|
| 204 |
+
flushPara();
|
| 205 |
+
const el = document.createElement("h" + h[1].length);
|
| 206 |
+
el.innerHTML = inline(h[2]);
|
| 207 |
+
container.appendChild(el);
|
| 208 |
+
i++;
|
| 209 |
+
continue;
|
| 210 |
+
}
|
| 211 |
+
if (
|
| 212 |
+
trimmed.startsWith("|") &&
|
| 213 |
+
i + 1 < lines.length &&
|
| 214 |
+
/^\|?[\s:|-]*-{2,}[\s:|-]*\|?$/.test(lines[i + 1].trim())
|
| 215 |
+
) {
|
| 216 |
+
flushPara();
|
| 217 |
+
const rows = [];
|
| 218 |
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
| 219 |
+
rows.push(parseRow(lines[i].trim()));
|
| 220 |
+
i++;
|
| 221 |
+
}
|
| 222 |
+
renderTable(rows, container);
|
| 223 |
+
continue;
|
| 224 |
+
}
|
| 225 |
+
if (trimmed.startsWith("> ")) {
|
| 226 |
+
flushPara();
|
| 227 |
+
const bq = document.createElement("blockquote");
|
| 228 |
+
bq.innerHTML = inline(trimmed.slice(2));
|
| 229 |
+
container.appendChild(bq);
|
| 230 |
+
i++;
|
| 231 |
+
continue;
|
| 232 |
+
}
|
| 233 |
+
if (/^`[^`]+`$/.test(trimmed)) {
|
| 234 |
+
flushPara();
|
| 235 |
+
const el = document.createElement("div");
|
| 236 |
+
el.className = "ts";
|
| 237 |
+
el.textContent = trimmed.replace(/`/g, "");
|
| 238 |
+
container.appendChild(el);
|
| 239 |
+
i++;
|
| 240 |
+
continue;
|
| 241 |
+
}
|
| 242 |
+
if (trimmed.startsWith("- ")) {
|
| 243 |
+
flushPara();
|
| 244 |
+
const items = [];
|
| 245 |
+
while (i < lines.length && lines[i].trim().startsWith("- ")) {
|
| 246 |
+
items.push(lines[i].trim().slice(2).trim());
|
| 247 |
+
i++;
|
| 248 |
+
}
|
| 249 |
+
renderList(items, container);
|
| 250 |
+
continue;
|
| 251 |
+
}
|
| 252 |
+
para.push(trimmed);
|
| 253 |
+
i++;
|
| 254 |
+
}
|
| 255 |
+
flushPara();
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
function renderCell(meta, body, container, artifacts) {
|
| 259 |
+
const cell = document.createElement("section");
|
| 260 |
+
cell.className = `cell ${meta.type || "markdown"}`;
|
| 261 |
+
if (meta.id) cell.dataset.cellId = meta.id;
|
| 262 |
+
if (isPinned(meta)) cell.classList.add("pinned-source");
|
| 263 |
+
|
| 264 |
+
const head = document.createElement("div");
|
| 265 |
+
head.className = "cell-head";
|
| 266 |
+
const rawTitle = (meta.title || "").trim();
|
| 267 |
+
const title = rawTitle && rawTitle.toLowerCase() !== "untitled" ? esc(rawTitle) : "";
|
| 268 |
+
const when = meta.created_at ? `<span>${esc(formatTime(meta.created_at))}</span>` : "";
|
| 269 |
+
head.innerHTML =
|
| 270 |
+
(title ? `<div class="cell-title">${title}</div>` : "") +
|
| 271 |
+
`<div class="cell-meta">${when}</div>`;
|
| 272 |
+
if (!title) head.classList.add("no-title");
|
| 273 |
+
cell.appendChild(head);
|
| 274 |
+
|
| 275 |
+
const bodyEl = document.createElement("div");
|
| 276 |
+
bodyEl.className = "cell-body";
|
| 277 |
+
if (meta.type === "code") {
|
| 278 |
+
renderCodeCell(body, bodyEl, artifacts);
|
| 279 |
+
} else if (meta.type === "figure") {
|
| 280 |
+
cell.dataset.resUrl = `trackio-figure://${(meta.title || "Figure").trim()}`;
|
| 281 |
+
renderFigureCell(body, bodyEl, head);
|
| 282 |
+
} else if (meta.type === "artifact") {
|
| 283 |
+
renderMarkdownPlain(body, bodyEl);
|
| 284 |
+
const chip = bodyEl.querySelector(".artifact-chip");
|
| 285 |
+
const uri = body.match(
|
| 286 |
+
/(trackio-artifact:\/\/\S+|trackio-local-path:\/\/\S+|https:\/\/huggingface\.co\/buckets\/[^\s<)]+#\S+)/
|
| 287 |
+
);
|
| 288 |
+
if (chip && uri) chip.dataset.resUrl = uri[1];
|
| 289 |
+
} else if (meta.type === "dashboard") {
|
| 290 |
+
const sp = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
|
| 291 |
+
cell.dataset.resUrl = sp
|
| 292 |
+
? sp[0]
|
| 293 |
+
: `trackio-local-dashboard://${(meta.dashboard_project || "").trim()}`;
|
| 294 |
+
renderDashboardCell(meta, body, bodyEl, head);
|
| 295 |
+
} else {
|
| 296 |
+
const cleaned = stripDuplicateTitle(body, meta.title);
|
| 297 |
+
renderMarkdownPlain(cleaned, bodyEl);
|
| 298 |
+
renderDetectedEmbeds(cleaned, bodyEl);
|
| 299 |
+
}
|
| 300 |
+
cell.appendChild(bodyEl);
|
| 301 |
+
container.appendChild(cell);
|
| 302 |
+
return cell;
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
function isPinned(meta) {
|
| 306 |
+
return Boolean(meta && (meta.pinned === true || meta.pinned === "true"));
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
function stripDuplicateTitle(body, title) {
|
| 310 |
+
if (!title) return body;
|
| 311 |
+
const m = body.match(/^\s*#{1,6}\s+([^\n]+)\n?/);
|
| 312 |
+
if (!m) return body;
|
| 313 |
+
const norm = (s) =>
|
| 314 |
+
s
|
| 315 |
+
.toLowerCase()
|
| 316 |
+
.replace(/[*_`#]/g, "")
|
| 317 |
+
.replace(/\s+/g, " ")
|
| 318 |
+
.trim();
|
| 319 |
+
return norm(m[1]) === norm(title) ? body.slice(m[0].length) : body;
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
function formatTime(iso) {
|
| 323 |
+
const d = new Date(iso);
|
| 324 |
+
if (Number.isNaN(d.getTime())) return iso;
|
| 325 |
+
return d.toLocaleString(undefined, {
|
| 326 |
+
month: "short",
|
| 327 |
+
day: "numeric",
|
| 328 |
+
hour: "2-digit",
|
| 329 |
+
minute: "2-digit",
|
| 330 |
+
});
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
function parseFences(text) {
|
| 334 |
+
const fenceRe = /(`{3,4}|~{3,4})([^\n]*)\n([\s\S]*?)\n\1/g;
|
| 335 |
+
const parts = [];
|
| 336 |
+
let pos = 0;
|
| 337 |
+
let match;
|
| 338 |
+
while ((match = fenceRe.exec(text))) {
|
| 339 |
+
if (match.index > pos) {
|
| 340 |
+
parts.push({ kind: "text", text: text.slice(pos, match.index) });
|
| 341 |
+
}
|
| 342 |
+
const info = match[2].trim();
|
| 343 |
+
const lang = (info.split(/\s+/)[0] || "").toLowerCase();
|
| 344 |
+
const titleMatch = info.match(/title=(\S+)/);
|
| 345 |
+
parts.push({
|
| 346 |
+
kind: lang === "result" || lang === "output" ? "output" : "code",
|
| 347 |
+
lang,
|
| 348 |
+
title: titleMatch ? titleMatch[1] : null,
|
| 349 |
+
text: match[3],
|
| 350 |
+
});
|
| 351 |
+
pos = match.index + match[0].length;
|
| 352 |
+
}
|
| 353 |
+
if (pos < text.length) parts.push({ kind: "text", text: text.slice(pos) });
|
| 354 |
+
return parts;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
function fitFigureFrame(frame, wrap) {
|
| 358 |
+
let doc;
|
| 359 |
+
try {
|
| 360 |
+
doc = frame.contentDocument;
|
| 361 |
+
} catch (e) {
|
| 362 |
+
return;
|
| 363 |
+
}
|
| 364 |
+
if (!doc || !doc.body) return;
|
| 365 |
+
frame.style.transform = "none";
|
| 366 |
+
frame.style.width = "100%";
|
| 367 |
+
frame.style.height = "auto";
|
| 368 |
+
frame.style.position = "";
|
| 369 |
+
frame.style.left = "";
|
| 370 |
+
frame.style.top = "";
|
| 371 |
+
const avail = wrap.clientWidth;
|
| 372 |
+
const isFullscreen =
|
| 373 |
+
document.fullscreenElement === wrap ||
|
| 374 |
+
document.webkitFullscreenElement === wrap;
|
| 375 |
+
const availHeight = isFullscreen ? wrap.clientHeight : Infinity;
|
| 376 |
+
const cw = Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth, 1);
|
| 377 |
+
const ch = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight, 1);
|
| 378 |
+
const scale = Math.min(avail / cw, availHeight / ch);
|
| 379 |
+
if (avail && scale < 1 - 1e-3) {
|
| 380 |
+
frame.style.width = `${cw}px`;
|
| 381 |
+
frame.style.height = `${ch}px`;
|
| 382 |
+
frame.style.transformOrigin = "top left";
|
| 383 |
+
frame.style.transform = `scale(${scale})`;
|
| 384 |
+
if (isFullscreen) {
|
| 385 |
+
frame.style.position = "absolute";
|
| 386 |
+
frame.style.left = `${Math.max(0, (avail - cw * scale) / 2)}px`;
|
| 387 |
+
frame.style.top = `${Math.max(0, (availHeight - ch * scale) / 2)}px`;
|
| 388 |
+
wrap.style.height = "100%";
|
| 389 |
+
} else {
|
| 390 |
+
wrap.style.height = `${Math.ceil(ch * scale)}px`;
|
| 391 |
+
}
|
| 392 |
+
} else {
|
| 393 |
+
frame.style.width = "100%";
|
| 394 |
+
frame.style.height = `${ch}px`;
|
| 395 |
+
wrap.style.height = isFullscreen ? "100%" : `${ch}px`;
|
| 396 |
+
}
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
function attachFigureFit(frame, wrap) {
|
| 400 |
+
const refit = () => fitFigureFrame(frame, wrap);
|
| 401 |
+
frame.addEventListener("load", refit);
|
| 402 |
+
if (window.ResizeObserver) {
|
| 403 |
+
const ro = new ResizeObserver(() => refit());
|
| 404 |
+
ro.observe(wrap);
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
function renderFigureCell(text, container, head) {
|
| 409 |
+
const parts = parseFences(text);
|
| 410 |
+
const htmlPart = parts.find((part) => part.lang === "html");
|
| 411 |
+
const rawPart = parts.find((part) => part.lang === "raw");
|
| 412 |
+
if (!htmlPart || !htmlPart.text.trim()) {
|
| 413 |
+
const empty = document.createElement("p");
|
| 414 |
+
empty.className = "muted";
|
| 415 |
+
empty.textContent = "No figure HTML.";
|
| 416 |
+
container.appendChild(empty);
|
| 417 |
+
return;
|
| 418 |
+
}
|
| 419 |
+
const frame = document.createElement("iframe");
|
| 420 |
+
frame.className = "figure-frame";
|
| 421 |
+
frame.sandbox = "allow-scripts allow-same-origin";
|
| 422 |
+
frame.loading = "lazy";
|
| 423 |
+
frame.srcdoc = htmlPart.text;
|
| 424 |
+
registerFigureNavigation(frame);
|
| 425 |
+
const figWrap = document.createElement("div");
|
| 426 |
+
figWrap.className = "figure-fit";
|
| 427 |
+
figWrap.appendChild(frame);
|
| 428 |
+
attachFigureFit(frame, figWrap);
|
| 429 |
+
if (head) {
|
| 430 |
+
const metaEl = head.querySelector(".cell-meta");
|
| 431 |
+
if (metaEl)
|
| 432 |
+
metaEl.insertBefore(buildFullscreenControl(figWrap, frame), metaEl.firstChild);
|
| 433 |
+
}
|
| 434 |
+
if (!rawPart || !rawPart.text.trim()) {
|
| 435 |
+
container.appendChild(figWrap);
|
| 436 |
+
return;
|
| 437 |
+
}
|
| 438 |
+
const sw = document.createElement("div");
|
| 439 |
+
sw.className = "fig-switch";
|
| 440 |
+
const thumb = document.createElement("span");
|
| 441 |
+
thumb.className = "fig-switch-thumb";
|
| 442 |
+
const figBtn = document.createElement("button");
|
| 443 |
+
figBtn.type = "button";
|
| 444 |
+
figBtn.className = "active";
|
| 445 |
+
figBtn.textContent = "Figure";
|
| 446 |
+
const rawBtn = document.createElement("button");
|
| 447 |
+
rawBtn.type = "button";
|
| 448 |
+
rawBtn.textContent = "Raw";
|
| 449 |
+
sw.appendChild(thumb);
|
| 450 |
+
sw.appendChild(figBtn);
|
| 451 |
+
sw.appendChild(rawBtn);
|
| 452 |
+
const rawView = document.createElement("div");
|
| 453 |
+
rawView.className = "figure-raw";
|
| 454 |
+
rawView.hidden = true;
|
| 455 |
+
const pre = document.createElement("pre");
|
| 456 |
+
const code = document.createElement("code");
|
| 457 |
+
code.textContent = rawPart.text;
|
| 458 |
+
pre.appendChild(code);
|
| 459 |
+
rawView.appendChild(pre);
|
| 460 |
+
rawView.appendChild(copySnippetBtn(rawPart.text));
|
| 461 |
+
const select = (showRaw) => {
|
| 462 |
+
sw.classList.toggle("raw", showRaw);
|
| 463 |
+
figBtn.classList.toggle("active", !showRaw);
|
| 464 |
+
rawBtn.classList.toggle("active", showRaw);
|
| 465 |
+
figWrap.hidden = showRaw;
|
| 466 |
+
rawView.hidden = !showRaw;
|
| 467 |
+
};
|
| 468 |
+
figBtn.addEventListener("click", () => select(false));
|
| 469 |
+
rawBtn.addEventListener("click", () => select(true));
|
| 470 |
+
if (head) {
|
| 471 |
+
head.insertBefore(sw, head.querySelector(".cell-meta"));
|
| 472 |
+
} else {
|
| 473 |
+
container.appendChild(sw);
|
| 474 |
+
}
|
| 475 |
+
container.appendChild(figWrap);
|
| 476 |
+
container.appendChild(rawView);
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
// Poster embeds can send `{ type: "trackio-logbook:navigate", target: "..." }`
|
| 480 |
+
// from their iframe. Only accept messages from figure frames we created, and
|
| 481 |
+
// only route to pages that are present in this logbook's manifest.
|
| 482 |
+
function registerFigureNavigation(frame) {
|
| 483 |
+
const registerFrameWindow = () => {
|
| 484 |
+
if (frame.contentWindow) FIGURE_FRAME_WINDOWS.add(frame.contentWindow);
|
| 485 |
+
};
|
| 486 |
+
// `srcdoc` replaces the initial about:blank document. Register after that
|
| 487 |
+
// navigation as well, so messages come from the live figure document.
|
| 488 |
+
frame.addEventListener("load", registerFrameWindow);
|
| 489 |
+
registerFrameWindow();
|
| 490 |
+
if (FIGURE_NAVIGATION_READY) return;
|
| 491 |
+
FIGURE_NAVIGATION_READY = true;
|
| 492 |
+
window.addEventListener("message", (event) => {
|
| 493 |
+
if (!FIGURE_FRAME_WINDOWS.has(event.source)) return;
|
| 494 |
+
const message = event.data;
|
| 495 |
+
if (!message || message.type !== "trackio-logbook:navigate") return;
|
| 496 |
+
const target = String(message.target || "").replace(/^#?\//, "");
|
| 497 |
+
if (!target || !MANIFEST || !findNode(MANIFEST.root, target)) return;
|
| 498 |
+
const hash = "#/" + target;
|
| 499 |
+
if (location.hash === hash) scrollToHash();
|
| 500 |
+
else location.hash = hash;
|
| 501 |
+
});
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
const FULLSCREEN_ICON =
|
| 505 |
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" ' +
|
| 506 |
+
'stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
| 507 |
+
'<path d="M8 3H3v5M16 3h5v5M21 16v5h-5M3 16v5h5"/>' +
|
| 508 |
+
'<path d="M3 8 8 3M16 3l5 5M21 16l-5 5M8 21l-5-5"/></svg>';
|
| 509 |
+
|
| 510 |
+
// Figures are rendered in same-origin iframes, so fullscreen the fitted
|
| 511 |
+
// wrapper rather than the iframe document. This uses the browser's native
|
| 512 |
+
// fullscreen UI and preserves the figure's existing responsive sizing.
|
| 513 |
+
function buildFullscreenControl(figWrap, frame) {
|
| 514 |
+
const wrap = document.createElement("span");
|
| 515 |
+
wrap.className = "cell-fullscreen";
|
| 516 |
+
const btn = document.createElement("button");
|
| 517 |
+
btn.type = "button";
|
| 518 |
+
btn.className = "cell-fullscreen-btn";
|
| 519 |
+
btn.setAttribute("aria-label", "Open figure in fullscreen");
|
| 520 |
+
btn.title = "Open figure in fullscreen";
|
| 521 |
+
btn.innerHTML = FULLSCREEN_ICON;
|
| 522 |
+
wrap.appendChild(btn);
|
| 523 |
+
|
| 524 |
+
btn.addEventListener("click", async () => {
|
| 525 |
+
const request = figWrap.requestFullscreen || figWrap.webkitRequestFullscreen;
|
| 526 |
+
if (!request) return;
|
| 527 |
+
try {
|
| 528 |
+
await request.call(figWrap);
|
| 529 |
+
} catch (_) {
|
| 530 |
+
// Fullscreen can be disabled by the embedding browser or policy.
|
| 531 |
+
}
|
| 532 |
+
});
|
| 533 |
+
document.addEventListener("fullscreenchange", () => {
|
| 534 |
+
if (document.fullscreenElement === figWrap) fitFigureFrame(frame, figWrap);
|
| 535 |
+
});
|
| 536 |
+
return wrap;
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
function extractUrls(text) {
|
| 540 |
+
const seen = new Set();
|
| 541 |
+
const urls = [];
|
| 542 |
+
let match;
|
| 543 |
+
while ((match = DETECTED_URL.exec(text))) {
|
| 544 |
+
const url = match[1].replace(/[.,;:!?'"`]+$/, "");
|
| 545 |
+
if (!seen.has(url)) {
|
| 546 |
+
seen.add(url);
|
| 547 |
+
urls.push(url);
|
| 548 |
+
}
|
| 549 |
+
}
|
| 550 |
+
DETECTED_URL.lastIndex = 0;
|
| 551 |
+
return urls;
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
const IMG_URL = /(\.(png|jpe?g|gif|svg|webp)(\?|$)|\/artifact_blob\/)/i;
|
| 555 |
+
|
| 556 |
+
function renderDetectedEmbeds(text, container) {
|
| 557 |
+
extractUrls(text).forEach((url) => {
|
| 558 |
+
if (url.startsWith("trackio-local-dashboard://")) {
|
| 559 |
+
const div = document.createElement("div");
|
| 560 |
+
div.className = "artifact-chip";
|
| 561 |
+
div.dataset.resUrl = url;
|
| 562 |
+
div.innerHTML =
|
| 563 |
+
"🎯 <strong>Local Trackio dashboard</strong> — publish the logbook to share it";
|
| 564 |
+
container.appendChild(div);
|
| 565 |
+
} else if (IMG_URL.test(url)) {
|
| 566 |
+
container.appendChild(renderImage(url));
|
| 567 |
+
} else if (/huggingface\.co\/spaces\//.test(url)) {
|
| 568 |
+
maybeEmbedTrackioSpace(url, container);
|
| 569 |
+
}
|
| 570 |
+
});
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
function renderStandaloneUrl(url) {
|
| 574 |
+
if (IMG_URL.test(url) || IMG_PATH.test(url)) return renderImage(url);
|
| 575 |
+
const item = classifyResource(url);
|
| 576 |
+
if (item) {
|
| 577 |
+
const marker = document.createElement("span");
|
| 578 |
+
marker.className = "resource-anchor";
|
| 579 |
+
marker.dataset.resUrl = item.url;
|
| 580 |
+
marker.setAttribute("aria-hidden", "true");
|
| 581 |
+
return marker;
|
| 582 |
+
}
|
| 583 |
+
const p = document.createElement("p");
|
| 584 |
+
p.innerHTML = inline(url);
|
| 585 |
+
return p;
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
function renderImage(url) {
|
| 589 |
+
const a = document.createElement("a");
|
| 590 |
+
a.className = "unfurl image";
|
| 591 |
+
a.href = url;
|
| 592 |
+
a.target = "_blank";
|
| 593 |
+
a.rel = "noopener";
|
| 594 |
+
const img = document.createElement("img");
|
| 595 |
+
img.loading = "lazy";
|
| 596 |
+
img.src = url;
|
| 597 |
+
img.alt = "artifact image";
|
| 598 |
+
a.appendChild(img);
|
| 599 |
+
return a;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
function maybeEmbedTrackioSpace(url, container) {
|
| 603 |
+
const id = url.split("/spaces/")[1].split(/[?#]/)[0].replace(/\/$/, "");
|
| 604 |
+
const holder = document.createElement("div");
|
| 605 |
+
container.appendChild(holder);
|
| 606 |
+
getJSON(`https://huggingface.co/api/spaces/${id}`).then((d) => {
|
| 607 |
+
const tags = (d && d.tags) || [];
|
| 608 |
+
if (tags.some((t) => String(t).toLowerCase() === "trackio")) {
|
| 609 |
+
renderTrackioSpaceEmbed(holder, url, id);
|
| 610 |
+
} else {
|
| 611 |
+
holder.remove();
|
| 612 |
+
}
|
| 613 |
+
});
|
| 614 |
+
}
|
| 615 |
+
|
| 616 |
+
function jpGutter(label) {
|
| 617 |
+
const g = document.createElement("div");
|
| 618 |
+
g.className = "jp-gutter";
|
| 619 |
+
g.textContent = label;
|
| 620 |
+
return g;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
function renderOutArtifact(info) {
|
| 624 |
+
const remote = !info.local && !!info.url;
|
| 625 |
+
const el = document.createElement(remote ? "a" : "div");
|
| 626 |
+
el.className = "out-artifact";
|
| 627 |
+
if (remote) {
|
| 628 |
+
el.href = info.url;
|
| 629 |
+
el.target = "_blank";
|
| 630 |
+
el.rel = "noopener";
|
| 631 |
+
}
|
| 632 |
+
el.dataset.resUrl = info.resUrl;
|
| 633 |
+
const parts = [info.type, info.size].filter(Boolean).map(esc);
|
| 634 |
+
const state = remote
|
| 635 |
+
? `<span class="out-artifact-state open">Open ↗</span>`
|
| 636 |
+
: `<span class="out-artifact-state">publish to share</span>`;
|
| 637 |
+
const meta = parts.length ? `${parts.join(" · ")} · ${state}` : state;
|
| 638 |
+
el.innerHTML =
|
| 639 |
+
`<span class="out-artifact-ico">${ARTIFACT_ICON_IMG}</span>` +
|
| 640 |
+
`<span class="out-artifact-name">${esc(info.name)}</span>` +
|
| 641 |
+
`<span class="out-artifact-meta">${meta}</span>`;
|
| 642 |
+
return el;
|
| 643 |
+
}
|
| 644 |
+
|
| 645 |
+
function renderCodeCell(body, container, artifacts) {
|
| 646 |
+
const parts = parseFences(body);
|
| 647 |
+
const block = document.createElement("div");
|
| 648 |
+
block.className = "jp";
|
| 649 |
+
const input = document.createElement("div");
|
| 650 |
+
input.className = "jp-in";
|
| 651 |
+
const inputBody = document.createElement("div");
|
| 652 |
+
inputBody.className = "jp-in-body";
|
| 653 |
+
input.appendChild(jpGutter("In"));
|
| 654 |
+
input.appendChild(inputBody);
|
| 655 |
+
let metaEl = null;
|
| 656 |
+
let outputEl = null;
|
| 657 |
+
let outBody = null;
|
| 658 |
+
const ensureOut = () => {
|
| 659 |
+
if (outputEl) return;
|
| 660 |
+
outputEl = document.createElement("div");
|
| 661 |
+
outputEl.className = "jp-out";
|
| 662 |
+
outputEl.appendChild(jpGutter("Out"));
|
| 663 |
+
outBody = document.createElement("div");
|
| 664 |
+
outBody.className = "jp-out-body";
|
| 665 |
+
outputEl.appendChild(outBody);
|
| 666 |
+
};
|
| 667 |
+
const embedTexts = [];
|
| 668 |
+
parts.forEach((part) => {
|
| 669 |
+
if (part.kind === "text") {
|
| 670 |
+
const text = part.text.trim();
|
| 671 |
+
if (!text) return;
|
| 672 |
+
if (/^exit\s+\S+(\s|·)/.test(text)) {
|
| 673 |
+
metaEl = document.createElement("div");
|
| 674 |
+
metaEl.className = "jp-meta";
|
| 675 |
+
metaEl.textContent = text.replace(
|
| 676 |
+
/\s*·\s*[A-Z][a-z]{2} \d{1,2}, \d{4}.*$/,
|
| 677 |
+
""
|
| 678 |
+
);
|
| 679 |
+
} else {
|
| 680 |
+
renderMarkdownPlain(text, container);
|
| 681 |
+
embedTexts.push(text);
|
| 682 |
+
}
|
| 683 |
+
return;
|
| 684 |
+
}
|
| 685 |
+
if (part.kind === "output") {
|
| 686 |
+
ensureOut();
|
| 687 |
+
const pre = document.createElement("pre");
|
| 688 |
+
pre.className = "jp-out-pre";
|
| 689 |
+
const c = document.createElement("code");
|
| 690 |
+
c.textContent = part.text;
|
| 691 |
+
pre.appendChild(c);
|
| 692 |
+
outBody.appendChild(pre);
|
| 693 |
+
outputEl.appendChild(copySnippetBtn(part.text));
|
| 694 |
+
embedTexts.push(part.text);
|
| 695 |
+
return;
|
| 696 |
+
}
|
| 697 |
+
inputBody.appendChild(renderCode(part.text, part.lang, part.title));
|
| 698 |
+
});
|
| 699 |
+
if (artifacts && artifacts.length) {
|
| 700 |
+
ensureOut();
|
| 701 |
+
const artWrap = document.createElement("div");
|
| 702 |
+
artWrap.className = "jp-artifacts";
|
| 703 |
+
artifacts.forEach((a) => {
|
| 704 |
+
artWrap.appendChild(
|
| 705 |
+
renderOutArtifact(artifactInfoFromCell(a.meta, a.body))
|
| 706 |
+
);
|
| 707 |
+
});
|
| 708 |
+
outBody.appendChild(artWrap);
|
| 709 |
+
}
|
| 710 |
+
if (inputBody.childNodes.length > 0) block.appendChild(input);
|
| 711 |
+
if (metaEl) block.appendChild(metaEl);
|
| 712 |
+
if (outputEl) block.appendChild(outputEl);
|
| 713 |
+
if (block.childNodes.length) container.appendChild(block);
|
| 714 |
+
embedTexts.forEach((text) => renderDetectedEmbeds(text, container));
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
function parseRow(line) {
|
| 718 |
+
let s = line.trim();
|
| 719 |
+
if (s.startsWith("|")) s = s.slice(1);
|
| 720 |
+
if (s.endsWith("|")) s = s.slice(0, -1);
|
| 721 |
+
return s.split(/(?<!\\)\|/).map((c) => c.replace(/\\\|/g, "|").trim());
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
const TRUTHY = ["x", "✓", "✔", "yes", "done", "true", "[x]"];
|
| 725 |
+
const CHIP_COLORS = [
|
| 726 |
+
["#e7f0ff", "#2158d0"],
|
| 727 |
+
["#fde8ec", "#c62a4b"],
|
| 728 |
+
["#e6f7ee", "#1a8a55"],
|
| 729 |
+
["#fdf0e0", "#b26a12"],
|
| 730 |
+
["#efe9ff", "#5b3bd6"],
|
| 731 |
+
["#e6f6f8", "#127b88"],
|
| 732 |
+
];
|
| 733 |
+
|
| 734 |
+
function chipColor(name) {
|
| 735 |
+
let h = 0;
|
| 736 |
+
for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
|
| 737 |
+
return CHIP_COLORS[h % CHIP_COLORS.length];
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
const STATUS_MAP = {
|
| 741 |
+
"": ["Planned", "gray"],
|
| 742 |
+
planned: ["Planned", "gray"],
|
| 743 |
+
todo: ["Planned", "gray"],
|
| 744 |
+
"to do": ["Planned", "gray"],
|
| 745 |
+
backlog: ["Planned", "gray"],
|
| 746 |
+
"in progress": ["In progress", "amber"],
|
| 747 |
+
"in-progress": ["In progress", "amber"],
|
| 748 |
+
wip: ["In progress", "amber"],
|
| 749 |
+
running: ["In progress", "amber"],
|
| 750 |
+
active: ["In progress", "amber"],
|
| 751 |
+
done: ["Done", "green"],
|
| 752 |
+
complete: ["Done", "green"],
|
| 753 |
+
completed: ["Done", "green"],
|
| 754 |
+
blocked: ["Blocked", "red"],
|
| 755 |
+
failed: ["Failed", "red"],
|
| 756 |
+
abandoned: ["Abandoned", "gray"],
|
| 757 |
+
};
|
| 758 |
+
|
| 759 |
+
function statusBadge(val) {
|
| 760 |
+
const [label, tone] = STATUS_MAP[val.toLowerCase()] || [val || "—", "gray"];
|
| 761 |
+
return `<span class="badge ${tone}">${esc(label)}</span>`;
|
| 762 |
+
}
|
| 763 |
+
|
| 764 |
+
function renderTable(rows, container) {
|
| 765 |
+
if (rows.length < 2) return;
|
| 766 |
+
const header = rows[0];
|
| 767 |
+
const body = rows.slice(2);
|
| 768 |
+
const roles = header.map((h) => {
|
| 769 |
+
const t = h.toLowerCase();
|
| 770 |
+
if (t.includes("status") || t.includes("state")) return "status";
|
| 771 |
+
if (t.includes("progress") || t.includes("complete") || t.includes("done"))
|
| 772 |
+
return "check";
|
| 773 |
+
if (t === "who" || t.includes("assign") || t.includes("owner")) return "who";
|
| 774 |
+
return "text";
|
| 775 |
+
});
|
| 776 |
+
const table = document.createElement("table");
|
| 777 |
+
table.className = "board";
|
| 778 |
+
const thead = document.createElement("thead");
|
| 779 |
+
const htr = document.createElement("tr");
|
| 780 |
+
header.forEach((h, c) => {
|
| 781 |
+
const th = document.createElement("th");
|
| 782 |
+
th.textContent = h;
|
| 783 |
+
if (roles[c] === "check") th.className = "col-check";
|
| 784 |
+
htr.appendChild(th);
|
| 785 |
+
});
|
| 786 |
+
thead.appendChild(htr);
|
| 787 |
+
table.appendChild(thead);
|
| 788 |
+
const tbody = document.createElement("tbody");
|
| 789 |
+
body.forEach((cells) => {
|
| 790 |
+
const nonEmpty = cells.filter((x) => x !== "").length;
|
| 791 |
+
if (header.length > 1 && nonEmpty === 1 && cells[0]) {
|
| 792 |
+
const tr = document.createElement("tr");
|
| 793 |
+
tr.className = "section-row";
|
| 794 |
+
const td = document.createElement("td");
|
| 795 |
+
td.colSpan = header.length;
|
| 796 |
+
td.innerHTML = inline(cells[0]);
|
| 797 |
+
tr.appendChild(td);
|
| 798 |
+
tbody.appendChild(tr);
|
| 799 |
+
return;
|
| 800 |
+
}
|
| 801 |
+
const tr = document.createElement("tr");
|
| 802 |
+
header.forEach((_, c) => {
|
| 803 |
+
const td = document.createElement("td");
|
| 804 |
+
const val = (cells[c] || "").trim();
|
| 805 |
+
if (roles[c] === "status") {
|
| 806 |
+
td.className = "col-status";
|
| 807 |
+
td.innerHTML = statusBadge(val);
|
| 808 |
+
} else if (roles[c] === "check") {
|
| 809 |
+
td.className = "col-check";
|
| 810 |
+
const on = TRUTHY.indexOf(val.toLowerCase()) !== -1;
|
| 811 |
+
td.innerHTML = `<span class="box ${on ? "on" : ""}">${on ? "✓" : ""}</span>`;
|
| 812 |
+
} else if (roles[c] === "who") {
|
| 813 |
+
if (!val || /^to assign$/i.test(val)) {
|
| 814 |
+
td.innerHTML = `<span class="who-chip muted">${esc(val || "—")}</span>`;
|
| 815 |
+
} else {
|
| 816 |
+
const [bg, fg] = chipColor(val);
|
| 817 |
+
td.innerHTML = `<span class="who-chip" style="background:${bg};color:${fg}">${esc(val)}</span>`;
|
| 818 |
+
}
|
| 819 |
+
} else {
|
| 820 |
+
td.innerHTML = inline(val);
|
| 821 |
+
}
|
| 822 |
+
tr.appendChild(td);
|
| 823 |
+
});
|
| 824 |
+
const link = tr.querySelector('a[href^="#/"]');
|
| 825 |
+
if (link) {
|
| 826 |
+
tr.classList.add("linked-row");
|
| 827 |
+
tr.addEventListener("click", (e) => {
|
| 828 |
+
if (e.target.tagName !== "A") location.hash = link.getAttribute("href");
|
| 829 |
+
});
|
| 830 |
+
}
|
| 831 |
+
tbody.appendChild(tr);
|
| 832 |
+
});
|
| 833 |
+
table.appendChild(tbody);
|
| 834 |
+
const wrap = document.createElement("div");
|
| 835 |
+
wrap.className = "board-wrap";
|
| 836 |
+
wrap.appendChild(table);
|
| 837 |
+
container.appendChild(wrap);
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
const HL_RULES = {
|
| 841 |
+
python: [
|
| 842 |
+
["comment", /#[^\n]*/],
|
| 843 |
+
["string", /'''[\s\S]*?'''|"""[\s\S]*?"""|'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
|
| 844 |
+
[
|
| 845 |
+
"keyword",
|
| 846 |
+
/\b(?:def|class|return|if|elif|else|for|while|import|from|as|with|try|except|finally|raise|in|not|and|or|is|None|True|False|lambda|yield|global|nonlocal|assert|pass|break|continue|async|await|print)\b/,
|
| 847 |
+
],
|
| 848 |
+
["number", /\b\d[\d_.eE+-]*\b/],
|
| 849 |
+
],
|
| 850 |
+
bash: [
|
| 851 |
+
["comment", /#[^\n]*/],
|
| 852 |
+
["string", /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
|
| 853 |
+
["keyword", /\b(?:if|then|else|fi|for|in|do|done|while|case|esac|function|export|source|echo|cd|return|local)\b/],
|
| 854 |
+
["number", /(?<=\s)-{1,2}[a-zA-Z][\w-]*/],
|
| 855 |
+
],
|
| 856 |
+
json: [
|
| 857 |
+
["string", /"(?:\\.|[^"\\])*"/],
|
| 858 |
+
["keyword", /\b(?:true|false|null)\b/],
|
| 859 |
+
["number", /-?\b\d[\d.eE+-]*\b/],
|
| 860 |
+
],
|
| 861 |
+
yaml: [
|
| 862 |
+
["comment", /#[^\n]*/],
|
| 863 |
+
["string", /'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"/],
|
| 864 |
+
["keyword", /\b(?:true|false|null|yes|no)\b/],
|
| 865 |
+
["number", /-?\b\d[\d.eE+-]*\b/],
|
| 866 |
+
],
|
| 867 |
+
};
|
| 868 |
+
HL_RULES.javascript = HL_RULES.python;
|
| 869 |
+
HL_RULES.typescript = HL_RULES.python;
|
| 870 |
+
HL_RULES.sql = [
|
| 871 |
+
["comment", /--[^\n]*/],
|
| 872 |
+
["string", /'(?:\\.|[^'\\])*'/],
|
| 873 |
+
[
|
| 874 |
+
"keyword",
|
| 875 |
+
/\b(?:SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|OUTER|ON|GROUP|BY|ORDER|LIMIT|INSERT|INTO|VALUES|UPDATE|SET|DELETE|CREATE|TABLE|AS|AND|OR|NOT|NULL|COUNT|DISTINCT|IN)\b/i,
|
| 876 |
+
],
|
| 877 |
+
["number", /\b\d[\d.]*\b/],
|
| 878 |
+
];
|
| 879 |
+
|
| 880 |
+
function highlightCode(code, lang) {
|
| 881 |
+
const rules = HL_RULES[lang];
|
| 882 |
+
if (!rules) return esc(code);
|
| 883 |
+
const combined = new RegExp(rules.map((r) => "(" + r[1].source + ")").join("|"), "g");
|
| 884 |
+
let out = "";
|
| 885 |
+
let last = 0;
|
| 886 |
+
let m;
|
| 887 |
+
while ((m = combined.exec(code))) {
|
| 888 |
+
if (m[0] === "") {
|
| 889 |
+
combined.lastIndex++;
|
| 890 |
+
continue;
|
| 891 |
+
}
|
| 892 |
+
out += esc(code.slice(last, m.index));
|
| 893 |
+
let gi = 1;
|
| 894 |
+
while (gi < m.length && m[gi] === undefined) gi++;
|
| 895 |
+
out += `<span class="tok-${rules[gi - 1][0]}">${esc(m[0])}</span>`;
|
| 896 |
+
last = m.index + m[0].length;
|
| 897 |
+
}
|
| 898 |
+
out += esc(code.slice(last));
|
| 899 |
+
return out;
|
| 900 |
+
}
|
| 901 |
+
|
| 902 |
+
function copySnippetBtn(text) {
|
| 903 |
+
const btn = document.createElement("button");
|
| 904 |
+
btn.type = "button";
|
| 905 |
+
btn.className = "copy-snippet";
|
| 906 |
+
btn.title = "Copy";
|
| 907 |
+
btn.textContent = "⧉";
|
| 908 |
+
btn.addEventListener("click", (e) => {
|
| 909 |
+
e.preventDefault();
|
| 910 |
+
e.stopPropagation();
|
| 911 |
+
copyText(text, btn, "⧉");
|
| 912 |
+
});
|
| 913 |
+
return btn;
|
| 914 |
+
}
|
| 915 |
+
|
| 916 |
+
function renderCode(code, lang, title) {
|
| 917 |
+
const pre = document.createElement("pre");
|
| 918 |
+
pre.className = "hl";
|
| 919 |
+
const c = document.createElement("code");
|
| 920 |
+
c.innerHTML = highlightCode(code, lang);
|
| 921 |
+
pre.appendChild(c);
|
| 922 |
+
if (!title) {
|
| 923 |
+
const wrap = document.createElement("div");
|
| 924 |
+
wrap.className = "snippet";
|
| 925 |
+
wrap.appendChild(pre);
|
| 926 |
+
wrap.appendChild(copySnippetBtn(code));
|
| 927 |
+
return wrap;
|
| 928 |
+
}
|
| 929 |
+
const det = document.createElement("details");
|
| 930 |
+
det.className = "code-accordion";
|
| 931 |
+
det.dataset.resUrl = `trackio-script://${title}`;
|
| 932 |
+
const sum = document.createElement("summary");
|
| 933 |
+
sum.innerHTML =
|
| 934 |
+
`<span class="code-ico"></></span>` +
|
| 935 |
+
`<span class="code-name">${esc(title)}</span>`;
|
| 936 |
+
sum
|
| 937 |
+
.querySelector(".code-name")
|
| 938 |
+
.addEventListener("click", (e) => e.preventDefault());
|
| 939 |
+
det.appendChild(sum);
|
| 940 |
+
const wrap = document.createElement("div");
|
| 941 |
+
wrap.className = "snippet";
|
| 942 |
+
wrap.appendChild(pre);
|
| 943 |
+
wrap.appendChild(copySnippetBtn(code));
|
| 944 |
+
det.appendChild(wrap);
|
| 945 |
+
return det;
|
| 946 |
+
}
|
| 947 |
+
|
| 948 |
+
const IMG_PATH = /^[^\s]+\.(png|jpe?g|gif|svg|webp)$/i;
|
| 949 |
+
|
| 950 |
+
function renderList(items, container) {
|
| 951 |
+
let ul = null;
|
| 952 |
+
items.forEach((item) => {
|
| 953 |
+
if (URL_ONLY.test(item) || IMG_PATH.test(item)) {
|
| 954 |
+
const el = renderStandaloneUrl(item);
|
| 955 |
+
if (el) {
|
| 956 |
+
ul = null;
|
| 957 |
+
container.appendChild(el);
|
| 958 |
+
}
|
| 959 |
+
} else if (item.indexOf("📦 Artifact") !== -1) {
|
| 960 |
+
ul = null;
|
| 961 |
+
const div = document.createElement("div");
|
| 962 |
+
div.className = "artifact-chip";
|
| 963 |
+
div.innerHTML = inline(item.replace("📦", "🪣"));
|
| 964 |
+
container.appendChild(div);
|
| 965 |
+
} else if (item.indexOf("trackio-local-dashboard://") !== -1) {
|
| 966 |
+
ul = null;
|
| 967 |
+
const uri = item.match(/trackio-local-dashboard:\/\/\S+/)?.[0] || "";
|
| 968 |
+
const div = document.createElement("div");
|
| 969 |
+
div.className = "artifact-chip";
|
| 970 |
+
if (uri) div.dataset.resUrl = uri;
|
| 971 |
+
div.innerHTML =
|
| 972 |
+
"🎯 <strong>Local dashboard</strong> — publish the logbook to share it";
|
| 973 |
+
container.appendChild(div);
|
| 974 |
+
} else {
|
| 975 |
+
if (!ul) {
|
| 976 |
+
ul = document.createElement("ul");
|
| 977 |
+
container.appendChild(ul);
|
| 978 |
+
}
|
| 979 |
+
const li = document.createElement("li");
|
| 980 |
+
li.innerHTML = inline(item);
|
| 981 |
+
ul.appendChild(li);
|
| 982 |
+
}
|
| 983 |
+
});
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
/* -------------------- resources rail -------------------- */
|
| 987 |
+
|
| 988 |
+
function fmt(n) {
|
| 989 |
+
if (n == null) return null;
|
| 990 |
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
| 991 |
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
| 992 |
+
return String(n);
|
| 993 |
+
}
|
| 994 |
+
|
| 995 |
+
const RESOURCE_SECTIONS = [
|
| 996 |
+
["dashboard", "Dashboards", "🎯"],
|
| 997 |
+
["model", "Models", "🤗"],
|
| 998 |
+
["dataset", "Datasets", "📊"],
|
| 999 |
+
["space", "Spaces", "🚀"],
|
| 1000 |
+
["artifact", "Artifacts", "🪣"],
|
| 1001 |
+
["paper", "Papers", "📄"],
|
| 1002 |
+
["repo", "Code", "🐙"],
|
| 1003 |
+
["job", "Jobs", "⚙️"],
|
| 1004 |
+
["bucket", "Buckets", "🪣"],
|
| 1005 |
+
];
|
| 1006 |
+
|
| 1007 |
+
const RESOURCE_ICONS = Object.fromEntries(
|
| 1008 |
+
RESOURCE_SECTIONS.map(([kind, , icon]) => [kind, icon])
|
| 1009 |
+
);
|
| 1010 |
+
|
| 1011 |
+
const ARTIFACT_ICON_IMG = `<img class="art-ico" src="./bucket-icon.svg" alt="" />`;
|
| 1012 |
+
const DASHBOARD_ICON_IMG = `<img class="art-ico" src="./trackio-logo-light.png" alt="" />`;
|
| 1013 |
+
|
| 1014 |
+
const RESOURCE_DESC = {
|
| 1015 |
+
dashboard: "Dashboard",
|
| 1016 |
+
model: "Model",
|
| 1017 |
+
dataset: "Dataset",
|
| 1018 |
+
space: "Space",
|
| 1019 |
+
artifact: "Artifact — in Bucket",
|
| 1020 |
+
paper: "Paper",
|
| 1021 |
+
repo: "Repository",
|
| 1022 |
+
job: "Job — status & logs",
|
| 1023 |
+
bucket: "Bucket — artifacts & data",
|
| 1024 |
+
};
|
| 1025 |
+
|
| 1026 |
+
const HF_NON_MODEL_PREFIX =
|
| 1027 |
+
/^(datasets|spaces|jobs|buckets|papers|blog|docs|api|posts|collections|organizations|settings|new|join|login|pricing|tasks|learn|chat|models)(\/|$)/;
|
| 1028 |
+
|
| 1029 |
+
function hfId(url, marker) {
|
| 1030 |
+
return url.split(marker)[1].split(/[?#]/)[0].replace(/\/$/, "");
|
| 1031 |
+
}
|
| 1032 |
+
|
| 1033 |
+
function classifyResource(url) {
|
| 1034 |
+
if (IMG_URL.test(url)) {
|
| 1035 |
+
return null;
|
| 1036 |
+
}
|
| 1037 |
+
let m;
|
| 1038 |
+
if (url.startsWith("trackio-local-dashboard://")) {
|
| 1039 |
+
return {
|
| 1040 |
+
kind: "dashboard",
|
| 1041 |
+
id: url.slice("trackio-local-dashboard://".length),
|
| 1042 |
+
url,
|
| 1043 |
+
local: true,
|
| 1044 |
+
};
|
| 1045 |
+
}
|
| 1046 |
+
if (url.startsWith("trackio-artifact://")) {
|
| 1047 |
+
return {
|
| 1048 |
+
kind: "artifact",
|
| 1049 |
+
id: url.slice("trackio-artifact://".length),
|
| 1050 |
+
url,
|
| 1051 |
+
local: true,
|
| 1052 |
+
};
|
| 1053 |
+
}
|
| 1054 |
+
if (url.startsWith("trackio-local-path://")) {
|
| 1055 |
+
return {
|
| 1056 |
+
kind: "artifact",
|
| 1057 |
+
id: url.slice("trackio-local-path://".length),
|
| 1058 |
+
url,
|
| 1059 |
+
local: true,
|
| 1060 |
+
};
|
| 1061 |
+
}
|
| 1062 |
+
if ((m = url.match(/huggingface\.co\/buckets\/[^#\s]+#(.+)/))) {
|
| 1063 |
+
return { kind: "artifact", id: decodeURIComponent(m[1]), url };
|
| 1064 |
+
}
|
| 1065 |
+
if (/huggingface\.co\/datasets\/[^/]+\/[^/]+/.test(url)) {
|
| 1066 |
+
return { kind: "dataset", id: hfId(url, "/datasets/"), url };
|
| 1067 |
+
}
|
| 1068 |
+
if (/huggingface\.co\/spaces\/[^/]+\/[^/]+/.test(url)) {
|
| 1069 |
+
return { kind: "space", id: hfId(url, "/spaces/"), url };
|
| 1070 |
+
}
|
| 1071 |
+
if (/huggingface\.co\/jobs\//.test(url)) {
|
| 1072 |
+
const parts = hfId(url, "/jobs/").split("/");
|
| 1073 |
+
const jid = parts[1] || "";
|
| 1074 |
+
return {
|
| 1075 |
+
kind: "job",
|
| 1076 |
+
id: parts[0] + (jid ? ` · ${jid.slice(0, 12)}${jid.length > 12 ? "…" : ""}` : ""),
|
| 1077 |
+
url,
|
| 1078 |
+
};
|
| 1079 |
+
}
|
| 1080 |
+
if (/huggingface\.co\/buckets\//.test(url)) {
|
| 1081 |
+
return { kind: "bucket", id: hfId(url, "/buckets/"), url };
|
| 1082 |
+
}
|
| 1083 |
+
if (/huggingface\.co\/papers\//.test(url)) {
|
| 1084 |
+
return { kind: "paper", id: `Paper ${hfId(url, "/papers/")}`, url };
|
| 1085 |
+
}
|
| 1086 |
+
if ((m = url.match(/arxiv\.org\/(?:abs|pdf)\/([^?#\s]+)/))) {
|
| 1087 |
+
return { kind: "paper", id: `arXiv:${m[1].replace(/\.pdf$/, "")}`, url };
|
| 1088 |
+
}
|
| 1089 |
+
if ((m = url.match(/github\.com\/([^/?#]+\/[^/?#]+)/))) {
|
| 1090 |
+
return { kind: "repo", id: m[1], url };
|
| 1091 |
+
}
|
| 1092 |
+
if ((m = url.match(/huggingface\.co\/([^?#]+)/))) {
|
| 1093 |
+
const rest = m[1].replace(/\/$/, "");
|
| 1094 |
+
if (/^[^/]+\/[^/]+$/.test(rest) && !HF_NON_MODEL_PREFIX.test(rest)) {
|
| 1095 |
+
return { kind: "model", id: rest, url };
|
| 1096 |
+
}
|
| 1097 |
+
}
|
| 1098 |
+
return null;
|
| 1099 |
+
}
|
| 1100 |
+
|
| 1101 |
+
async function fillRailMeta(item, el) {
|
| 1102 |
+
if (item.local) return;
|
| 1103 |
+
const meta = el.querySelector(".rail-meta");
|
| 1104 |
+
const set = (parts) => {
|
| 1105 |
+
const text = parts.filter(Boolean).join(" · ");
|
| 1106 |
+
if (text) meta.textContent = text;
|
| 1107 |
+
};
|
| 1108 |
+
if (item.kind === "model") {
|
| 1109 |
+
const d = await getJSON(`https://huggingface.co/api/models/${item.id}`);
|
| 1110 |
+
if (d) set([d.pipeline_tag, `↓ ${fmt(d.downloads)}`, `♥ ${fmt(d.likes)}`]);
|
| 1111 |
+
} else if (item.kind === "dataset") {
|
| 1112 |
+
const d = await getJSON(`https://huggingface.co/api/datasets/${item.id}`);
|
| 1113 |
+
if (d) set([`↓ ${fmt(d.downloads)}`, `♥ ${fmt(d.likes)}`]);
|
| 1114 |
+
} else if (item.kind === "space" || item.kind === "dashboard") {
|
| 1115 |
+
const d = await getJSON(`https://huggingface.co/api/spaces/${item.id}`);
|
| 1116 |
+
if (d) set([d.sdk, `♥ ${fmt(d.likes)}`]);
|
| 1117 |
+
} else if (item.kind === "repo") {
|
| 1118 |
+
const d = await getJSON(`https://api.github.com/repos/${item.id}`);
|
| 1119 |
+
if (d) set([`★ ${fmt(d.stargazers_count)}`, d.language]);
|
| 1120 |
+
} else if (item.kind === "paper") {
|
| 1121 |
+
const m = item.id.match(/^(?:arXiv:|Paper )(.+)$/);
|
| 1122 |
+
if (!m) return;
|
| 1123 |
+
const arxivId = m[1].replace(/v\d+$/, "");
|
| 1124 |
+
const d = await getJSON(`https://huggingface.co/api/papers/${arxivId}`);
|
| 1125 |
+
if (d && d.id) {
|
| 1126 |
+
if (el.href) el.href = `https://huggingface.co/papers/${d.id}`;
|
| 1127 |
+
const title =
|
| 1128 |
+
d.title && d.title.length > 70 ? `${d.title.slice(0, 69)}…` : d.title;
|
| 1129 |
+
set([title, d.upvotes ? `▲ ${fmt(d.upvotes)}` : null]);
|
| 1130 |
+
}
|
| 1131 |
+
}
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
+
const BARE_ID_SKIP_DIRS = new Set([
|
| 1135 |
+
"scripts",
|
| 1136 |
+
"configs",
|
| 1137 |
+
"config",
|
| 1138 |
+
"results",
|
| 1139 |
+
"figures",
|
| 1140 |
+
"data",
|
| 1141 |
+
"datasets",
|
| 1142 |
+
"src",
|
| 1143 |
+
"tests",
|
| 1144 |
+
"test",
|
| 1145 |
+
"examples",
|
| 1146 |
+
"pages",
|
| 1147 |
+
"assets",
|
| 1148 |
+
"docs",
|
| 1149 |
+
"outputs",
|
| 1150 |
+
"output",
|
| 1151 |
+
"checkpoints",
|
| 1152 |
+
"models",
|
| 1153 |
+
"utils",
|
| 1154 |
+
"lib",
|
| 1155 |
+
"bin",
|
| 1156 |
+
"tmp",
|
| 1157 |
+
"node_modules",
|
| 1158 |
+
"dist",
|
| 1159 |
+
"build",
|
| 1160 |
+
]);
|
| 1161 |
+
const FILE_EXT_RE =
|
| 1162 |
+
/\.(py|pyc|js|ts|jsx|tsx|json|jsonl|yaml|yml|csv|tsv|md|txt|sh|bash|html|css|png|jpe?g|svg|gif|webp|ipynb|toml|cfg|ini|lock|pdf|whl|gz|zip|tar|pt|pth|bin|safetensors|db|sqlite)$/i;
|
| 1163 |
+
|
| 1164 |
+
async function detectBareModelIds(text, groups) {
|
| 1165 |
+
const stripped = text.replace(DETECTED_URL, " ");
|
| 1166 |
+
DETECTED_URL.lastIndex = 0;
|
| 1167 |
+
const seen = new Set();
|
| 1168 |
+
const candidates = [];
|
| 1169 |
+
const re = /(^|[\s"'`(=[])([A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*)/g;
|
| 1170 |
+
let m;
|
| 1171 |
+
while ((m = re.exec(stripped)) && candidates.length < 15) {
|
| 1172 |
+
const id = m[2].replace(/[.:,]+$/, "");
|
| 1173 |
+
if (seen.has(id)) continue;
|
| 1174 |
+
seen.add(id);
|
| 1175 |
+
if (FILE_EXT_RE.test(id)) continue;
|
| 1176 |
+
if (BARE_ID_SKIP_DIRS.has(id.split("/")[0].toLowerCase())) continue;
|
| 1177 |
+
candidates.push(id);
|
| 1178 |
+
}
|
| 1179 |
+
const results = await Promise.all(
|
| 1180 |
+
candidates.map((id) => getJSON(`https://huggingface.co/api/models/${id}`))
|
| 1181 |
+
);
|
| 1182 |
+
let added = false;
|
| 1183 |
+
const confirmed = [];
|
| 1184 |
+
results.forEach((d, i) => {
|
| 1185 |
+
if (!d || !d.id) return;
|
| 1186 |
+
const id = candidates[i];
|
| 1187 |
+
confirmed.push(id);
|
| 1188 |
+
const url = `https://huggingface.co/${id}`;
|
| 1189 |
+
if (!groups.has("model")) groups.set("model", new Map());
|
| 1190 |
+
if (!groups.get("model").has(url)) {
|
| 1191 |
+
groups.get("model").set(url, { kind: "model", id, url });
|
| 1192 |
+
added = true;
|
| 1193 |
+
}
|
| 1194 |
+
});
|
| 1195 |
+
return { added, confirmed };
|
| 1196 |
+
}
|
| 1197 |
+
|
| 1198 |
+
function chipifyBareIds(ids, container) {
|
| 1199 |
+
if (!ids.length) return;
|
| 1200 |
+
const escaped = ids.map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
| 1201 |
+
const pattern = new RegExp("(" + escaped.join("|") + ")");
|
| 1202 |
+
const splitter = new RegExp(pattern.source, "g");
|
| 1203 |
+
container
|
| 1204 |
+
.querySelectorAll(".cell.markdown .cell-body")
|
| 1205 |
+
.forEach((body) => {
|
| 1206 |
+
const walker = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, {
|
| 1207 |
+
acceptNode(node) {
|
| 1208 |
+
if (!pattern.test(node.nodeValue)) return NodeFilter.FILTER_REJECT;
|
| 1209 |
+
for (
|
| 1210 |
+
let el = node.parentElement;
|
| 1211 |
+
el && el !== body;
|
| 1212 |
+
el = el.parentElement
|
| 1213 |
+
) {
|
| 1214 |
+
if (["A", "CODE", "PRE", "BUTTON"].indexOf(el.tagName) !== -1) {
|
| 1215 |
+
return NodeFilter.FILTER_REJECT;
|
| 1216 |
+
}
|
| 1217 |
+
}
|
| 1218 |
+
return NodeFilter.FILTER_ACCEPT;
|
| 1219 |
+
},
|
| 1220 |
+
});
|
| 1221 |
+
const nodes = [];
|
| 1222 |
+
while (walker.nextNode()) nodes.push(walker.currentNode);
|
| 1223 |
+
nodes.forEach((node) => {
|
| 1224 |
+
const frag = document.createDocumentFragment();
|
| 1225 |
+
node.nodeValue.split(splitter).forEach((part) => {
|
| 1226 |
+
if (ids.indexOf(part) !== -1) {
|
| 1227 |
+
const holder = document.createElement("span");
|
| 1228 |
+
holder.innerHTML = resChipHtml({
|
| 1229 |
+
kind: "model",
|
| 1230 |
+
id: part,
|
| 1231 |
+
url: `https://huggingface.co/${part}`,
|
| 1232 |
+
});
|
| 1233 |
+
frag.appendChild(holder.firstChild);
|
| 1234 |
+
} else if (part) {
|
| 1235 |
+
frag.appendChild(document.createTextNode(part));
|
| 1236 |
+
}
|
| 1237 |
+
});
|
| 1238 |
+
node.parentNode.replaceChild(frag, node);
|
| 1239 |
+
});
|
| 1240 |
+
});
|
| 1241 |
+
}
|
| 1242 |
+
|
| 1243 |
+
let RAIL_TOKEN = 0;
|
| 1244 |
+
const RAIL_EXCLUDE_KINDS = new Set(["paper", "repo", "artifact", "dashboard"]);
|
| 1245 |
+
|
| 1246 |
+
function railDashboardItem(it) {
|
| 1247 |
+
return {
|
| 1248 |
+
kind: "dashboard",
|
| 1249 |
+
id: it.id,
|
| 1250 |
+
url: it.local ? it.resUrl : it.url || it.resUrl,
|
| 1251 |
+
local: it.local,
|
| 1252 |
+
railLabel: "Dashboard",
|
| 1253 |
+
};
|
| 1254 |
+
}
|
| 1255 |
+
|
| 1256 |
+
function promoteTrackioSpacesInRail(groups, dashResUrls, body, rail, token) {
|
| 1257 |
+
const spaceGroup = groups.get("space");
|
| 1258 |
+
if (!spaceGroup || !spaceGroup.size) return;
|
| 1259 |
+
spaceGroup.forEach((item, url) => {
|
| 1260 |
+
getJSON(`https://huggingface.co/api/spaces/${item.id}`)
|
| 1261 |
+
.then((d) => {
|
| 1262 |
+
if (rail.dataset.renderToken !== token) return;
|
| 1263 |
+
const tags = (d && d.tags) || [];
|
| 1264 |
+
if (!tags.some((t) => String(t).toLowerCase() === "trackio")) return;
|
| 1265 |
+
if (dashResUrls.has(url)) return;
|
| 1266 |
+
spaceGroup.delete(url);
|
| 1267 |
+
if (!spaceGroup.size) groups.delete("space");
|
| 1268 |
+
if (!groups.has("dashboard")) groups.set("dashboard", new Map());
|
| 1269 |
+
groups.get("dashboard").set(url, {
|
| 1270 |
+
kind: "dashboard",
|
| 1271 |
+
id: item.id,
|
| 1272 |
+
url: item.url,
|
| 1273 |
+
local: false,
|
| 1274 |
+
railLabel: "Dashboard",
|
| 1275 |
+
});
|
| 1276 |
+
dashResUrls.add(url);
|
| 1277 |
+
paintRail(groups, body, rail);
|
| 1278 |
+
})
|
| 1279 |
+
.catch(() => {});
|
| 1280 |
+
});
|
| 1281 |
+
}
|
| 1282 |
+
|
| 1283 |
+
function renderRail(md, body, rail) {
|
| 1284 |
+
const token = String(++RAIL_TOKEN);
|
| 1285 |
+
rail.dataset.renderToken = token;
|
| 1286 |
+
const scanText = md.replace(
|
| 1287 |
+
/(`{3,4}|~{3,4})(html|raw)[^\n]*\n[\s\S]*?\n\1/g,
|
| 1288 |
+
" "
|
| 1289 |
+
);
|
| 1290 |
+
const groups = new Map();
|
| 1291 |
+
const dashMap = new Map();
|
| 1292 |
+
const dashResUrls = new Set();
|
| 1293 |
+
cellDashboardItems(md).forEach((it) => {
|
| 1294 |
+
if (dashMap.has(it.resUrl)) return;
|
| 1295 |
+
dashMap.set(it.resUrl, railDashboardItem(it));
|
| 1296 |
+
dashResUrls.add(it.resUrl);
|
| 1297 |
+
});
|
| 1298 |
+
if (dashMap.size) groups.set("dashboard", dashMap);
|
| 1299 |
+
extractUrls(scanText).forEach((url) => {
|
| 1300 |
+
const item = classifyResource(url);
|
| 1301 |
+
if (!item) return;
|
| 1302 |
+
if (RAIL_EXCLUDE_KINDS.has(item.kind)) return;
|
| 1303 |
+
if (dashResUrls.has(url)) return;
|
| 1304 |
+
if (!groups.has(item.kind)) groups.set(item.kind, new Map());
|
| 1305 |
+
groups.get(item.kind).set(item.url, item);
|
| 1306 |
+
});
|
| 1307 |
+
const artMap = new Map();
|
| 1308 |
+
cellArtifactItems(md).forEach((it) => {
|
| 1309 |
+
if (artMap.has(it.resUrl)) return;
|
| 1310 |
+
const label = it.type
|
| 1311 |
+
? it.type.charAt(0).toUpperCase() + it.type.slice(1)
|
| 1312 |
+
: "Artifact";
|
| 1313 |
+
artMap.set(it.resUrl, {
|
| 1314 |
+
kind: "artifact",
|
| 1315 |
+
id: it.name,
|
| 1316 |
+
url: it.local ? it.resUrl : it.url || it.resUrl,
|
| 1317 |
+
local: it.local,
|
| 1318 |
+
railLabel: label,
|
| 1319 |
+
size: it.size,
|
| 1320 |
+
});
|
| 1321 |
+
});
|
| 1322 |
+
if (artMap.size) groups.set("artifact", artMap);
|
| 1323 |
+
paintRail(groups, body, rail);
|
| 1324 |
+
promoteTrackioSpacesInRail(groups, dashResUrls, body, rail, token);
|
| 1325 |
+
detectBareModelIds(scanText, groups)
|
| 1326 |
+
.then((result) => {
|
| 1327 |
+
if (rail.dataset.renderToken !== token) return;
|
| 1328 |
+
chipifyBareIds(result.confirmed, body);
|
| 1329 |
+
if (result.added) paintRail(groups, body, rail);
|
| 1330 |
+
})
|
| 1331 |
+
.catch(() => {});
|
| 1332 |
+
}
|
| 1333 |
+
|
| 1334 |
+
function paintRail(groups, body, rail) {
|
| 1335 |
+
rail.innerHTML = "";
|
| 1336 |
+
RESOURCE_SECTIONS.forEach(([kind, label, icon]) => {
|
| 1337 |
+
const group = groups.get(kind);
|
| 1338 |
+
if (!group || !group.size) return;
|
| 1339 |
+
group.forEach((item) => {
|
| 1340 |
+
const el = document.createElement(item.local ? "div" : "a");
|
| 1341 |
+
el.className = item.local ? "rail-item rail-local" : "rail-item";
|
| 1342 |
+
if (!item.local) {
|
| 1343 |
+
el.href = item.url;
|
| 1344 |
+
el.target = "_blank";
|
| 1345 |
+
el.rel = "noopener";
|
| 1346 |
+
}
|
| 1347 |
+
el.dataset.resUrl = item.url;
|
| 1348 |
+
let desc;
|
| 1349 |
+
if (kind === "artifact") {
|
| 1350 |
+
const state = item.local ? "publish to share" : "Open ↗";
|
| 1351 |
+
desc = item.size ? `${item.size} · ${state}` : state;
|
| 1352 |
+
} else if (kind === "dashboard") {
|
| 1353 |
+
desc = item.local ? "publish to share" : "Open ↗";
|
| 1354 |
+
} else {
|
| 1355 |
+
desc = item.local ? "publish to share" : RESOURCE_DESC[kind];
|
| 1356 |
+
}
|
| 1357 |
+
const kindLabel = item.railLabel || label.replace(/s$/, "");
|
| 1358 |
+
const iconHtml =
|
| 1359 |
+
kind === "artifact"
|
| 1360 |
+
? ARTIFACT_ICON_IMG
|
| 1361 |
+
: kind === "dashboard"
|
| 1362 |
+
? DASHBOARD_ICON_IMG
|
| 1363 |
+
: `<span>${icon}</span>`;
|
| 1364 |
+
el.innerHTML =
|
| 1365 |
+
`<div class="rail-kind">${iconHtml}${esc(kindLabel)}</div>` +
|
| 1366 |
+
`<div class="rail-title">${esc(item.id)}</div>` +
|
| 1367 |
+
`<div class="rail-meta">${esc(desc)}</div>`;
|
| 1368 |
+
rail.appendChild(el);
|
| 1369 |
+
fillRailMeta(item, el)
|
| 1370 |
+
.catch(() => {})
|
| 1371 |
+
.finally(() => scheduleRailPosition(body, rail));
|
| 1372 |
+
});
|
| 1373 |
+
});
|
| 1374 |
+
rail.hidden = !rail.childElementCount;
|
| 1375 |
+
scheduleRailPosition(body, rail);
|
| 1376 |
+
}
|
| 1377 |
+
|
| 1378 |
+
function resourceAnchor(body, url) {
|
| 1379 |
+
return body.querySelector(`[data-res-url="${CSS.escape(url)}"]`);
|
| 1380 |
+
}
|
| 1381 |
+
|
| 1382 |
+
function positionRail(body, rail) {
|
| 1383 |
+
if (rail.hidden || !rail.isConnected) return;
|
| 1384 |
+
const bodyRect = body.getBoundingClientRect();
|
| 1385 |
+
const items = Array.from(rail.querySelectorAll(".rail-item")).map((el, index) => {
|
| 1386 |
+
const anchor = resourceAnchor(body, el.dataset.resUrl);
|
| 1387 |
+
return {
|
| 1388 |
+
el,
|
| 1389 |
+
index,
|
| 1390 |
+
desired: anchor
|
| 1391 |
+
? Math.max(0, anchor.getBoundingClientRect().top - bodyRect.top)
|
| 1392 |
+
: 0,
|
| 1393 |
+
};
|
| 1394 |
+
});
|
| 1395 |
+
items.sort((a, b) => a.desired - b.desired || a.index - b.index);
|
| 1396 |
+
let cursor = 0;
|
| 1397 |
+
items.forEach(({ el, desired }) => {
|
| 1398 |
+
const top = Math.max(desired, cursor);
|
| 1399 |
+
el.style.top = `${top}px`;
|
| 1400 |
+
cursor = top + el.offsetHeight + 10;
|
| 1401 |
+
});
|
| 1402 |
+
rail.style.minHeight = `${Math.max(body.offsetHeight, cursor)}px`;
|
| 1403 |
+
}
|
| 1404 |
+
|
| 1405 |
+
function scheduleRailPosition(body, rail) {
|
| 1406 |
+
cancelAnimationFrame(Number(rail.dataset.positionFrame || 0));
|
| 1407 |
+
rail.dataset.positionFrame = String(
|
| 1408 |
+
requestAnimationFrame(() => positionRail(body, rail))
|
| 1409 |
+
);
|
| 1410 |
+
}
|
| 1411 |
+
|
| 1412 |
+
function dashboardSubdomainFromUrl(url) {
|
| 1413 |
+
return spaceIdFromUrl(url).toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
| 1414 |
+
}
|
| 1415 |
+
|
| 1416 |
+
function dashboardOpenLink(head, url) {
|
| 1417 |
+
if (!head || !url) return;
|
| 1418 |
+
const meta = head.querySelector(".cell-meta");
|
| 1419 |
+
if (!meta) return;
|
| 1420 |
+
let link = meta.querySelector(".cell-open");
|
| 1421 |
+
if (!link) {
|
| 1422 |
+
link = document.createElement("a");
|
| 1423 |
+
link.className = "cell-open";
|
| 1424 |
+
link.target = "_blank";
|
| 1425 |
+
link.rel = "noopener";
|
| 1426 |
+
meta.insertBefore(link, meta.firstChild);
|
| 1427 |
+
}
|
| 1428 |
+
link.href = url;
|
| 1429 |
+
link.textContent = "Open ↗";
|
| 1430 |
+
}
|
| 1431 |
+
|
| 1432 |
+
function dashboardFrame(src) {
|
| 1433 |
+
const iframe = document.createElement("iframe");
|
| 1434 |
+
iframe.className = "dashboard-frame";
|
| 1435 |
+
iframe.src = src;
|
| 1436 |
+
iframe.loading = "lazy";
|
| 1437 |
+
iframe.allow = "clipboard-read; clipboard-write; fullscreen";
|
| 1438 |
+
return iframe;
|
| 1439 |
+
}
|
| 1440 |
+
|
| 1441 |
+
function renderDashboardCell(meta, body, container, head) {
|
| 1442 |
+
const project = meta.dashboard_project || "";
|
| 1443 |
+
const holder = document.createElement("div");
|
| 1444 |
+
holder.className = "dashboard-shell";
|
| 1445 |
+
container.appendChild(holder);
|
| 1446 |
+
const space = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
|
| 1447 |
+
if (space) {
|
| 1448 |
+
const url = space[0];
|
| 1449 |
+
dashboardOpenLink(head, url);
|
| 1450 |
+
holder.appendChild(
|
| 1451 |
+
dashboardFrame(
|
| 1452 |
+
`https://${dashboardSubdomainFromUrl(url)}.hf.space/?sidebar=hidden&hide_empty_tabs=true`
|
| 1453 |
+
)
|
| 1454 |
+
);
|
| 1455 |
+
return;
|
| 1456 |
+
}
|
| 1457 |
+
if (!isLocalPreview()) {
|
| 1458 |
+
holder.className = "artifact-chip";
|
| 1459 |
+
holder.dataset.resUrl = `trackio-local-dashboard://${project}`;
|
| 1460 |
+
holder.innerHTML =
|
| 1461 |
+
"🎯 <strong>Local Trackio dashboard</strong> — publish the logbook to share it";
|
| 1462 |
+
return;
|
| 1463 |
+
}
|
| 1464 |
+
const open = "/dashboard/?project=" + encodeURIComponent(project);
|
| 1465 |
+
dashboardOpenLink(head, open);
|
| 1466 |
+
holder.appendChild(
|
| 1467 |
+
dashboardFrame(open + "&sidebar=hidden&hide_empty_tabs=true"),
|
| 1468 |
+
);
|
| 1469 |
+
}
|
| 1470 |
+
|
| 1471 |
+
const CACHE_PREFIX = "trackio-logbook:";
|
| 1472 |
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
| 1473 |
+
const CACHE_MISS_TTL_MS = 60 * 60 * 1000;
|
| 1474 |
+
|
| 1475 |
+
function cacheGet(url) {
|
| 1476 |
+
try {
|
| 1477 |
+
const raw = localStorage.getItem(CACHE_PREFIX + url);
|
| 1478 |
+
if (!raw) return undefined;
|
| 1479 |
+
const entry = JSON.parse(raw);
|
| 1480 |
+
const ttl = entry.d === null ? CACHE_MISS_TTL_MS : CACHE_TTL_MS;
|
| 1481 |
+
if (Date.now() - entry.t > ttl) {
|
| 1482 |
+
localStorage.removeItem(CACHE_PREFIX + url);
|
| 1483 |
+
return undefined;
|
| 1484 |
+
}
|
| 1485 |
+
return entry.d;
|
| 1486 |
+
} catch (e) {
|
| 1487 |
+
return undefined;
|
| 1488 |
+
}
|
| 1489 |
+
}
|
| 1490 |
+
|
| 1491 |
+
function cacheSet(url, data) {
|
| 1492 |
+
try {
|
| 1493 |
+
localStorage.setItem(
|
| 1494 |
+
CACHE_PREFIX + url,
|
| 1495 |
+
JSON.stringify({ t: Date.now(), d: data })
|
| 1496 |
+
);
|
| 1497 |
+
} catch (e) {}
|
| 1498 |
+
}
|
| 1499 |
+
|
| 1500 |
+
async function getJSON(url) {
|
| 1501 |
+
if (UNFURL_CACHE[url] !== undefined) return UNFURL_CACHE[url];
|
| 1502 |
+
const cached = cacheGet(url);
|
| 1503 |
+
if (cached !== undefined) {
|
| 1504 |
+
UNFURL_CACHE[url] = cached;
|
| 1505 |
+
return cached;
|
| 1506 |
+
}
|
| 1507 |
+
try {
|
| 1508 |
+
const r = await fetch(url);
|
| 1509 |
+
if (!r.ok) throw new Error(r.status);
|
| 1510 |
+
const j = await r.json();
|
| 1511 |
+
UNFURL_CACHE[url] = j;
|
| 1512 |
+
cacheSet(url, j);
|
| 1513 |
+
return j;
|
| 1514 |
+
} catch (e) {
|
| 1515 |
+
UNFURL_CACHE[url] = null;
|
| 1516 |
+
cacheSet(url, null);
|
| 1517 |
+
return null;
|
| 1518 |
+
}
|
| 1519 |
+
}
|
| 1520 |
+
|
| 1521 |
+
/* -------------------- routing / render -------------------- */
|
| 1522 |
+
|
| 1523 |
+
function buildTree() {
|
| 1524 |
+
const tree = document.getElementById("tree");
|
| 1525 |
+
tree.innerHTML = "";
|
| 1526 |
+
const nodes = [];
|
| 1527 |
+
(MANIFEST.root.children || []).forEach((c) => flattenTree(c, 0, nodes));
|
| 1528 |
+
nodes.forEach(({ node, depth }) => {
|
| 1529 |
+
const a = document.createElement("a");
|
| 1530 |
+
a.href = "#/" + node.slug;
|
| 1531 |
+
a.className = "depth-" + depth;
|
| 1532 |
+
a.dataset.slug = node.slug;
|
| 1533 |
+
const mark = document.createElement("span");
|
| 1534 |
+
mark.className = "tree-mark";
|
| 1535 |
+
mark.textContent = "§";
|
| 1536 |
+
a.appendChild(mark);
|
| 1537 |
+
a.appendChild(document.createTextNode(" " + node.title));
|
| 1538 |
+
tree.appendChild(a);
|
| 1539 |
+
});
|
| 1540 |
+
}
|
| 1541 |
+
|
| 1542 |
+
function highlight(slug) {
|
| 1543 |
+
document
|
| 1544 |
+
.querySelectorAll("#tree a")
|
| 1545 |
+
.forEach((a) => a.classList.toggle("active", a.dataset.slug === slug));
|
| 1546 |
+
document
|
| 1547 |
+
.getElementById("book-head")
|
| 1548 |
+
.classList.toggle("active", slug === MANIFEST.root.slug);
|
| 1549 |
+
}
|
| 1550 |
+
|
| 1551 |
+
function clearPageCache() {
|
| 1552 |
+
Object.keys(PAGE_CACHE).forEach((key) => {
|
| 1553 |
+
delete PAGE_CACHE[key];
|
| 1554 |
+
});
|
| 1555 |
+
}
|
| 1556 |
+
|
| 1557 |
+
function isLocalPreview() {
|
| 1558 |
+
return ["localhost", "127.0.0.1", "::1"].includes(location.hostname);
|
| 1559 |
+
}
|
| 1560 |
+
|
| 1561 |
+
async function fetchManifest() {
|
| 1562 |
+
const suffix = isLocalPreview() ? `?t=${Date.now()}` : "";
|
| 1563 |
+
return await (await fetch("./logbook.json" + suffix, { cache: "no-store" })).json();
|
| 1564 |
+
}
|
| 1565 |
+
|
| 1566 |
+
async function fetchPage(node) {
|
| 1567 |
+
if (PAGE_CACHE[node.file]) return PAGE_CACHE[node.file];
|
| 1568 |
+
try {
|
| 1569 |
+
const suffix = isLocalPreview()
|
| 1570 |
+
? `?rev=${encodeURIComponent(MANIFEST.revision || "")}`
|
| 1571 |
+
: "";
|
| 1572 |
+
const r = await fetch("./" + node.file + suffix, { cache: "no-store" });
|
| 1573 |
+
PAGE_CACHE[node.file] = await r.text();
|
| 1574 |
+
} catch (e) {
|
| 1575 |
+
PAGE_CACHE[node.file] = "# " + node.title + "\n\n_Could not load section._";
|
| 1576 |
+
}
|
| 1577 |
+
return PAGE_CACHE[node.file];
|
| 1578 |
+
}
|
| 1579 |
+
|
| 1580 |
+
function allNodes() {
|
| 1581 |
+
const nodes = [];
|
| 1582 |
+
flattenTree(MANIFEST.root, 0, nodes);
|
| 1583 |
+
return nodes.map(({ node }) => node);
|
| 1584 |
+
}
|
| 1585 |
+
|
| 1586 |
+
function collectPinnedCells(markdown, nodes) {
|
| 1587 |
+
const cells = [];
|
| 1588 |
+
markdown.forEach((text, index) => {
|
| 1589 |
+
const cellRe = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
|
| 1590 |
+
let match;
|
| 1591 |
+
let cellIndex = 0;
|
| 1592 |
+
while ((match = cellRe.exec(text))) {
|
| 1593 |
+
const meta = parseCellMeta(match[2]);
|
| 1594 |
+
if (isPinned(meta)) {
|
| 1595 |
+
cells.push({
|
| 1596 |
+
meta,
|
| 1597 |
+
body: match[3],
|
| 1598 |
+
node: nodes[index],
|
| 1599 |
+
index: cells.length,
|
| 1600 |
+
order: meta.pinned_at || meta.created_at || "",
|
| 1601 |
+
cellIndex,
|
| 1602 |
+
});
|
| 1603 |
+
}
|
| 1604 |
+
cellIndex++;
|
| 1605 |
+
}
|
| 1606 |
+
});
|
| 1607 |
+
return cells.sort(
|
| 1608 |
+
(a, b) =>
|
| 1609 |
+
a.order.localeCompare(b.order) ||
|
| 1610 |
+
a.index - b.index ||
|
| 1611 |
+
a.cellIndex - b.cellIndex
|
| 1612 |
+
);
|
| 1613 |
+
}
|
| 1614 |
+
|
| 1615 |
+
function renderPinnedNotes(cells, container) {
|
| 1616 |
+
if (!cells.length) return;
|
| 1617 |
+
const deck = document.createElement("section");
|
| 1618 |
+
deck.className = "pinned-notes";
|
| 1619 |
+
const list = document.createElement("div");
|
| 1620 |
+
list.className = "pinned-notes-list";
|
| 1621 |
+
cells.forEach(({ meta, body }) => {
|
| 1622 |
+
const cell = renderCell(meta, body, list);
|
| 1623 |
+
cell.classList.add("pinned-copy");
|
| 1624 |
+
});
|
| 1625 |
+
deck.appendChild(list);
|
| 1626 |
+
const anchor =
|
| 1627 |
+
container.querySelector(".logbook-stats") ||
|
| 1628 |
+
container.querySelector(".agent-hint");
|
| 1629 |
+
container.insertBefore(deck, anchor ? anchor.nextSibling : container.firstChild);
|
| 1630 |
+
container.closest(".book-intro").classList.add("has-pinned-notes");
|
| 1631 |
+
}
|
| 1632 |
+
|
| 1633 |
+
function removeIndexProse(body) {
|
| 1634 |
+
const h1 = Array.from(body.children).find((el) => el.tagName === "H1");
|
| 1635 |
+
if (!h1) return;
|
| 1636 |
+
let current = h1.nextElementSibling;
|
| 1637 |
+
while (current && current.tagName !== "H2") {
|
| 1638 |
+
const next = current.nextElementSibling;
|
| 1639 |
+
current.remove();
|
| 1640 |
+
current = next;
|
| 1641 |
+
}
|
| 1642 |
+
}
|
| 1643 |
+
|
| 1644 |
+
function removePageDirectory(body) {
|
| 1645 |
+
const heading = Array.from(body.children).find(
|
| 1646 |
+
(el) => el.tagName === "H2" && el.textContent.trim().toLowerCase() === "pages"
|
| 1647 |
+
);
|
| 1648 |
+
if (!heading) return;
|
| 1649 |
+
let current = heading;
|
| 1650 |
+
while (current) {
|
| 1651 |
+
const next = current.nextElementSibling;
|
| 1652 |
+
current.remove();
|
| 1653 |
+
if (next && ["H1", "H2"].includes(next.tagName)) break;
|
| 1654 |
+
current = next;
|
| 1655 |
+
}
|
| 1656 |
+
}
|
| 1657 |
+
|
| 1658 |
+
const RAIL_OBSERVERS = [];
|
| 1659 |
+
|
| 1660 |
+
async function renderLogbook(opts = {}) {
|
| 1661 |
+
const scrollY = window.scrollY;
|
| 1662 |
+
const page = document.getElementById("page");
|
| 1663 |
+
RAIL_OBSERVERS.splice(0).forEach((observer) => observer.disconnect());
|
| 1664 |
+
page.innerHTML = "";
|
| 1665 |
+
const nodes = allNodes();
|
| 1666 |
+
const markdown = await Promise.all(nodes.map(fetchPage));
|
| 1667 |
+
const pinnedCells = collectPinnedCells(markdown, nodes);
|
| 1668 |
+
let bookIntroBody = null;
|
| 1669 |
+
nodes.forEach((node, index) => {
|
| 1670 |
+
const section = document.createElement("section");
|
| 1671 |
+
section.className = "page-section";
|
| 1672 |
+
section.id = "/" + node.slug;
|
| 1673 |
+
section.dataset.slug = node.slug;
|
| 1674 |
+
|
| 1675 |
+
const layout = document.createElement("div");
|
| 1676 |
+
layout.className = "page-layout";
|
| 1677 |
+
const body = document.createElement("div");
|
| 1678 |
+
body.className = "page-body";
|
| 1679 |
+
const rail = document.createElement("aside");
|
| 1680 |
+
rail.className = "context-rail";
|
| 1681 |
+
rail.setAttribute("aria-label", `Resources for ${node.title}`);
|
| 1682 |
+
|
| 1683 |
+
renderMarkdown(markdown[index], body);
|
| 1684 |
+
if (node.slug === MANIFEST.root.slug) {
|
| 1685 |
+
section.classList.add("book-intro");
|
| 1686 |
+
removeIndexProse(body);
|
| 1687 |
+
removePageDirectory(body);
|
| 1688 |
+
const hint = buildAgentHint();
|
| 1689 |
+
const h1 = body.querySelector("h1");
|
| 1690 |
+
if (h1 && h1.parentNode === body) {
|
| 1691 |
+
body.insertBefore(hint, h1.nextSibling);
|
| 1692 |
+
} else {
|
| 1693 |
+
body.prepend(hint);
|
| 1694 |
+
}
|
| 1695 |
+
hint.after(buildLogbookStats(markdown));
|
| 1696 |
+
bookIntroBody = body;
|
| 1697 |
+
}
|
| 1698 |
+
layout.appendChild(body);
|
| 1699 |
+
layout.appendChild(rail);
|
| 1700 |
+
section.appendChild(layout);
|
| 1701 |
+
page.appendChild(section);
|
| 1702 |
+
renderRail(markdown[index], body, rail);
|
| 1703 |
+
if (window.ResizeObserver) {
|
| 1704 |
+
const observer = new ResizeObserver(() => scheduleRailPosition(body, rail));
|
| 1705 |
+
observer.observe(body);
|
| 1706 |
+
observer.observe(rail);
|
| 1707 |
+
RAIL_OBSERVERS.push(observer);
|
| 1708 |
+
}
|
| 1709 |
+
});
|
| 1710 |
+
if (bookIntroBody) renderPinnedNotes(pinnedCells, bookIntroBody);
|
| 1711 |
+
if (bookIntroBody) {
|
| 1712 |
+
const section = bookIntroBody.closest(".book-intro");
|
| 1713 |
+
const hasExtra = Array.from(bookIntroBody.children).some(
|
| 1714 |
+
(el) =>
|
| 1715 |
+
el.tagName !== "H1" &&
|
| 1716 |
+
!el.classList.contains("agent-hint") &&
|
| 1717 |
+
!el.classList.contains("logbook-stats") &&
|
| 1718 |
+
!el.classList.contains("pinned-notes")
|
| 1719 |
+
);
|
| 1720 |
+
if (section && !section.classList.contains("has-pinned-notes") && !hasExtra) {
|
| 1721 |
+
section.classList.add("book-intro-tight");
|
| 1722 |
+
}
|
| 1723 |
+
}
|
| 1724 |
+
requestAnimationFrame(() => {
|
| 1725 |
+
if (opts.preserveScroll) {
|
| 1726 |
+
window.scrollTo(0, scrollY);
|
| 1727 |
+
} else {
|
| 1728 |
+
scrollToHash({ behavior: "auto" });
|
| 1729 |
+
}
|
| 1730 |
+
updateActiveSection();
|
| 1731 |
+
});
|
| 1732 |
+
}
|
| 1733 |
+
|
| 1734 |
+
function setupResourceHover() {
|
| 1735 |
+
document.addEventListener("mouseover", (e) => {
|
| 1736 |
+
const el = e.target.closest && e.target.closest("[data-res-url]");
|
| 1737 |
+
if (!el || el.classList.contains("rail-item")) return;
|
| 1738 |
+
const url = el.getAttribute("data-res-url");
|
| 1739 |
+
const section = el.closest(".page-section");
|
| 1740 |
+
const scope = section || document;
|
| 1741 |
+
scope.querySelectorAll(".context-rail [data-res-url]").forEach((n) => {
|
| 1742 |
+
n.classList.toggle("res-hl", n.getAttribute("data-res-url") === url);
|
| 1743 |
+
});
|
| 1744 |
+
});
|
| 1745 |
+
document.addEventListener("mouseout", (e) => {
|
| 1746 |
+
const el = e.target.closest && e.target.closest("[data-res-url]");
|
| 1747 |
+
if (!el || el.classList.contains("rail-item")) return;
|
| 1748 |
+
document.querySelectorAll(".context-rail .res-hl").forEach((n) => {
|
| 1749 |
+
n.classList.remove("res-hl");
|
| 1750 |
+
});
|
| 1751 |
+
});
|
| 1752 |
+
}
|
| 1753 |
+
|
| 1754 |
+
let STATS_TOKEN = 0;
|
| 1755 |
+
let STATS_LISTENERS = false;
|
| 1756 |
+
|
| 1757 |
+
function fmtBytes(n) {
|
| 1758 |
+
if (n == null || isNaN(n)) return null;
|
| 1759 |
+
if (n < 1000) return `${n} B`;
|
| 1760 |
+
const units = ["kB", "MB", "GB", "TB"];
|
| 1761 |
+
let v = n;
|
| 1762 |
+
let i = -1;
|
| 1763 |
+
do {
|
| 1764 |
+
v /= 1000;
|
| 1765 |
+
i++;
|
| 1766 |
+
} while (v >= 1000 && i < units.length - 1);
|
| 1767 |
+
return `${v.toFixed(v < 10 ? 1 : 0)} ${units[i]}`;
|
| 1768 |
+
}
|
| 1769 |
+
|
| 1770 |
+
function spaceIdFromUrl(url) {
|
| 1771 |
+
return url.split("/spaces/")[1].split(/[?#]/)[0].replace(/\/$/, "");
|
| 1772 |
+
}
|
| 1773 |
+
|
| 1774 |
+
const LB_CELL_RE = /(^|\n)---\n<!-- trackio-cell\n([\s\S]*?)\n-->\n([\s\S]*?)(?=\n---\n<!-- trackio-cell\n|\s*$)/g;
|
| 1775 |
+
|
| 1776 |
+
function cellDashboardItems(md) {
|
| 1777 |
+
const re = new RegExp(LB_CELL_RE.source, "g");
|
| 1778 |
+
const items = [];
|
| 1779 |
+
let m;
|
| 1780 |
+
while ((m = re.exec(md))) {
|
| 1781 |
+
const meta = parseCellMeta(m[2]);
|
| 1782 |
+
if (meta.type !== "dashboard") continue;
|
| 1783 |
+
const body = m[3];
|
| 1784 |
+
const project = meta.dashboard_project || "";
|
| 1785 |
+
const sp = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
|
| 1786 |
+
const local = !sp;
|
| 1787 |
+
const url = sp ? sp[0] : "";
|
| 1788 |
+
const resUrl = local ? `trackio-local-dashboard://${project}` : url;
|
| 1789 |
+
items.push({
|
| 1790 |
+
id: local ? project : spaceIdFromUrl(url),
|
| 1791 |
+
local,
|
| 1792 |
+
url,
|
| 1793 |
+
resUrl,
|
| 1794 |
+
});
|
| 1795 |
+
}
|
| 1796 |
+
return items;
|
| 1797 |
+
}
|
| 1798 |
+
|
| 1799 |
+
function artifactInfoFromCell(meta, body) {
|
| 1800 |
+
const name = meta.artifact || meta.path || "";
|
| 1801 |
+
let size = null;
|
| 1802 |
+
const sm = body.match(/·\s*([\d.]+\s*[kMGT]?B)\b/);
|
| 1803 |
+
if (sm) size = sm[1].trim();
|
| 1804 |
+
if (!size && meta.size != null) size = fmtBytes(meta.size);
|
| 1805 |
+
const bucket = body.match(/https:\/\/huggingface\.co\/buckets\/[^\s<>)"'`]+/);
|
| 1806 |
+
const artUri = body.match(/trackio-artifact:\/\/\S+/);
|
| 1807 |
+
const pathUri = body.match(/trackio-local-path:\/\/\S+/);
|
| 1808 |
+
const url = bucket ? bucket[0] : "";
|
| 1809 |
+
const local = !bucket;
|
| 1810 |
+
const resUrl =
|
| 1811 |
+
url || (artUri ? artUri[0] : pathUri ? pathUri[0] : `trackio-artifact://${name}`);
|
| 1812 |
+
return {
|
| 1813 |
+
name,
|
| 1814 |
+
type: meta.artifact_type || "",
|
| 1815 |
+
size,
|
| 1816 |
+
local,
|
| 1817 |
+
isPathRef: !!meta.path,
|
| 1818 |
+
url,
|
| 1819 |
+
resUrl,
|
| 1820 |
+
};
|
| 1821 |
+
}
|
| 1822 |
+
|
| 1823 |
+
function cellArtifactItems(md) {
|
| 1824 |
+
const re = new RegExp(LB_CELL_RE.source, "g");
|
| 1825 |
+
const items = [];
|
| 1826 |
+
let m;
|
| 1827 |
+
while ((m = re.exec(md))) {
|
| 1828 |
+
const meta = parseCellMeta(m[2]);
|
| 1829 |
+
const body = m[3];
|
| 1830 |
+
const order = meta.created_at || "";
|
| 1831 |
+
if (meta.type === "artifact") {
|
| 1832 |
+
const info = artifactInfoFromCell(meta, body);
|
| 1833 |
+
if (info.name) items.push({ ...info, order });
|
| 1834 |
+
}
|
| 1835 |
+
}
|
| 1836 |
+
return items;
|
| 1837 |
+
}
|
| 1838 |
+
|
| 1839 |
+
function collectLogbookResources(markdownList) {
|
| 1840 |
+
const re = new RegExp(LB_CELL_RE.source, "g");
|
| 1841 |
+
const dashboards = new Map();
|
| 1842 |
+
markdownList.forEach((md) => {
|
| 1843 |
+
let m;
|
| 1844 |
+
while ((m = re.exec(md))) {
|
| 1845 |
+
const meta = parseCellMeta(m[2]);
|
| 1846 |
+
const body = m[3];
|
| 1847 |
+
if (meta.type !== "dashboard") continue;
|
| 1848 |
+
const project = meta.dashboard_project || "";
|
| 1849 |
+
const space = body.match(/https:\/\/huggingface\.co\/spaces\/[^\s<>)"'`]+/);
|
| 1850 |
+
const local = !space;
|
| 1851 |
+
const url = space ? space[0] : "";
|
| 1852 |
+
const key = local ? `local:${project}` : `space:${spaceIdFromUrl(url)}`;
|
| 1853 |
+
const resUrl = local ? `trackio-local-dashboard://${project}` : url;
|
| 1854 |
+
if (!dashboards.has(key))
|
| 1855 |
+
dashboards.set(key, { project, local, url, resUrl });
|
| 1856 |
+
}
|
| 1857 |
+
});
|
| 1858 |
+
const artifacts = new Map();
|
| 1859 |
+
markdownList.forEach((md) => {
|
| 1860 |
+
cellArtifactItems(md).forEach((it) => {
|
| 1861 |
+
const key = `${it.type}:${it.name}`;
|
| 1862 |
+
const prev = artifacts.get(key);
|
| 1863 |
+
if (!prev || it.order >= prev.order) artifacts.set(key, it);
|
| 1864 |
+
});
|
| 1865 |
+
});
|
| 1866 |
+
return {
|
| 1867 |
+
dashboards: Array.from(dashboards.values()).sort((a, b) =>
|
| 1868 |
+
a.project.localeCompare(b.project)
|
| 1869 |
+
),
|
| 1870 |
+
artifacts: Array.from(artifacts.values()).sort((a, b) =>
|
| 1871 |
+
a.name.localeCompare(b.name)
|
| 1872 |
+
),
|
| 1873 |
+
};
|
| 1874 |
+
}
|
| 1875 |
+
|
| 1876 |
+
function closeStatPopovers() {
|
| 1877 |
+
document
|
| 1878 |
+
.querySelectorAll(".stat-popover")
|
| 1879 |
+
.forEach((p) => (p.hidden = true));
|
| 1880 |
+
document
|
| 1881 |
+
.querySelectorAll(".stat-tile.open")
|
| 1882 |
+
.forEach((t) => t.classList.remove("open"));
|
| 1883 |
+
}
|
| 1884 |
+
|
| 1885 |
+
function ensureStatListeners() {
|
| 1886 |
+
if (STATS_LISTENERS) return;
|
| 1887 |
+
STATS_LISTENERS = true;
|
| 1888 |
+
document.addEventListener("click", closeStatPopovers);
|
| 1889 |
+
document.addEventListener("keydown", (e) => {
|
| 1890 |
+
if (e.key === "Escape") closeStatPopovers();
|
| 1891 |
+
});
|
| 1892 |
+
}
|
| 1893 |
+
|
| 1894 |
+
function stateHtml(remote, url) {
|
| 1895 |
+
return remote
|
| 1896 |
+
? `<a class="stat-row-state open" href="${esc(url)}" target="_blank" rel="noopener" title="Open in a new tab">Open ↗</a>`
|
| 1897 |
+
: `<span class="stat-row-state">publish to share</span>`;
|
| 1898 |
+
}
|
| 1899 |
+
|
| 1900 |
+
function scrollToResource(resUrl) {
|
| 1901 |
+
closeStatPopovers();
|
| 1902 |
+
if (!resUrl) return;
|
| 1903 |
+
const el = document.querySelector(
|
| 1904 |
+
`#page .page-body [data-res-url="${CSS.escape(resUrl)}"]:not(.stat-row)`
|
| 1905 |
+
);
|
| 1906 |
+
if (!el) return;
|
| 1907 |
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
| 1908 |
+
el.classList.add("res-flash");
|
| 1909 |
+
setTimeout(() => el.classList.remove("res-flash"), 1500);
|
| 1910 |
+
}
|
| 1911 |
+
|
| 1912 |
+
function dashRowHtml(d) {
|
| 1913 |
+
const inner =
|
| 1914 |
+
`<span class="stat-row-ico">${DASHBOARD_ICON_IMG}</span>` +
|
| 1915 |
+
`<div class="stat-row-main"><div class="stat-row-title">${esc(d.project)}</div>` +
|
| 1916 |
+
`<div class="stat-row-meta">${stateHtml(!d.local, d.url)}</div></div>`;
|
| 1917 |
+
return `<div class="stat-row" data-res-url="${esc(d.resUrl)}" title="Jump to it in the logbook">${inner}</div>`;
|
| 1918 |
+
}
|
| 1919 |
+
|
| 1920 |
+
function artRowHtml(a) {
|
| 1921 |
+
const remote = !a.local && !!a.url;
|
| 1922 |
+
const parts = [a.type, a.size].filter(Boolean).map(esc);
|
| 1923 |
+
const meta = parts.length
|
| 1924 |
+
? `${parts.join(" · ")} · ${stateHtml(remote, a.url)}`
|
| 1925 |
+
: stateHtml(remote, a.url);
|
| 1926 |
+
const inner =
|
| 1927 |
+
`<span class="stat-row-ico">${ARTIFACT_ICON_IMG}</span>` +
|
| 1928 |
+
`<div class="stat-row-main"><div class="stat-row-title">${esc(a.name)}</div>` +
|
| 1929 |
+
`<div class="stat-row-meta">${meta}</div></div>`;
|
| 1930 |
+
return `<div class="stat-row" data-res-url="${esc(a.resUrl)}" title="Jump to it in the logbook">${inner}</div>`;
|
| 1931 |
+
}
|
| 1932 |
+
|
| 1933 |
+
function statTile(icon, alt, singular, plural, head, rowFn) {
|
| 1934 |
+
const tile = document.createElement("button");
|
| 1935 |
+
tile.type = "button";
|
| 1936 |
+
tile.className = "stat-tile";
|
| 1937 |
+
const render = (items) => {
|
| 1938 |
+
const count = items.length;
|
| 1939 |
+
const label = count === 1 ? singular : plural;
|
| 1940 |
+
const caret = count > 0 ? `<span class="stat-caret">▾</span>` : "";
|
| 1941 |
+
tile.innerHTML =
|
| 1942 |
+
`<img class="stat-icon" src="${icon}" alt="${esc(alt)}" />` +
|
| 1943 |
+
`<div class="stat-text"><div class="stat-num">${count}</div>` +
|
| 1944 |
+
`<div class="stat-label">${esc(label)}</div></div>` +
|
| 1945 |
+
caret;
|
| 1946 |
+
tile.disabled = count === 0;
|
| 1947 |
+
if (count > 0) {
|
| 1948 |
+
const pop = document.createElement("div");
|
| 1949 |
+
pop.className = "stat-popover";
|
| 1950 |
+
pop.hidden = true;
|
| 1951 |
+
pop.innerHTML =
|
| 1952 |
+
`<div class="stat-pop-head">${esc(head)}</div>` +
|
| 1953 |
+
items.map(rowFn).join("");
|
| 1954 |
+
pop.addEventListener("click", (e) => {
|
| 1955 |
+
if (e.target.closest("a.stat-row-state")) {
|
| 1956 |
+
e.stopPropagation();
|
| 1957 |
+
return;
|
| 1958 |
+
}
|
| 1959 |
+
e.stopPropagation();
|
| 1960 |
+
const row = e.target.closest(".stat-row");
|
| 1961 |
+
if (row) scrollToResource(row.dataset.resUrl);
|
| 1962 |
+
});
|
| 1963 |
+
tile.appendChild(pop);
|
| 1964 |
+
}
|
| 1965 |
+
};
|
| 1966 |
+
tile.addEventListener("click", (e) => {
|
| 1967 |
+
if (tile.disabled) return;
|
| 1968 |
+
e.stopPropagation();
|
| 1969 |
+
const pop = tile.querySelector(".stat-popover");
|
| 1970 |
+
if (!pop) return;
|
| 1971 |
+
const isOpen = !pop.hidden;
|
| 1972 |
+
closeStatPopovers();
|
| 1973 |
+
if (!isOpen) {
|
| 1974 |
+
pop.hidden = false;
|
| 1975 |
+
tile.classList.add("open");
|
| 1976 |
+
}
|
| 1977 |
+
});
|
| 1978 |
+
return { tile, render };
|
| 1979 |
+
}
|
| 1980 |
+
|
| 1981 |
+
function buildLogbookStats(markdownList) {
|
| 1982 |
+
const token = ++STATS_TOKEN;
|
| 1983 |
+
ensureStatListeners();
|
| 1984 |
+
const { dashboards, artifacts } = collectLogbookResources(markdownList);
|
| 1985 |
+
|
| 1986 |
+
const el = document.createElement("div");
|
| 1987 |
+
el.className = "logbook-stats";
|
| 1988 |
+
const dash = statTile(
|
| 1989 |
+
"./trackio-logo-light.png",
|
| 1990 |
+
"Trackio",
|
| 1991 |
+
"Trackio Dashboard",
|
| 1992 |
+
"Trackio Dashboards",
|
| 1993 |
+
"Dashboards created in this logbook",
|
| 1994 |
+
dashRowHtml
|
| 1995 |
+
);
|
| 1996 |
+
const art = statTile(
|
| 1997 |
+
"./bucket-icon.svg",
|
| 1998 |
+
"Bucket",
|
| 1999 |
+
"Artifact",
|
| 2000 |
+
"Artifacts",
|
| 2001 |
+
"Artifacts created in this logbook",
|
| 2002 |
+
artRowHtml
|
| 2003 |
+
);
|
| 2004 |
+
dash.render(dashboards);
|
| 2005 |
+
art.render(artifacts);
|
| 2006 |
+
el.appendChild(dash.tile);
|
| 2007 |
+
el.appendChild(art.tile);
|
| 2008 |
+
|
| 2009 |
+
const scanText = markdownList
|
| 2010 |
+
.map((md) =>
|
| 2011 |
+
md.replace(/(`{3,4}|~{3,4})(html|raw)[^\n]*\n[\s\S]*?\n\1/g, " ")
|
| 2012 |
+
)
|
| 2013 |
+
.join("\n");
|
| 2014 |
+
const seen = new Set(
|
| 2015 |
+
dashboards.map((d) =>
|
| 2016 |
+
d.local ? `local:${d.project}` : `space:${spaceIdFromUrl(d.url)}`
|
| 2017 |
+
)
|
| 2018 |
+
);
|
| 2019 |
+
const remoteSpaces = new Map();
|
| 2020 |
+
extractUrls(scanText).forEach((url) => {
|
| 2021 |
+
const item = classifyResource(url);
|
| 2022 |
+
if (item && item.kind === "space" && !item.local) {
|
| 2023 |
+
remoteSpaces.set(item.url, item);
|
| 2024 |
+
}
|
| 2025 |
+
});
|
| 2026 |
+
remoteSpaces.forEach((s) => {
|
| 2027 |
+
const key = `space:${s.id}`;
|
| 2028 |
+
if (seen.has(key)) return;
|
| 2029 |
+
getJSON(`https://huggingface.co/api/spaces/${s.id}`)
|
| 2030 |
+
.then((d) => {
|
| 2031 |
+
if (STATS_TOKEN !== token) return;
|
| 2032 |
+
const tags = (d && d.tags) || [];
|
| 2033 |
+
if (
|
| 2034 |
+
!seen.has(key) &&
|
| 2035 |
+
tags.some((t) => String(t).toLowerCase() === "trackio")
|
| 2036 |
+
) {
|
| 2037 |
+
seen.add(key);
|
| 2038 |
+
dashboards.push({
|
| 2039 |
+
project: s.id,
|
| 2040 |
+
local: false,
|
| 2041 |
+
url: s.url,
|
| 2042 |
+
resUrl: s.url,
|
| 2043 |
+
});
|
| 2044 |
+
dashboards.sort((a, b) => a.project.localeCompare(b.project));
|
| 2045 |
+
dash.render(dashboards);
|
| 2046 |
+
}
|
| 2047 |
+
})
|
| 2048 |
+
.catch(() => {});
|
| 2049 |
+
});
|
| 2050 |
+
return el;
|
| 2051 |
+
}
|
| 2052 |
+
|
| 2053 |
+
function buildAgentHint() {
|
| 2054 |
+
const onSpaces =
|
| 2055 |
+
/\.hf\.space$/.test(location.hostname) ||
|
| 2056 |
+
/(^|\.)huggingface\.co$/.test(location.hostname);
|
| 2057 |
+
let source = "";
|
| 2058 |
+
if (onSpaces && MANIFEST.space_id) {
|
| 2059 |
+
source = ` ${MANIFEST.space_id}`;
|
| 2060 |
+
} else if (/^https?:$/.test(location.protocol)) {
|
| 2061 |
+
source = ` ${location.origin}/`;
|
| 2062 |
+
}
|
| 2063 |
+
const command = `trackio logbook read${source}`;
|
| 2064 |
+
const tokens = MANIFEST.agent_view_tokens;
|
| 2065 |
+
const div = document.createElement("div");
|
| 2066 |
+
div.className = "agent-hint";
|
| 2067 |
+
const label = document.createElement("span");
|
| 2068 |
+
label.className = "agent-hint-label";
|
| 2069 |
+
label.textContent = "Read from the CLI:";
|
| 2070 |
+
const code = document.createElement("code");
|
| 2071 |
+
code.textContent = command;
|
| 2072 |
+
const copy = document.createElement("button");
|
| 2073 |
+
copy.className = "copy";
|
| 2074 |
+
copy.type = "button";
|
| 2075 |
+
copy.title = "Copy";
|
| 2076 |
+
copy.textContent = "⧉";
|
| 2077 |
+
copy.addEventListener("click", () => copyText(command, copy, "⧉"));
|
| 2078 |
+
const note = document.createElement("span");
|
| 2079 |
+
note.className = "agent-hint-note";
|
| 2080 |
+
note.textContent =
|
| 2081 |
+
"compact view for agents" + (tokens ? ` · ~${fmt(tokens)} tokens` : "");
|
| 2082 |
+
div.appendChild(label);
|
| 2083 |
+
div.appendChild(code);
|
| 2084 |
+
div.appendChild(copy);
|
| 2085 |
+
div.appendChild(note);
|
| 2086 |
+
return div;
|
| 2087 |
+
}
|
| 2088 |
+
|
| 2089 |
+
function currentSlug() {
|
| 2090 |
+
const slug = (location.hash || "").replace(/^#\//, "") || MANIFEST.root.slug;
|
| 2091 |
+
return findNode(MANIFEST.root, slug) ? slug : MANIFEST.root.slug;
|
| 2092 |
+
}
|
| 2093 |
+
|
| 2094 |
+
function scrollToHash(opts = {}) {
|
| 2095 |
+
const slug = currentSlug();
|
| 2096 |
+
if (!location.hash) {
|
| 2097 |
+
window.scrollTo({ top: 0, behavior: opts.behavior || "auto" });
|
| 2098 |
+
highlight(slug);
|
| 2099 |
+
return;
|
| 2100 |
+
}
|
| 2101 |
+
const section = document.getElementById("/" + slug);
|
| 2102 |
+
if (section) section.scrollIntoView({ behavior: opts.behavior || "smooth" });
|
| 2103 |
+
highlight(slug);
|
| 2104 |
+
}
|
| 2105 |
+
|
| 2106 |
+
function navigateToLogbookSlug(target) {
|
| 2107 |
+
const slug = String(target || "").replace(/^#?\//, "").trim();
|
| 2108 |
+
if (!slug || !findNode(MANIFEST.root, slug)) return;
|
| 2109 |
+
const hash = "#/" + slug;
|
| 2110 |
+
if (location.hash === hash) {
|
| 2111 |
+
scrollToHash({ behavior: "smooth" });
|
| 2112 |
+
} else {
|
| 2113 |
+
location.hash = hash;
|
| 2114 |
+
}
|
| 2115 |
+
}
|
| 2116 |
+
|
| 2117 |
+
function setupFigureNavigation() {
|
| 2118 |
+
window.addEventListener("message", (event) => {
|
| 2119 |
+
const data = event.data;
|
| 2120 |
+
if (!data || data.type !== "trackio-logbook:navigate") return;
|
| 2121 |
+
// Only accept messages from one of this logbook's sandboxed figure
|
| 2122 |
+
// iframes, rather than from an arbitrary same-origin page.
|
| 2123 |
+
const isFigureFrame = Array.from(
|
| 2124 |
+
document.querySelectorAll("iframe.figure-frame")
|
| 2125 |
+
).some((frame) => frame.contentWindow === event.source);
|
| 2126 |
+
if (!isFigureFrame) return;
|
| 2127 |
+
navigateToLogbookSlug(data.target);
|
| 2128 |
+
});
|
| 2129 |
+
}
|
| 2130 |
+
|
| 2131 |
+
let SCROLL_FRAME = 0;
|
| 2132 |
+
function updateActiveSection() {
|
| 2133 |
+
cancelAnimationFrame(SCROLL_FRAME);
|
| 2134 |
+
SCROLL_FRAME = requestAnimationFrame(() => {
|
| 2135 |
+
const sections = Array.from(document.querySelectorAll(".page-section"));
|
| 2136 |
+
if (!sections.length) return;
|
| 2137 |
+
const marker = Math.min(window.innerHeight * 0.28, 180);
|
| 2138 |
+
let active = sections[0];
|
| 2139 |
+
sections.forEach((section) => {
|
| 2140 |
+
if (section.getBoundingClientRect().top <= marker) active = section;
|
| 2141 |
+
});
|
| 2142 |
+
if (
|
| 2143 |
+
window.innerHeight + window.scrollY >=
|
| 2144 |
+
document.documentElement.scrollHeight - 2
|
| 2145 |
+
) {
|
| 2146 |
+
active = sections[sections.length - 1];
|
| 2147 |
+
}
|
| 2148 |
+
highlight(active.dataset.slug);
|
| 2149 |
+
});
|
| 2150 |
+
}
|
| 2151 |
+
|
| 2152 |
+
function startLiveReload() {
|
| 2153 |
+
if (!isLocalPreview()) return;
|
| 2154 |
+
setInterval(async () => {
|
| 2155 |
+
try {
|
| 2156 |
+
const next = await fetchManifest();
|
| 2157 |
+
if (!next || next.revision === MANIFEST.revision) return;
|
| 2158 |
+
MANIFEST = next;
|
| 2159 |
+
clearPageCache();
|
| 2160 |
+
document.title = MANIFEST.title + " · Trackio Logbook";
|
| 2161 |
+
document.getElementById("book-title").textContent = MANIFEST.title;
|
| 2162 |
+
document.getElementById("book-head").setAttribute("aria-label", MANIFEST.title);
|
| 2163 |
+
buildTree();
|
| 2164 |
+
renderLogbook({ preserveScroll: true });
|
| 2165 |
+
} catch (e) {}
|
| 2166 |
+
}, LIVE_RELOAD_MS);
|
| 2167 |
+
}
|
| 2168 |
+
|
| 2169 |
+
function setupConnect() {
|
| 2170 |
+
const space = MANIFEST.space_id;
|
| 2171 |
+
if (!space) return;
|
| 2172 |
+
const steps = [
|
| 2173 |
+
{ t: "Install Trackio, if you don't have it yet.", c: "uv tool install trackio" },
|
| 2174 |
+
{ t: "Add the Trackio skill for your agent, then reload it.", c: "trackio skills add" },
|
| 2175 |
+
{ t: "Connect to this logbook.", c: `trackio logbook open ${space}` },
|
| 2176 |
+
];
|
| 2177 |
+
const ol = document.getElementById("connect-steps");
|
| 2178 |
+
steps.forEach((s, i) => {
|
| 2179 |
+
const li = document.createElement("li");
|
| 2180 |
+
const title = document.createElement("div");
|
| 2181 |
+
title.className = "step-title";
|
| 2182 |
+
title.textContent = `${i + 1}. ${s.t}`;
|
| 2183 |
+
const block = document.createElement("div");
|
| 2184 |
+
block.className = "codeblock";
|
| 2185 |
+
const code = document.createElement("code");
|
| 2186 |
+
code.textContent = s.c;
|
| 2187 |
+
const copy = document.createElement("button");
|
| 2188 |
+
copy.className = "copy";
|
| 2189 |
+
copy.type = "button";
|
| 2190 |
+
copy.title = "Copy";
|
| 2191 |
+
copy.textContent = "⧉";
|
| 2192 |
+
copy.addEventListener("click", () => copyText(s.c, copy, "⧉"));
|
| 2193 |
+
block.appendChild(code);
|
| 2194 |
+
block.appendChild(copy);
|
| 2195 |
+
li.appendChild(title);
|
| 2196 |
+
li.appendChild(block);
|
| 2197 |
+
ol.appendChild(li);
|
| 2198 |
+
});
|
| 2199 |
+
|
| 2200 |
+
const agentPrompt =
|
| 2201 |
+
`Read and help maintain this Trackio experiment logbook ("${MANIFEST.title}").\n\n` +
|
| 2202 |
+
"1. If you don't have Trackio, install it: uv tool install trackio\n" +
|
| 2203 |
+
"2. Add the Trackio skill for your agent: trackio skills add (then reload)\n" +
|
| 2204 |
+
`3. Connect to this logbook: trackio logbook open ${space}\n\n` +
|
| 2205 |
+
"Start with `trackio logbook read`; use `trackio logbook read page \"...\"` " +
|
| 2206 |
+
"for a page-level view, then fetch relevant details with " +
|
| 2207 |
+
"`trackio logbook read cell cell_<id>`. If I've given you " +
|
| 2208 |
+
'write access to the Space, add findings with `trackio logbook cell markdown "..." ' +
|
| 2209 |
+
'--page "..."` and they will sync back automatically.';
|
| 2210 |
+
|
| 2211 |
+
const foot = document.getElementById("sidebar-foot");
|
| 2212 |
+
foot.hidden = false;
|
| 2213 |
+
const modal = document.getElementById("modal");
|
| 2214 |
+
const open = () => (modal.hidden = false);
|
| 2215 |
+
const close = () => (modal.hidden = true);
|
| 2216 |
+
document.getElementById("connect-btn").addEventListener("click", open);
|
| 2217 |
+
document.getElementById("modal-close").addEventListener("click", close);
|
| 2218 |
+
modal.querySelector(".modal-backdrop").addEventListener("click", close);
|
| 2219 |
+
document.addEventListener("keydown", (e) => {
|
| 2220 |
+
if (e.key === "Escape") close();
|
| 2221 |
+
});
|
| 2222 |
+
const agentBtn = document.getElementById("copy-agent");
|
| 2223 |
+
agentBtn.addEventListener("click", () =>
|
| 2224 |
+
copyText(agentPrompt, agentBtn, "Copy for agent")
|
| 2225 |
+
);
|
| 2226 |
+
}
|
| 2227 |
+
|
| 2228 |
+
function copyText(text, btn, restore) {
|
| 2229 |
+
const done = () => {
|
| 2230 |
+
const prev = btn.textContent;
|
| 2231 |
+
btn.textContent = restore === "⧉" ? "✓" : "Copied!";
|
| 2232 |
+
btn.classList.add("copied");
|
| 2233 |
+
setTimeout(() => {
|
| 2234 |
+
btn.textContent = restore;
|
| 2235 |
+
btn.classList.remove("copied");
|
| 2236 |
+
}, 1400);
|
| 2237 |
+
void prev;
|
| 2238 |
+
};
|
| 2239 |
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
| 2240 |
+
navigator.clipboard.writeText(text).then(done, done);
|
| 2241 |
+
} else {
|
| 2242 |
+
const ta = document.createElement("textarea");
|
| 2243 |
+
ta.value = text;
|
| 2244 |
+
document.body.appendChild(ta);
|
| 2245 |
+
ta.select();
|
| 2246 |
+
try {
|
| 2247 |
+
document.execCommand("copy");
|
| 2248 |
+
} catch (e) {}
|
| 2249 |
+
document.body.removeChild(ta);
|
| 2250 |
+
done();
|
| 2251 |
+
}
|
| 2252 |
+
}
|
| 2253 |
+
|
| 2254 |
+
async function init() {
|
| 2255 |
+
MANIFEST = await fetchManifest();
|
| 2256 |
+
document.title = MANIFEST.title + " · Trackio Logbook";
|
| 2257 |
+
document.getElementById("book-title").textContent = MANIFEST.title;
|
| 2258 |
+
document.getElementById("book-head").setAttribute("aria-label", MANIFEST.title);
|
| 2259 |
+
document.getElementById("book-head").addEventListener("click", () => {
|
| 2260 |
+
const target = "#/" + MANIFEST.root.slug;
|
| 2261 |
+
if (location.hash === target) scrollToHash();
|
| 2262 |
+
else location.hash = target;
|
| 2263 |
+
});
|
| 2264 |
+
buildTree();
|
| 2265 |
+
setupConnect();
|
| 2266 |
+
setupResourceHover();
|
| 2267 |
+
setupFigureNavigation();
|
| 2268 |
+
window.addEventListener("hashchange", () => scrollToHash());
|
| 2269 |
+
window.addEventListener("scroll", updateActiveSection, { passive: true });
|
| 2270 |
+
await renderLogbook();
|
| 2271 |
+
startLiveReload();
|
| 2272 |
+
}
|
| 2273 |
+
|
| 2274 |
+
init();
|
| 2275 |
+
})();
|
logbook.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"agent_view_tokens": 7395,
|
| 3 |
+
"artifact": "agentic-theorem-prover-theory-repro/exact-six-cpu-evidence:v0",
|
| 4 |
+
"artifact_anchor": "https://huggingface.co/buckets/neonforestmist/repro-agentic-theorem-prover-theory-artifacts#agentic-theorem-prover-theory-repro/exact-six-cpu-evidence:v0",
|
| 5 |
+
"artifact_bucket": "https://huggingface.co/buckets/neonforestmist/repro-agentic-theorem-prover-theory-artifacts",
|
| 6 |
+
"bucket_url": "https://huggingface.co/buckets/neonforestmist/repro-agentic-theorem-prover-theory-artifacts",
|
| 7 |
+
"emoji": "🎯",
|
| 8 |
+
"expected_parent_sha": "a19abf404a8863ec22186ac6dac7709b995b2982",
|
| 9 |
+
"offline_staged": true,
|
| 10 |
+
"paper": {
|
| 11 |
+
"arxiv_id": "2602.10538v2",
|
| 12 |
+
"openreview_id": "hAQZl57Nvx"
|
| 13 |
+
},
|
| 14 |
+
"publication_status": "PUBLISHED_PENDING_VERDICT",
|
| 15 |
+
"revision": "0",
|
| 16 |
+
"root": {
|
| 17 |
+
"children": [
|
| 18 |
+
{
|
| 19 |
+
"children": [],
|
| 20 |
+
"file": "pages/executive-summary/page.md",
|
| 21 |
+
"slug": "executive-summary",
|
| 22 |
+
"title": "Executive summary"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"children": [],
|
| 26 |
+
"file": "pages/claim-1-optimal-policies/page.md",
|
| 27 |
+
"slug": "claim-1-optimal-policies",
|
| 28 |
+
"title": "Claim 1 - Optimal policies"
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"children": [],
|
| 32 |
+
"file": "pages/claim-2-bellman-structure/page.md",
|
| 33 |
+
"slug": "claim-2-bellman-structure",
|
| 34 |
+
"title": "Claim 2 - Bellman structure"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"children": [],
|
| 38 |
+
"file": "pages/claim-3-score-regret/page.md",
|
| 39 |
+
"slug": "claim-3-score-regret",
|
| 40 |
+
"title": "Claim 3 - Score regret"
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"children": [],
|
| 44 |
+
"file": "pages/claim-4-statistical-provability/page.md",
|
| 45 |
+
"slug": "claim-4-statistical-provability",
|
| 46 |
+
"title": "Claim 4 - Statistical provability"
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"children": [],
|
| 50 |
+
"file": "pages/claim-5-component-complexity/page.md",
|
| 51 |
+
"slug": "claim-5-component-complexity",
|
| 52 |
+
"title": "Claim 5 - Component complexity"
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"children": [],
|
| 56 |
+
"file": "pages/claim-6-failure-boundary/page.md",
|
| 57 |
+
"slug": "claim-6-failure-boundary",
|
| 58 |
+
"title": "Claim 6 - Failure boundary"
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"children": [],
|
| 62 |
+
"file": "pages/conclusion/page.md",
|
| 63 |
+
"slug": "conclusion",
|
| 64 |
+
"title": "Conclusion"
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"file": "pages/index.md",
|
| 68 |
+
"slug": "index",
|
| 69 |
+
"title": "Agentic Theorem Prover Theory — Exact 12/12 CPU Reproduction"
|
| 70 |
+
},
|
| 71 |
+
"schema_version": 1,
|
| 72 |
+
"source_placeholder": "neonforestmist/placeholder28",
|
| 73 |
+
"space_id": "neonforestmist/repro-agentic-theorem-prover-theory",
|
| 74 |
+
"tags": [
|
| 75 |
+
"icml2026-repro",
|
| 76 |
+
"paper-hAQZl57Nvx"
|
| 77 |
+
],
|
| 78 |
+
"target_space": "neonforestmist/repro-agentic-theorem-prover-theory",
|
| 79 |
+
"title": "Agentic Theorem Prover Theory — Exact 12/12 CPU Reproduction",
|
| 80 |
+
"updated_at": "2026-07-18T23:32:19+00:00"
|
| 81 |
+
}
|
pages/claim-1-optimal-policies/page.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 01 — Optimal policies and Bellman structure
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_93fba650a8cd","title":"Compact state encoding and attained policies","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Claims 1–2 — VERIFIED (4/4)
|
| 9 |
+
|
| 10 |
+
> Theorem 1 establishes that the space of finite measures with bounded mass forms a compact metric space under the bounded Lipschitz metric, providing the topological foundation for the measure-valued goal encoding of the reachability MDP (Theorem 1, Section 3).
|
| 11 |
+
|
| 12 |
+
> Theorem 2 proves that under mild Feller-type continuity conditions on the transition kernel, optimal deterministic Markov policies exist for the finite-horizon time-bounded reachability MDP (Theorem 2).
|
| 13 |
+
|
| 14 |
+
## Source theorem chain
|
| 15 |
+
|
| 16 |
+
Version 2 encodes a proof state containing finitely many goals as a positive atomic measure on a compact goal-embedding space. With total mass truncated at `W`, Theorem 1 (`thm:p1-2.1`) states that `M_{<=W}(X)` is compact under the bounded-Lipschitz metric. A solved proof is the zero measure. This gives a permutation-invariant state representation that permits variable goal counts.
|
| 17 |
+
|
| 18 |
+
The time-bounded proving objective is finite-horizon reachability of `{0}`. Starting from `V*_0(x)=1[x in G]`, the optimal recursion is
|
| 19 |
+
|
| 20 |
+
`Q*_b(x,a) = integral V*_{b-1}(x') P(dx' | x,a)`
|
| 21 |
+
|
| 22 |
+
and
|
| 23 |
+
|
| 24 |
+
`V*_{b+1}(x) = 1[x in G] OR sup_a Q*_{b+1}(x,a)`.
|
| 25 |
+
|
| 26 |
+
Under compact actions, weak/Feller continuity of the kernel, and the closed goal set, Theorem 2 (`thm:p1-4.2`) states that every action supremum is attained and an optimal deterministic Markov policy exists.
|
| 27 |
+
|
| 28 |
+
## Exact finite-MDP audit
|
| 29 |
+
|
| 30 |
+
The local audit constructs **48** finite compact reachability MDPs. Each has a goal, a dead state, three active proof states, two or three actions, rational transition probabilities, and horizons from 2 through 7. Finite state/action spaces satisfy compactness and Feller continuity trivially.
|
| 31 |
+
|
| 32 |
+
Every transition row sums to exactly one as a rational number. Backward induction performs **2,160** separate checks that `V_b=T V_{b-1}` and that the recorded action attains each maximum. There is no numerical tolerance and no sampled trajectory estimate.
|
| 33 |
+
|
| 34 |
+
For **18** smaller instances the audit independently enumerates all time-indexed deterministic Markov mappings: **1,152 policies** total. Direct policy evaluation matches the Bellman optimum in every case. This is the operational content of the existence result: backward induction does not merely report a supremum; it constructs an attaining policy.
|
| 35 |
+
|
| 36 |
+
The compactness surface is separately exercised by **40** finite atomic-measure grids with 2–6 goal atoms and mass bounds 1–8. The largest contains **3,003** measure states. Finiteness certifies compactness for each grid and makes the bounded-mass constraint explicit.
|
| 37 |
+
|
| 38 |
+
## Interpretation boundary
|
| 39 |
+
|
| 40 |
+
The finite grids do not prove Theorem 1 for arbitrary compact metric goal spaces. The general result relies on the primary proof's tightness/Prokhorov and bounded-Lipschitz weak-convergence argument. Likewise, finite kernel continuity is automatic and does not erase the Feller assumption for continuous state spaces. The package therefore combines source-locked universal theorems with exact finite witnesses, never extrapolating a sweep into a new proof.
|
pages/claim-2-bellman-structure/page.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 02 — Bellman certificates and score regret
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_feff67af660e","title":"Sub/super inequalities and the factor two","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Claims 3–4 — VERIFIED (4/4)
|
| 9 |
+
|
| 10 |
+
> Theorem 3 shows that sequences of functions satisfying Bellman sub-/super-solution inequalities yield provability certificates (upper and lower bounds on the optimal value function) without requiring the full MDP to be solved, with the certificate gap defined as UB(x0) - LB(x0) (Theorem 3, Definition 5, Section 5).
|
| 11 |
+
|
| 12 |
+
> Theorem 4 bounds the worst-case regret of score-guided planning under uniform score-approximation error εb as 0 ≤ V*_B(x0) - V^{πh}_B(x0) ≤ 2∑_{b=1}^{B} εb, showing performance degrades linearly with accumulated approximation error (Theorem 4, Section 6).
|
| 13 |
+
|
| 14 |
+
## Certificates without solving the full MDP
|
| 15 |
+
|
| 16 |
+
The Bellman operator is monotone: `V <= W` implies `T V <= T W`. Theorem 3 (`thm:p1-5.3`) turns that simple order fact into a certificate system. An upper sequence satisfying
|
| 17 |
+
|
| 18 |
+
`U_0 >= 1_G`, `U_{b+1} >= T U_b`
|
| 19 |
+
|
| 20 |
+
obeys `U_b >= V*_b`; a lower sequence satisfying
|
| 21 |
+
|
| 22 |
+
`L_0 <= 1_G`, `L_{b+1} <= T L_b`
|
| 23 |
+
|
| 24 |
+
obeys `L_b <= V*_b`. Definition 5 names `[L_B(x0), U_B(x0)]` a provability certificate and `U_B(x0)-L_B(x0)` its gap.
|
| 25 |
+
|
| 26 |
+
The local reproduction creates **48** exact certificate pairs by shifting the exact value sequence up/down and clipping to `[0,1]`. It checks **3,720** initial, recursive, and sandwich inequalities. Every lower sequence remains below `V*`; every upper sequence remains above it; every reported gap is computed as an exact rational number.
|
| 27 |
+
|
| 28 |
+
## Score-guided planning
|
| 29 |
+
|
| 30 |
+
Let `h_b` approximate `Q*_b` uniformly with error `epsilon_b`, and let the policy greedily maximize `h_b`. The one-step comparison uses three inequalities:
|
| 31 |
+
|
| 32 |
+
`Q*(a*) <= h(a*) + epsilon <= h(a_hat) + epsilon <= Q*(a_hat) + 2 epsilon`.
|
| 33 |
+
|
| 34 |
+
Theorem 4 (`thm:p1-6.3`) telescopes this through the finite horizon:
|
| 35 |
+
|
| 36 |
+
`0 <= V*_B(x0) - V^pi_h_B(x0) <= 2 sum_{b=1}^B epsilon_b`.
|
| 37 |
+
|
| 38 |
+
The audit perturbs exact action values inside a rational error envelope for **200** independent MDP/horizon cases. It then recomputes the greedy policy value from the kernel rather than assuming the bound. All regrets are nonnegative and all remain inside `2 sum epsilon_b`; **158** cases actually change policy and incur positive regret.
|
| 39 |
+
|
| 40 |
+
The factor two cannot be casually reduced. A one-step witness has a bad action with `Q=0`, a best action with `Q=2 epsilon`, and tied scores `h=epsilon`. Deterministic tie-breaking selects the bad action. The realized regret is exactly `2 epsilon`, matching the theorem constant.
|
| 41 |
+
|
| 42 |
+
## Where statistical complexity enters
|
| 43 |
+
|
| 44 |
+
The score error is not assumed free. Version 2's Lemma 7.1 controls adaptive uniform deviations with covering numbers, and its supplement distinguishes explicit net coverage from sequential Rademacher-complexity control under adaptive/on-policy data. This package reproduces the downstream bound exactly and preserves those hypotheses. It does not claim that arbitrary self-generated rollouts automatically satisfy uniform coverage.
|
pages/claim-3-score-regret/page.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 02 — Bellman certificates and score regret
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_feff67af660e","title":"Sub/super inequalities and the factor two","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Claims 3–4 — VERIFIED (4/4)
|
| 9 |
+
|
| 10 |
+
> Theorem 3 shows that sequences of functions satisfying Bellman sub-/super-solution inequalities yield provability certificates (upper and lower bounds on the optimal value function) without requiring the full MDP to be solved, with the certificate gap defined as UB(x0) - LB(x0) (Theorem 3, Definition 5, Section 5).
|
| 11 |
+
|
| 12 |
+
> Theorem 4 bounds the worst-case regret of score-guided planning under uniform score-approximation error εb as 0 ≤ V*_B(x0) - V^{πh}_B(x0) ≤ 2∑_{b=1}^{B} εb, showing performance degrades linearly with accumulated approximation error (Theorem 4, Section 6).
|
| 13 |
+
|
| 14 |
+
## Certificates without solving the full MDP
|
| 15 |
+
|
| 16 |
+
The Bellman operator is monotone: `V <= W` implies `T V <= T W`. Theorem 3 (`thm:p1-5.3`) turns that simple order fact into a certificate system. An upper sequence satisfying
|
| 17 |
+
|
| 18 |
+
`U_0 >= 1_G`, `U_{b+1} >= T U_b`
|
| 19 |
+
|
| 20 |
+
obeys `U_b >= V*_b`; a lower sequence satisfying
|
| 21 |
+
|
| 22 |
+
`L_0 <= 1_G`, `L_{b+1} <= T L_b`
|
| 23 |
+
|
| 24 |
+
obeys `L_b <= V*_b`. Definition 5 names `[L_B(x0), U_B(x0)]` a provability certificate and `U_B(x0)-L_B(x0)` its gap.
|
| 25 |
+
|
| 26 |
+
The local reproduction creates **48** exact certificate pairs by shifting the exact value sequence up/down and clipping to `[0,1]`. It checks **3,720** initial, recursive, and sandwich inequalities. Every lower sequence remains below `V*`; every upper sequence remains above it; every reported gap is computed as an exact rational number.
|
| 27 |
+
|
| 28 |
+
## Score-guided planning
|
| 29 |
+
|
| 30 |
+
Let `h_b` approximate `Q*_b` uniformly with error `epsilon_b`, and let the policy greedily maximize `h_b`. The one-step comparison uses three inequalities:
|
| 31 |
+
|
| 32 |
+
`Q*(a*) <= h(a*) + epsilon <= h(a_hat) + epsilon <= Q*(a_hat) + 2 epsilon`.
|
| 33 |
+
|
| 34 |
+
Theorem 4 (`thm:p1-6.3`) telescopes this through the finite horizon:
|
| 35 |
+
|
| 36 |
+
`0 <= V*_B(x0) - V^pi_h_B(x0) <= 2 sum_{b=1}^B epsilon_b`.
|
| 37 |
+
|
| 38 |
+
The audit perturbs exact action values inside a rational error envelope for **200** independent MDP/horizon cases. It then recomputes the greedy policy value from the kernel rather than assuming the bound. All regrets are nonnegative and all remain inside `2 sum epsilon_b`; **158** cases actually change policy and incur positive regret.
|
| 39 |
+
|
| 40 |
+
The factor two cannot be casually reduced. A one-step witness has a bad action with `Q=0`, a best action with `Q=2 epsilon`, and tied scores `h=epsilon`. Deterministic tie-breaking selects the bad action. The realized regret is exactly `2 epsilon`, matching the theorem constant.
|
| 41 |
+
|
| 42 |
+
## Where statistical complexity enters
|
| 43 |
+
|
| 44 |
+
The score error is not assumed free. Version 2's Lemma 7.1 controls adaptive uniform deviations with covering numbers, and its supplement distinguishes explicit net coverage from sequential Rademacher-complexity control under adaptive/on-policy data. This package reproduces the downstream bound exactly and preserves those hypotheses. It does not claim that arbitrary self-generated rollouts automatically satisfy uniform coverage.
|
pages/claim-4-statistical-provability/page.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 03 — Component-sensitive statistical provability
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_3d4a1421bfa9","title":"Margins, geometry, labels, and proof length","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Claims 5–6 — VERIFIED (4/4)
|
| 9 |
+
|
| 10 |
+
> Theorem 5 shows that under a margin condition on action-value separation, the expected regret improves to O(Bε^{β+1}) for β>0, i.e., faster than the worst-case linear-in-B rate of Theorem 4 (Theorem 5).
|
| 11 |
+
|
| 12 |
+
> Theorem 6 derives a high-probability estimation-error bound of order O(L H d_D/(d_D+2) (log(n/δ)/n)^{1/(d_D+2)}) under a doubling-dimension assumption on the relevant problem domain, formalizing how statistical complexity of a biased real-world problem distribution governs sample efficiency (Theorem 6, Section 7).
|
| 13 |
+
|
| 14 |
+
The point of statistical provability is distributional: performance is evaluated on a specified, possibly biased, theorem stream under a finite proof budget. Worst-case proof-search hardness is not contradicted. The analysis says which components help when the relevant state-action occupancy has exploitable geometry and score learning is accurate there.
|
| 15 |
+
|
| 16 |
+
## Margin fast rate
|
| 17 |
+
|
| 18 |
+
Under a small-gap tail `nu_b(Delta <= t) <= C_Delta t^beta`, score perturbations can change the selected action only when `Delta <= 2 epsilon`. Multiplying the `O(epsilon)` loss by the `O(epsilon^beta)` probability of such near ties yields Theorem 5's `O(B epsilon^(beta+1))` expected regret.
|
| 19 |
+
|
| 20 |
+
The reproduction uses a normalized gap family with CDF `t^beta`, for which the truncated expected regret is available in closed form. Across **45** combinations of `beta in {0.5,1,2}`, horizons `{1,4,16}`, and five error scales, every value lies below the theorem envelope. All **36** adjacent log-log slopes equal `beta+1` to machine precision; the observed slope range is `1.5–3.0`.
|
| 21 |
+
|
| 22 |
+
## Doubling-dimension estimation rate
|
| 23 |
+
|
| 24 |
+
Theorem 6 (`thm:p1-7.3`) combines approximation error with a high-probability estimation term, hiding polylogarithms:
|
| 25 |
+
|
| 26 |
+
`epsilon_est = O(L_H^(d_D/(d_D+2)) (log(n/delta)/n)^(1/(d_D+2)))`.
|
| 27 |
+
|
| 28 |
+
The **75** deterministic rate certificates cross doubling dimensions `1,2,4,8,16`, Lipschitz scales `0.5,1,2`, and sample sizes from `1,000` through **10,000,000**. For every fixed geometry the bound decreases strictly with sample size. For fixed sample size, higher effective dimension slows the rate. Approximation error remains additive instead of disappearing with more labels.
|
| 29 |
+
|
| 30 |
+
## The component map
|
| 31 |
+
|
| 32 |
+
- Proof shortening: reduces horizon/occupancy multiplier.
|
| 33 |
+
- Retrieval and representation: reduce approximation error, Lipschitz constants, or effective doubling dimension.
|
| 34 |
+
- Verifier feedback: creates bounded rollout labels and increases covered sample size.
|
| 35 |
+
- Top-k/beam search: retains optimal actions when score error is below the action gap.
|
| 36 |
+
- Biased workload: concentrates occupancy on a lower-complexity relevant domain.
|
| 37 |
+
|
| 38 |
+
The audit records **25** one-factor interventions. Shortening the proof horizon lowers the accumulated regret multiplier. Improving approximation lowers the irreducible score envelope. Retrieval/representation can reduce effective dimension or Lipschitz scale. More verified rollout labels reduce the statistical term when coverage holds. Larger margin exponent makes harmful misranking rarer.
|
| 39 |
+
|
| 40 |
+
These are component-sensitive predictions, not a claim that one component always dominates. In adversarial or off-support regimes, approximation, coverage, or margin assumptions can fail. That boundary is why the explanation applies to biased real-world problem distributions rather than all formal mathematics uniformly.
|
pages/claim-5-component-complexity/page.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 04 — Exact claims, source versions, and falsifiers
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_8c76ae5dba04","title":"Challenge ingestion and revision boundary","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Exact official claim surface
|
| 9 |
+
|
| 10 |
+
The six scoring-operative anchored claims are repeated verbatim:
|
| 11 |
+
|
| 12 |
+
1. Theorem 1 establishes that the space of finite measures with bounded mass forms a compact metric space under the bounded Lipschitz metric, providing the topological foundation for the measure-valued goal encoding of the reachability MDP (Theorem 1, Section 3).
|
| 13 |
+
|
| 14 |
+
2. Theorem 2 proves that under mild Feller-type continuity conditions on the transition kernel, optimal deterministic Markov policies exist for the finite-horizon time-bounded reachability MDP (Theorem 2).
|
| 15 |
+
|
| 16 |
+
3. Theorem 3 shows that sequences of functions satisfying Bellman sub-/super-solution inequalities yield provability certificates (upper and lower bounds on the optimal value function) without requiring the full MDP to be solved, with the certificate gap defined as UB(x0) - LB(x0) (Theorem 3, Definition 5, Section 5).
|
| 17 |
+
|
| 18 |
+
4. Theorem 4 bounds the worst-case regret of score-guided planning under uniform score-approximation error εb as 0 ≤ V*_B(x0) - V^{πh}_B(x0) ≤ 2∑_{b=1}^{B} εb, showing performance degrades linearly with accumulated approximation error (Theorem 4, Section 6).
|
| 19 |
+
|
| 20 |
+
5. Theorem 5 shows that under a margin condition on action-value separation, the expected regret improves to O(Bε^{β+1}) for β>0, i.e., faster than the worst-case linear-in-B rate of Theorem 4 (Theorem 5).
|
| 21 |
+
|
| 22 |
+
6. Theorem 6 derives a high-probability estimation-error bound of order O(L H d_D/(d_D+2) (log(n/δ)/n)^{1/(d_D+2)}) under a doubling-dimension assumption on the relevant problem domain, formalizing how statistical complexity of a biased real-world problem distribution governs sample efficiency (Theorem 6, Section 7).
|
| 23 |
+
|
| 24 |
+
For provenance, the legacy three-claim base layer is also retained verbatim:
|
| 25 |
+
|
| 26 |
+
1. Proves existence of optimal policies under mild regularity using Bellman structure in time-bounded reachability MDPs
|
| 27 |
+
2. Derives provability certificates via sub-/super-solution inequalities and bounds performance gap of score-guided planning in terms of approximation error and sequential statistical complexity
|
| 28 |
+
3. Provides principled component-sensitive explanation of when agentic theorem provers succeed on biased real-world problem distributions
|
| 29 |
+
|
| 30 |
+
## Version lock
|
| 31 |
+
|
| 32 |
+
- OpenReview: `hAQZl57Nvx`; arXiv: `2602.10538`
|
| 33 |
+
- v2 PDF SHA-256: `b725e91c0cfe924e6aa536d34fbde4da905003a3dfc94b65b7c7c050d6b1a9ad`
|
| 34 |
+
- v2 source SHA-256: `ffd5a325a9f10e60635b7b36a38a8c0dd6dad9a3063eaf5c0134c0bf2d3fb0f6`
|
| 35 |
+
- v3 source SHA-256: `ba30962d80ae1725cccd6e2fdba44abdf94485dd63ffb5081e2e5666af6abd9e`
|
| 36 |
+
- v2 theory TeX SHA-256: `8d06629c1f87a8409e449b6c11b8d0ced99feefa3e97d38cf407353525e9fcc2`
|
| 37 |
+
- target: `neonforestmist/repro-agentic-theorem-prover-theory`
|
| 38 |
+
- tags: `icml2026-repro`, `paper-hAQZl57Nvx`
|
| 39 |
+
|
| 40 |
+
All six numbered labels are present in v2 and absent in v3. This is not a contradiction: v3 is a later rewrite, and its revised abstract centers faithful state abstraction and occupancy-weighted offline regression. Because the challenge text was generated from v2, v2 is the decisive source for scoring. v3 is retained to expose drift and prevent accidental source mixing.
|
| 41 |
+
|
| 42 |
+
At the read-only lock, the author Space listing contained neither the exact target nor `paper-hAQZl57Nvx`; the official verdict snapshot contained no matching target or OpenReview ID. No competitor file was opened.
|
| 43 |
+
|
| 44 |
+
## Destructive controls
|
| 45 |
+
|
| 46 |
+
| mutation | outcome | witness |
|
| 47 |
+
| --- | --- | --- |
|
| 48 |
+
| `use_v3_as_numbered_source` | killed | v3 lacks all six v2 labels |
|
| 49 |
+
| `drop_compactness` | killed | Theorem 2 assumption gate |
|
| 50 |
+
| `drop_feller_continuity` | killed | Theorem 2 assumption gate |
|
| 51 |
+
| `replace_supremum_by_average` | killed | Bellman identity mismatch |
|
| 52 |
+
| `reverse_subsolution_inequality` | killed | exact lower-certificate gate |
|
| 53 |
+
| `reverse_supersolution_inequality` | killed | exact upper-certificate gate |
|
| 54 |
+
| `omit_certificate_gap` | killed | judge-visible Definition 5 gate |
|
| 55 |
+
| `use_one_epsilon_regret` | killed | tight factor-two witness |
|
| 56 |
+
| `sum_only_last_depth_error` | killed | multi-depth exact regret gate |
|
| 57 |
+
| `linear_margin_rate` | killed | quadratic beta=1 witness |
|
| 58 |
+
| `wrong_margin_exponent` | killed | log-log slope gate |
|
| 59 |
+
| `dimension_free_rate` | killed | dimension sensitivity |
|
| 60 |
+
| `omit_approximation_error` | killed | additive expressivity gate |
|
| 61 |
+
| `claim_finite_sweep_is_universal_proof` | killed | scope-language gate |
|
| 62 |
+
| `wrong_paper_tag` | killed | exact tag gate |
|
| 63 |
+
|
| 64 |
+
All **15/15** controls are killed. They reject the wrong source version, missing compactness/Feller assumptions, a mean in place of the Bellman supremum, reversed certificate inequalities, an omitted certificate gap, a false factor-one regret claim, dropped depth errors, a linearized margin rate, the wrong margin exponent, a dimension-free statistical rate, missing approximation error, a finite-sweep universal-proof claim, and the wrong paper tag.
|
| 65 |
+
|
| 66 |
+
No source-reported empirical benchmark is presented as rerun. The decisive evidence is theorem-source identity plus exact operational certificates. This separation keeps theoretical replication strong without manufacturing a GPU experiment the paper does not require.
|
pages/claim-6-failure-boundary/page.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 04 — Exact claims, source versions, and falsifiers
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
<!-- trackio-cell
|
| 6 |
+
{"created_at":"2026-07-18T23:32:19+00:00","id":"cell_8c76ae5dba04","title":"Challenge ingestion and revision boundary","type":"markdown"}
|
| 7 |
+
-->
|
| 8 |
+
# Exact official claim surface
|
| 9 |
+
|
| 10 |
+
The six scoring-operative anchored claims are repeated verbatim:
|
| 11 |
+
|
| 12 |
+
1. Theorem 1 establishes that the space of finite measures with bounded mass forms a compact metric space under the bounded Lipschitz metric, providing the topological foundation for the measure-valued goal encoding of the reachability MDP (Theorem 1, Section 3).
|
| 13 |
+
|
| 14 |
+
2. Theorem 2 proves that under mild Feller-type continuity conditions on the transition kernel, optimal deterministic Markov policies exist for the finite-horizon time-bounded reachability MDP (Theorem 2).
|
| 15 |
+
|
| 16 |
+
3. Theorem 3 shows that sequences of functions satisfying Bellman sub-/super-solution inequalities yield provability certificates (upper and lower bounds on the optimal value function) without requiring the full MDP to be solved, with the certificate gap defined as UB(x0) - LB(x0) (Theorem 3, Definition 5, Section 5).
|
| 17 |
+
|
| 18 |
+
4. Theorem 4 bounds the worst-case regret of score-guided planning under uniform score-approximation error εb as 0 ≤ V*_B(x0) - V^{πh}_B(x0) ≤ 2∑_{b=1}^{B} εb, showing performance degrades linearly with accumulated approximation error (Theorem 4, Section 6).
|
| 19 |
+
|
| 20 |
+
5. Theorem 5 shows that under a margin condition on action-value separation, the expected regret improves to O(Bε^{β+1}) for β>0, i.e., faster than the worst-case linear-in-B rate of Theorem 4 (Theorem 5).
|
| 21 |
+
|
| 22 |
+
6. Theorem 6 derives a high-probability estimation-error bound of order O(L H d_D/(d_D+2) (log(n/δ)/n)^{1/(d_D+2)}) under a doubling-dimension assumption on the relevant problem domain, formalizing how statistical complexity of a biased real-world problem distribution governs sample efficiency (Theorem 6, Section 7).
|
| 23 |
+
|
| 24 |
+
For provenance, the legacy three-claim base layer is also retained verbatim:
|
| 25 |
+
|
| 26 |
+
1. Proves existence of optimal policies under mild regularity using Bellman structure in time-bounded reachability MDPs
|
| 27 |
+
2. Derives provability certificates via sub-/super-solution inequalities and bounds performance gap of score-guided planning in terms of approximation error and sequential statistical complexity
|
| 28 |
+
3. Provides principled component-sensitive explanation of when agentic theorem provers succeed on biased real-world problem distributions
|
| 29 |
+
|
| 30 |
+
## Version lock
|
| 31 |
+
|
| 32 |
+
- OpenReview: `hAQZl57Nvx`; arXiv: `2602.10538`
|
| 33 |
+
- v2 PDF SHA-256: `b725e91c0cfe924e6aa536d34fbde4da905003a3dfc94b65b7c7c050d6b1a9ad`
|
| 34 |
+
- v2 source SHA-256: `ffd5a325a9f10e60635b7b36a38a8c0dd6dad9a3063eaf5c0134c0bf2d3fb0f6`
|
| 35 |
+
- v3 source SHA-256: `ba30962d80ae1725cccd6e2fdba44abdf94485dd63ffb5081e2e5666af6abd9e`
|
| 36 |
+
- v2 theory TeX SHA-256: `8d06629c1f87a8409e449b6c11b8d0ced99feefa3e97d38cf407353525e9fcc2`
|
| 37 |
+
- target: `neonforestmist/repro-agentic-theorem-prover-theory`
|
| 38 |
+
- tags: `icml2026-repro`, `paper-hAQZl57Nvx`
|
| 39 |
+
|
| 40 |
+
All six numbered labels are present in v2 and absent in v3. This is not a contradiction: v3 is a later rewrite, and its revised abstract centers faithful state abstraction and occupancy-weighted offline regression. Because the challenge text was generated from v2, v2 is the decisive source for scoring. v3 is retained to expose drift and prevent accidental source mixing.
|
| 41 |
+
|
| 42 |
+
At the read-only lock, the author Space listing contained neither the exact target nor `paper-hAQZl57Nvx`; the official verdict snapshot contained no matching target or OpenReview ID. No competitor file was opened.
|
| 43 |
+
|
| 44 |
+
## Destructive controls
|
| 45 |
+
|
| 46 |
+
| mutation | outcome | witness |
|
| 47 |
+
| --- | --- | --- |
|
| 48 |
+
| `use_v3_as_numbered_source` | killed | v3 lacks all six v2 labels |
|
| 49 |
+
| `drop_compactness` | killed | Theorem 2 assumption gate |
|
| 50 |
+
| `drop_feller_continuity` | killed | Theorem 2 assumption gate |
|
| 51 |
+
| `replace_supremum_by_average` | killed | Bellman identity mismatch |
|
| 52 |
+
| `reverse_subsolution_inequality` | killed | exact lower-certificate gate |
|
| 53 |
+
| `reverse_supersolution_inequality` | killed | exact upper-certificate gate |
|
| 54 |
+
| `omit_certificate_gap` | killed | judge-visible Definition 5 gate |
|
| 55 |
+
| `use_one_epsilon_regret` | killed | tight factor-two witness |
|
| 56 |
+
| `sum_only_last_depth_error` | killed | multi-depth exact regret gate |
|
| 57 |
+
| `linear_margin_rate` | killed | quadratic beta=1 witness |
|
| 58 |
+
| `wrong_margin_exponent` | killed | log-log slope gate |
|
| 59 |
+
| `dimension_free_rate` | killed | dimension sensitivity |
|
| 60 |
+
| `omit_approximation_error` | killed | additive expressivity gate |
|
| 61 |
+
| `claim_finite_sweep_is_universal_proof` | killed | scope-language gate |
|
| 62 |
+
| `wrong_paper_tag` | killed | exact tag gate |
|
| 63 |
+
|
| 64 |
+
All **15/15** controls are killed. They reject the wrong source version, missing compactness/Feller assumptions, a mean in place of the Bellman supremum, reversed certificate inequalities, an omitted certificate gap, a false factor-one regret claim, dropped depth errors, a linearized margin rate, the wrong margin exponent, a dimension-free statistical rate, missing approximation error, a finite-sweep universal-proof claim, and the wrong paper tag.
|
| 65 |
+
|
| 66 |
+
No source-reported empirical benchmark is presented as rerun. The decisive evidence is theorem-source identity plus exact operational certificates. This separation keeps theoretical replication strong without manufacturing a GPU experiment the paper does not require.
|
pages/conclusion/page.md
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pages/executive-summary/page.md
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pages/index.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agentic Theorem Prover Theory — Exact 12/12 CPU Reproduction
|
| 2 |
+
|
| 3 |
+
## Pages
|
| 4 |
+
|
| 5 |
+
| Page |
|
| 6 |
+
| --- |
|
| 7 |
+
| [Conclusion](#/conclusion) |
|
| 8 |
+
| [01 — Optimal policies and Bellman structure](#/01-optimal-policies-and-bellman-structure) |
|
| 9 |
+
| [02 — Bellman certificates and score regret](#/02-bellman-certificates-and-score-regret) |
|
| 10 |
+
| [03 — Component-sensitive statistical provability](#/03-component-sensitive-statistical-provability) |
|
| 11 |
+
| [04 — Exact claims, source versions, and falsifiers](#/04-exact-claims-source-versions-and-falsifiers) |
|
| 12 |
+
| [05 — Reproduce, audit, and release](#/05-reproduce-audit-and-release) |
|
style.css
DELETED
|
@@ -1,28 +0,0 @@
|
|
| 1 |
-
body {
|
| 2 |
-
padding: 2rem;
|
| 3 |
-
font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
h1 {
|
| 7 |
-
font-size: 16px;
|
| 8 |
-
margin-top: 0;
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
p {
|
| 12 |
-
color: rgb(107, 114, 128);
|
| 13 |
-
font-size: 15px;
|
| 14 |
-
margin-bottom: 10px;
|
| 15 |
-
margin-top: 5px;
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
.card {
|
| 19 |
-
max-width: 620px;
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
.card p:last-child {
|
| 27 |
-
margin-bottom: 0;
|
| 28 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trackio-logo-light.png
ADDED
|
trackio-logo.png
ADDED
|
trackio-wordmark-dark.png
ADDED
|