commit 7512a6704a29547c578bbde96477bc9c2cf9e869 Spenser Truex <truex@equwal.com> 2026-09-24 00:48:19 -0700 bm-html: remove the live preview Most sites do not let a page of another site show them in a frame: they send X-Frame-Options or a frame-ancestors policy. YouTube is one of them. For these sites the frame of the preview stayed empty, so the preview did not work for most bookmarks. bm-html is again the page of 0.5: a filter box, and the complete list without script. The fzf preview of bm in a terminal stays: it shows the text of the page, which bm-page gets with curl. The test "bm-html shows no page in a frame" replaces the test of the preview pane.
README | 10 +++------- bm-html | 49 +++++-------------------------------------------- test/run.sh | 7 ++++--- 3 files changed, 12 insertions(+), 54 deletions(-)
diff --git a/README b/README index 2fb3302..054c19e 100755 --- a/README +++ b/README @@ -152,13 +152,9 @@ bookmark file itself stays a plain list. One self-contained file: no server side, nothing loaded from elsewhere. Bookmarks are listed under each of their tags, with the tags linked at the -top. Inline script adds a filter box and, in a wide window, a live preview: -the page of the highlighted bookmark shows on the right. The arrow keys and -the mouse move the highlight, and Enter opens it. The preview loads a page -only when you type or point, in a sandboxed frame without a referrer; some -sites do not let other pages show them. Without script the page is still the -complete list, and Ctrl-F works. page.html?q=words starts out filtered, so -the page can be a search keyword in your browser, or in bm: +top. A dozen lines of inline script add a filter box; without script the page +is still the complete list, and Ctrl-F works. page.html?q=words starts out +filtered, so the page can be a search keyword in your browser, or in bm: b | https://example.org/bookmarks.html?q=%s diff --git a/bm-html b/bm-html index 73887d2..4031c5f 100755 --- a/bm-html +++ b/bm-html @@ -8,11 +8,10 @@ # Without a file the default bookmark file is read; "-" is stdin. # # The page lists the bookmarks under each of their tags, with links to the -# tags at the top. A few lines of script add a filter box and, in a wide -# window, a live preview: the page of the highlighted bookmark. Without -# script the page is still the complete list. page.html?q=words starts out -# filtered, so the page can serve as a search engine keyword in a browser, or -# in bm's own engines file: b | https://example.org/bookmarks.html?q=%s +# tags at the top. A few lines of script add a filter box; without script the +# page is still the complete list. page.html?q=words starts out filtered, so +# the page can serve as a search engine keyword in a browser, or in bm's own +# engines file: b | https://example.org/bookmarks.html?q=%s usage () { printf 'usage: bm-html [-t title] [file | -] ...\n' @@ -80,18 +79,9 @@ END { print "body{max-width:50em;margin:1em auto;padding:0 1em;font:1em/1.5 sans-serif}" print "#q{width:100%;box-sizing:border-box;font:inherit;padding:.3em}" print "ul{padding-left:1.2em}small,.t{color:#777;font-size:.85em}.t{margin-left:.3em}" - print "li.sel{background:#e3eef3;box-shadow:0 0 0 .2em #e3eef3}" - print "#preview{display:none}#preview p{margin:0 0 .3em;color:#777;font-size:.85em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}" - print "#preview iframe{flex:1;width:100%;box-sizing:border-box;border:1px solid #ccc;background:#fff}" - print "@media(min-width:80em){body:has(#preview:not([hidden])){margin-left:1em}" - print "#preview:not([hidden]){display:flex;flex-direction:column;position:fixed;top:1em;bottom:1em;left:54em;right:1em}}" print "</style>" print "<h1>" t "</h1>" print "<input id=q type=search placeholder=\"filter " total " bookmarks\" hidden>" - # The live preview: the page of the highlighted bookmark, on the right - # of a wide window. The script shows it. - print "<aside id=preview hidden><p>Type in the filter, or point at a bookmark, to see its page here.</p>" - print "<iframe title=\"Preview of the page\" sandbox=\"allow-scripts allow-same-origin\" referrerpolicy=no-referrer></iframe></aside>" printf "<p>" for (i = 1; i <= count; i++) if (names[i] != "") @@ -110,35 +100,6 @@ END { print "var t=(l.textContent+\" \"+l.firstChild.href).toLowerCase();" print "var ok=w.every(function(x){return t.indexOf(x)>=0});l.hidden=!ok;if(ok)n++});" print "s.hidden=!n})}" - # The live preview, as on sbm Sync: the arrow keys and the mouse move the - # highlight, Enter opens it, and the page loads when the highlight stays - # on it for 300 ms. On an https page an http bookmark loads as https, - # because a secure page cannot show an insecure frame. A page in the - # frame cannot keep the focus: the typing stays in the filter. - print "var pane=document.getElementById(\"preview\"),frame=pane.querySelector(\"iframe\"),cap=pane.querySelector(\"p\");" - print "var wide=matchMedia(\"(min-width:80em)\"),sel=0,started=false,timer=0,framed=null,pointer=\"\";" - print "function rows(){return Array.prototype.filter.call(document.querySelectorAll(\"li\"),function(l){return !l.hidden})}" - print "function address(l){var u;try{u=new URL(l.firstChild.href)}catch(e){return null}" - print "if(u.protocol!==\"http:\"&&u.protocol!==\"https:\")return null;" - print "if(location.protocol===\"https:\")u.protocol=\"https:\";return u.href}" - print "function load(to){if(to===framed)return;framed=to;frame.src=to||\"about:blank\"}" - print "function show(l){clearTimeout(timer);if(!wide.matches)return;var to=l?address(l):null;" - print "cap.textContent=!l?\"No bookmark to preview.\":to||\"No preview: this bookmark is not a web page.\";" - print "if(to)timer=setTimeout(function(){load(to)},300);else load(null)}" - print "function light(i){var r=rows();sel=Math.max(0,Math.min(r.length-1,i));" - print "document.querySelectorAll(\"li.sel\").forEach(function(l){l.className=\"\"});" - print "if(r[sel])r[sel].className=\"sel\";if(started)show(r[sel])}" - print "function point(e){var l=e.target.closest&&e.target.closest(\"li\");if(!l)return;started=true;light(rows().indexOf(l))}" - print "q.oninput=function(){started=true;f();light(0)};" - print "q.onkeydown=function(e){var r=rows();" - print "if(e.key===\"ArrowDown\"||e.key===\"ArrowUp\"){e.preventDefault();started=true;" - print "light(sel+(e.key===\"ArrowDown\"?1:-1));if(rows()[sel])rows()[sel].scrollIntoView({block:\"nearest\"})}" - print "else if(e.key===\"Enter\"&&r[sel]){if(e.ctrlKey||e.metaKey)open(r[sel].firstChild.href,\"_blank\",\"noopener\");" - print "else location.assign(r[sel].firstChild.href)}};" - print "document.addEventListener(\"mousemove\",function(e){var at=e.screenX+\",\"+e.screenY;if(at===pointer)return;pointer=at;point(e)});" - print "document.addEventListener(\"focusin\",point);" - print "wide.addEventListener(\"change\",function(){if(wide.matches)light(sel);else load(null)});" - print "addEventListener(\"blur\",function(){setTimeout(function(){if(document.activeElement===frame)q.focus({preventScroll:true})})});" - print "q.value=new URLSearchParams(location.search).get(\"q\")||\"\";started=q.value!==\"\";f();pane.hidden=false;light(0);" + print "q.oninput=f;q.value=new URLSearchParams(location.search).get(\"q\")||\"\";f();" print "</script>" }' diff --git a/test/run.sh b/test/run.sh index 3755398..5a5dd34 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1022,9 +1022,10 @@ eq 'bm-html has one inline script, a filter box hidden without it, and ?q=' \ "$(printf '%s\n' "$page" | grep -c '<script>') $(printf '%s\n' "$page" | grep -c '<input id=q type=search placeholder="filter 3 bookmarks" hidden>') $(printf '%s\n' "$page" | grep -c 'URLSearchParams(location.search).get("q")')" \ '1 1 1' eq 'bm-html loads nothing from elsewhere' "$(printf '%s\n' "$page" | grep -c -e ' src=' -e '<link' -e '@import')" '0' -eq 'bm-html has a preview pane that only its script shows, with a sandboxed frame and no referrer' \ - "$(printf '%s\n' "$page" | grep -c '^<aside id=preview hidden>') $(printf '%s\n' "$page" | grep -c '<iframe title="Preview of the page" sandbox="allow-scripts allow-same-origin" referrerpolicy=no-referrer></iframe></aside>') $(printf '%s\n' "$page" | grep -c 'pane.hidden=false')" \ - '1 1 1' +# Most sites do not let a page of another site show them in a frame +# (X-Frame-Options or frame-ancestors), YouTube among them, so a preview in +# a frame stays empty for them. +eq 'bm-html shows no page in a frame' "$(printf '%s\n' "$page" | grep -ci 'iframe')" '0' eq 'bm-html is a filter' "$($BM -t lib -l | $HTML - | grep -c '^<li>')" '2' eq 'bm-html reads the bookmark file by default, whatever stdin is' \ "$($HTML </dev/null | grep -c '^<li>')" '4'