fix: HTML 预览子页面改用 JS 切换,避免沙箱 blob 导航被拦截
上一版通过设置 location.hash 触发 CSS :target 切页,但沙箱 blob iframe 禁止导航到自身 blob URL,浏览器报 Not allowed to load local resource,且 :target 仅在真实片段导航时更新,注定失效。改为完全 不动 hash,用 JS 模拟 html-multipage 技能的 .page/.pages 显示隐藏 (内联 display 覆盖类级 CSS),普通目录锚点仍平滑滚动。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
21e53310f8
commit
2f414c6e08
1 file changed
+31
-21
@@ -102,14 +102,18 @@ function candidateTextFromHref(href: string): string {
|
||||
* Injects a small runtime script into generated HTML so that clicking an
|
||||
* in-page anchor (`<a href="#section">`) works inside the preview iframe.
|
||||
*
|
||||
* The hash MUST really change on click: multi-page documents (the
|
||||
* html-multipage skill) show/hide their sub-page `<section class="page">`
|
||||
* blocks with CSS `:target` rules, which only fire on a fragment navigation.
|
||||
* Intercepting with preventDefault + scrollIntoView alone leaves the hash
|
||||
* untouched, so sub-pages stay `display:none` and clicks look dead. The
|
||||
* handler therefore syncs `location.hash` (a same-document navigation —
|
||||
* safe in the sandboxed blob: frame) and only smooth-scrolls afterwards when
|
||||
* the target is actually rendered.
|
||||
* The preview loads from a sandboxed `blob:` URL (opaque origin). Such a frame
|
||||
* is FORBIDDEN from navigating to its own `blob:` URL, so changing the hash
|
||||
* throws "Not allowed to load local resource: blob:…#page-x". And because CSS
|
||||
* `:target` only updates on a real fragment navigation, the html-multipage
|
||||
* skill's `:target`-based sub-page switching can never fire here.
|
||||
*
|
||||
* So we never touch the hash. Instead the handler emulates the skill's
|
||||
* show/hide in JS: a "page" is a `.page` inside a `.pages` container; clicking
|
||||
* a link to one shows it and hides its siblings (mirroring the
|
||||
* `.page:target` + `~ #page-home` rules via inline `display`, which outranks
|
||||
* the skill's class-level CSS). Plain in-page anchors (TOC headings, etc.) just
|
||||
* smooth-scroll as before.
|
||||
*/
|
||||
export function injectInPageAnchorHandler(html: string): string {
|
||||
const script = `<script>(function(){
|
||||
@@ -119,6 +123,22 @@ export function injectInPageAnchorHandler(html: string): string {
|
||||
var esc=window.CSS&&CSS.escape?CSS.escape(id):id.replace(/["\\\\]/g,"\\\\$&");
|
||||
try{return document.querySelector("#"+esc+",[name=\\""+esc+"\\"]");}catch(e){return null;}
|
||||
}
|
||||
function find(raw){
|
||||
var id=decode(raw);
|
||||
return document.getElementById(id)||document.getElementById(raw)||
|
||||
document.getElementsByName(id)[0]||document.getElementsByName(raw)[0]||
|
||||
bySelector(id)||bySelector(raw);
|
||||
}
|
||||
function switchPage(target){
|
||||
var container=target.parentElement;
|
||||
while(container&&!(container.classList&&container.classList.contains("pages"))){container=container.parentElement;}
|
||||
var pages=(container||document).querySelectorAll(".page");
|
||||
if(!pages.length)return false;
|
||||
for(var i=0;i<pages.length;i++){pages[i].style.display="none";}
|
||||
target.style.display="block";
|
||||
if(window.scrollTo){window.scrollTo(0,0);}
|
||||
return true;
|
||||
}
|
||||
document.addEventListener("click",function(e){
|
||||
var el=e.target;
|
||||
while(el&&el.tagName!=="A"){el=el.parentElement;}
|
||||
@@ -126,20 +146,10 @@ export function injectInPageAnchorHandler(html: string): string {
|
||||
var href=el.getAttribute("href");
|
||||
if(!href||href.charAt(0)!=="#"||href.length<2)return;
|
||||
e.preventDefault();
|
||||
var raw=href.slice(1);
|
||||
var id=decode(raw);
|
||||
var target=
|
||||
document.getElementById(id)||
|
||||
document.getElementById(raw)||
|
||||
document.getElementsByName(id)[0]||
|
||||
document.getElementsByName(raw)[0]||
|
||||
bySelector(id)||
|
||||
bySelector(raw);
|
||||
try{if(location.hash!=="#"+raw){location.hash=raw;}}catch(err){}
|
||||
var target=find(href.slice(1));
|
||||
if(!target)return;
|
||||
requestAnimationFrame(function(){
|
||||
if(target.getClientRects().length){target.scrollIntoView({behavior:"smooth",block:"start"});}
|
||||
});
|
||||
if(target.classList&&target.classList.contains("page")&&switchPage(target))return;
|
||||
target.scrollIntoView({behavior:"smooth",block:"start"});
|
||||
},true);
|
||||
})();</script>`;
|
||||
if (/<\/body>/i.test(html)) {
|
||||
|
||||
Reference in new issue
Block a user