|
8 | 8 | <meta name="generator" content="ARIS render-html (academic, v1)"> |
9 | 9 | <meta name="aris:source-path" content="docs/tutorials/3d_generation_tutorial.md"> |
10 | 10 | <meta name="aris:source-sha256" content="beab28036da8f28744b8d64815a669d0fce44c978bd869145e41b15860659ccc"> |
11 | | -<meta name="aris:generated-at" content="2026-05-27 15:19 UTC"> |
| 11 | +<meta name="aris:generated-at" content="2026-05-31 05:07 UTC"> |
12 | 12 |
|
13 | 13 | <!-- MathJax 3 --> |
14 | 14 | <script> |
|
462 | 462 | padding-left: 12px; |
463 | 463 | margin-left: -12px; |
464 | 464 | } |
| 465 | + |
| 466 | +/* --- Focus-dim reading mode (B1) + floating toggle (B2) + ↑/↓ nav (B4) ----- |
| 467 | + Blog opt-in. Everything is scoped under body.aris-blog(.aris-focus-dim) so |
| 468 | + the 46 non-blog tutorials are completely unaffected (no .aris-blog → button |
| 469 | + hidden, dim rule never matches). While ON, dims every <main> child except |
| 470 | + the current section and the intro (header.hero stays bright). F7's io2 is |
| 471 | + paused while ON; the scroll listener is bound only while ON. */ |
| 472 | +body.aris-blog.aris-focus-dim main > *:not(.aris-in-focus) { |
| 473 | + opacity: 0.28; |
| 474 | + transition: opacity 0.25s ease; |
| 475 | +} |
| 476 | +body.aris-blog.aris-focus-dim main > .aris-in-focus { opacity: 1; } |
| 477 | +.aris-focus-toggle { |
| 478 | + position: fixed; |
| 479 | + top: 16px; |
| 480 | + right: 16px; |
| 481 | + z-index: 1100; |
| 482 | + display: none; |
| 483 | + background: var(--bg); |
| 484 | + border: 1px solid var(--border); |
| 485 | + border-radius: 18px; |
| 486 | + padding: 6px 13px; |
| 487 | + font-family: inherit; |
| 488 | + font-size: 13px; |
| 489 | + color: var(--ink-muted); |
| 490 | + cursor: pointer; |
| 491 | + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06); |
| 492 | +} |
| 493 | +body.aris-blog .aris-focus-toggle { display: inline-block; } |
| 494 | +.aris-focus-toggle:hover { color: var(--primary); } |
| 495 | +.aris-focus-toggle.on { background: var(--accent); color: #fff; border-color: var(--accent); } |
| 496 | +.aris-focus-hint { |
| 497 | + position: fixed; |
| 498 | + bottom: 18px; |
| 499 | + right: 16px; |
| 500 | + z-index: 1100; |
| 501 | + background: rgba(0, 0, 0, 0.78); |
| 502 | + color: #fff; |
| 503 | + font-size: 12px; |
| 504 | + padding: 6px 12px; |
| 505 | + border-radius: 6px; |
| 506 | + opacity: 0; |
| 507 | + transition: opacity 0.3s ease; |
| 508 | + pointer-events: none; |
| 509 | +} |
| 510 | +.aris-focus-hint.show { opacity: 1; } |
| 511 | +@media print { |
| 512 | + .aris-focus-toggle, .aris-focus-hint { display: none !important; } |
| 513 | + body.aris-focus-dim main > * { opacity: 1 !important; } |
| 514 | +} |
465 | 515 | </style> |
466 | 516 | </head> |
467 | 517 | <body class=""> |
@@ -580,7 +630,7 @@ <h1>3D Generation 面试 Cheat Sheet</h1> |
580 | 630 | <div class="meta"> |
581 | 631 | <span><strong>Source:</strong> <code>docs/tutorials/3d_generation_tutorial.md</code></span> |
582 | 632 | <span><strong>SHA256:</strong> <code>beab28036da8</code></span> |
583 | | - <span><strong>Rendered:</strong> 2026-05-27 15:19 UTC</span> |
| 633 | + <span><strong>Rendered:</strong> 2026-05-31 05:07 UTC</span> |
584 | 634 |
|
585 | 635 | </div> |
586 | 636 | </header> |
@@ -1155,7 +1205,7 @@ <h3 id="a3-embodied-ai--ar--vr-常见追问">A.3 Embodied AI / AR / VR 常见 |
1155 | 1205 | Generated by <a href="https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md">ARIS <code>/render-html</code></a> · |
1156 | 1206 | source path <code>docs/tutorials/3d_generation_tutorial.md</code> · |
1157 | 1207 | SHA256 <code>beab28036da8</code> · |
1158 | | - generated at 2026-05-27 15:19 UTC. |
| 1208 | + generated at 2026-05-31 05:07 UTC. |
1159 | 1209 | This is a generated view — edit the source Markdown, then re-render. |
1160 | 1210 | </footer> |
1161 | 1211 | </main> |
@@ -1366,6 +1416,122 @@ <h3 id="a3-embodied-ai--ar--vr-常见追问">A.3 Embodied AI / AR / VR 常见 |
1366 | 1416 | }, { rootMargin: "-15% 0px -75% 0px" }); |
1367 | 1417 | h2s.forEach(function (h) { io2.observe(h); }); |
1368 | 1418 | } |
| 1419 | + |
| 1420 | + // --- Feature 8: Focus-dim reading mode (B1) + floating toggle (B2) + |
| 1421 | + // ↑/↓ section nav (B4). Built only inside this .aris-blog guard, so |
| 1422 | + // non-blog tutorials never construct it. Default OFF. While ON: |
| 1423 | + // dims non-current sections (h3-granular), keeps the intro bright, |
| 1424 | + // pauses F7's io2, and binds a scroll listener (removed on OFF — no |
| 1425 | + // new *persistent* global hook). Restores everything on OFF / print. |
| 1426 | + var mainEl = document.querySelector("main"); |
| 1427 | + if (mainEl) { |
| 1428 | + var focusBtn = document.createElement("button"); |
| 1429 | + focusBtn.className = "aris-focus-toggle"; |
| 1430 | + focusBtn.type = "button"; |
| 1431 | + focusBtn.setAttribute("aria-pressed", "false"); |
| 1432 | + focusBtn.textContent = "🎯 Focus"; |
| 1433 | + document.body.appendChild(focusBtn); |
| 1434 | + |
| 1435 | + var focusHint = document.createElement("div"); |
| 1436 | + focusHint.className = "aris-focus-hint"; |
| 1437 | + document.body.appendChild(focusHint); |
| 1438 | + var hintTimer = null; |
| 1439 | + var showFocusHint = function (msg) { |
| 1440 | + focusHint.textContent = msg; |
| 1441 | + focusHint.classList.add("show"); |
| 1442 | + if (hintTimer) clearTimeout(hintTimer); |
| 1443 | + hintTimer = setTimeout(function () { focusHint.classList.remove("show"); }, 1600); |
| 1444 | + }; |
| 1445 | + |
| 1446 | + var focusKids = function () { return [].slice.call(mainEl.children); }; |
| 1447 | + var headerIdxList = function (kids) { |
| 1448 | + var idxs = []; |
| 1449 | + kids.forEach(function (el, i) { |
| 1450 | + if (el.tagName === "H2" || el.tagName === "H3") idxs.push(i); |
| 1451 | + }); |
| 1452 | + return idxs; |
| 1453 | + }; |
| 1454 | + // Deterministic current-section pick (no IntersectionObserver): the |
| 1455 | + // header whose top is closest to but above the viewport's 40% line; |
| 1456 | + // fallback = first header still below the line; then first header. |
| 1457 | + var activeHeaderIdx = function (kids, headerIdxs) { |
| 1458 | + var best = -1, bestTop = -Infinity, lineY = window.innerHeight * 0.4; |
| 1459 | + headerIdxs.forEach(function (idx) { |
| 1460 | + var top = kids[idx].getBoundingClientRect().top; |
| 1461 | + if (top < lineY && top > bestTop) { bestTop = top; best = idx; } |
| 1462 | + }); |
| 1463 | + if (best < 0) { |
| 1464 | + for (var i = 0; i < headerIdxs.length; i++) { |
| 1465 | + if (kids[headerIdxs[i]].getBoundingClientRect().top >= 0) { best = headerIdxs[i]; break; } |
| 1466 | + } |
| 1467 | + if (best < 0) best = headerIdxs[0]; |
| 1468 | + } |
| 1469 | + return best; |
| 1470 | + }; |
| 1471 | + var updateFocus = function () { |
| 1472 | + var kids = focusKids(); |
| 1473 | + var headerIdxs = headerIdxList(kids); |
| 1474 | + if (!headerIdxs.length) { // no h2/h3 → keep all bright (never dim a headerless page) |
| 1475 | + kids.forEach(function (el) { el.classList.add("aris-in-focus"); }); |
| 1476 | + return; |
| 1477 | + } |
| 1478 | + var best = activeHeaderIdx(kids, headerIdxs); |
| 1479 | + var next = kids.length; |
| 1480 | + for (var j = 0; j < headerIdxs.length; j++) { |
| 1481 | + if (headerIdxs[j] > best) { next = headerIdxs[j]; break; } |
| 1482 | + } |
| 1483 | + var introEnd = headerIdxs[0]; // header.hero + anything before first heading stays bright |
| 1484 | + kids.forEach(function (el, i) { |
| 1485 | + el.classList.toggle("aris-in-focus", (i < introEnd) || (i >= best && i < next)); |
| 1486 | + }); |
| 1487 | + }; |
| 1488 | + var onFocusScroll = function () { |
| 1489 | + if (document.body.classList.contains("aris-focus-dim")) updateFocus(); |
| 1490 | + }; |
| 1491 | + var onFocusKeydown = function (e) { |
| 1492 | + var tg = e.target; |
| 1493 | + if (tg && (tg.tagName === "INPUT" || tg.tagName === "TEXTAREA" || tg.tagName === "SELECT" || tg.isContentEditable)) return; |
| 1494 | + if (e.key === "Escape") { |
| 1495 | + var popEl = document.getElementById("cite-pop"); |
| 1496 | + if (popEl && !popEl.hidden) return; // an open popover handles Esc first |
| 1497 | + setFocus(false); |
| 1498 | + return; |
| 1499 | + } |
| 1500 | + if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return; |
| 1501 | + e.preventDefault(); |
| 1502 | + var heads = [].slice.call(document.querySelectorAll("main h2, main h3")); |
| 1503 | + if (!heads.length) return; |
| 1504 | + var cur = 0, bestDist = Infinity; |
| 1505 | + heads.forEach(function (h, i) { |
| 1506 | + var d = Math.abs(h.getBoundingClientRect().top - 80); |
| 1507 | + if (d < bestDist) { bestDist = d; cur = i; } |
| 1508 | + }); |
| 1509 | + var ni = e.key === "ArrowDown" ? Math.min(cur + 1, heads.length - 1) : Math.max(cur - 1, 0); |
| 1510 | + heads[ni].scrollIntoView({ behavior: "smooth", block: "start" }); |
| 1511 | + showFocusHint((ni + 1) + " / " + heads.length + " " + heads[ni].textContent.trim().slice(0, 24)); |
| 1512 | + }; |
| 1513 | + var setFocus = function (on) { |
| 1514 | + document.body.classList.toggle("aris-focus-dim", on); |
| 1515 | + focusBtn.classList.toggle("on", on); |
| 1516 | + focusBtn.setAttribute("aria-pressed", on ? "true" : "false"); |
| 1517 | + focusBtn.textContent = on ? "🎯 Focus ON" : "🎯 Focus"; |
| 1518 | + if (on) { |
| 1519 | + window.addEventListener("scroll", onFocusScroll, { passive: true }); |
| 1520 | + document.addEventListener("keydown", onFocusKeydown, true); // capture phase: see an open popover before its bubble Esc handler (also: bound only while ON, guardrail 2) |
| 1521 | + if (typeof io2 !== "undefined" && io2) io2.disconnect(); // pause F7 |
| 1522 | + updateFocus(); |
| 1523 | + showFocusHint("Focus 模式:↑ / ↓ 切换小节,Esc 退出"); |
| 1524 | + } else { |
| 1525 | + window.removeEventListener("scroll", onFocusScroll); |
| 1526 | + document.removeEventListener("keydown", onFocusKeydown, true); |
| 1527 | + focusKids().forEach(function (el) { el.classList.remove("aris-in-focus"); }); |
| 1528 | + if (typeof io2 !== "undefined" && io2) h2s.forEach(function (h) { io2.observe(h); }); // resume F7 |
| 1529 | + } |
| 1530 | + }; |
| 1531 | + focusBtn.addEventListener("click", function () { |
| 1532 | + setFocus(!document.body.classList.contains("aris-focus-dim")); |
| 1533 | + }); |
| 1534 | + } |
1369 | 1535 | } |
1370 | 1536 | })(); |
1371 | 1537 | </script> |
|
0 commit comments