fix(booth): put the blur toggle on every item kind, and make it look like a control
The operator asked "no UI option to blur/unblur?" and was right twice over. MISSING ENTIRELY ON TWO OF THREE BRANCHES. booth.html renders docs, media and everything-else through separate <figure> blocks. The toggle went into the media branch only, so inline docs -- the branch that puts readable text straight on the page, the one that needs blur most -- had no control at all, and `other` files only got a caption row if they happened to carry a caption. This is the SECOND time this feature shipped having patched some branches and not others; the blurred class itself had the same gap one commit ago. So the toggle is now a single Jinja macro called from all three sites, which makes "patched two of three" impossible rather than merely unlikely, and test_every_item_kind_gets_exactly_one_blur_toggle counts toggles against figures across mixed kinds so a fourth branch cannot quietly skip it. INVISIBLE WHERE IT DID RENDER. v1 was a bare `◌` at 0.78rem in --muted with no border, no label and no hover affordance. It now reads `◌ blur` / `◉ blurred` with a border, matching the other per-item controls. A control nobody can find is a control that is not there. Docs get it in the doc bar beside ⤢ ⬇ ✕, with stopPropagation so submitting it does not collapse the <details> it lives inside. Verified live on all three kinds: 3 figures, 3 toggles, and the POST round trip blurs and un-blurs. 167 tests pass.
This commit is contained in:
@@ -52,7 +52,17 @@ booth blur <name> <file>... # hide from a glance
|
||||
booth unblur <name> <file>...
|
||||
```
|
||||
|
||||
Or the ◌ / ◉ toggle in each item's caption row on the booth page.
|
||||
Or the **`◌ blur` / `◉ blurred`** button on every item in the booth page —
|
||||
in the caption row for images, video, audio and plain files, and in the doc bar
|
||||
beside ⤢ ⬇ ✕ for inline docs.
|
||||
|
||||
⚠ The toggle is emitted by ONE Jinja macro (`blurtoggle`) called from all three
|
||||
item branches. booth.html renders docs, media and everything-else through
|
||||
separate `<figure>` blocks, and this feature was twice shipped having patched
|
||||
only some of them — first the blur class, then the toggle itself. Add a fourth
|
||||
branch and you must call the macro from it;
|
||||
`test_every_item_kind_gets_exactly_one_blur_toggle` counts toggles against
|
||||
figures across mixed kinds and will fail if you don't.
|
||||
|
||||
- **State** is `.blurred` in the booth dir — one booth-relative item path per
|
||||
line, the same filesystem-is-the-state idiom as `.pins` and `.forever`. An
|
||||
|
||||
@@ -270,10 +270,17 @@
|
||||
font:inherit;font-size:.72rem;line-height:1;padding:.24rem .5rem;cursor:pointer;
|
||||
border:1px solid var(--line);border-radius:.3rem;background:var(--bg);color:var(--fg)}
|
||||
.item.blurred .reveal:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||||
.blurtoggle{display:inline;margin:0}
|
||||
.blurtoggle button{font:inherit;font-size:.78rem;line-height:1;padding:0 .2rem;
|
||||
cursor:pointer;border:0;background:none;color:var(--muted)}
|
||||
.blurtoggle button:hover{color:var(--fg)}
|
||||
/* Legible on purpose. v1 was a bare muted glyph with no border and no label,
|
||||
and the operator's reaction to it was "no UI option to blur/unblur?" — a
|
||||
control nobody can find is a control that is not there. */
|
||||
.blurtoggle{display:inline-block;margin:0}
|
||||
.blurtoggle button{font:inherit;font-size:.7rem;line-height:1;padding:.2rem .4rem;
|
||||
cursor:pointer;border:1px solid var(--line);border-radius:.3rem;
|
||||
background:var(--bg);color:var(--fg);white-space:nowrap}
|
||||
.blurtoggle button:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||||
/* In the doc bar it sits beside ⤢ ⬇ ✕ and should not out-shout them. */
|
||||
.doc-bar .blurtoggle{margin-left:.35rem}
|
||||
.doc-bar .blurtoggle button{font-size:.66rem;padding:.14rem .34rem}
|
||||
/* Cover thumbs on the index inherit the blur so the front page cannot undo it. */
|
||||
.blurred-thumb{filter:blur(16px)}
|
||||
/* opaque dark control-scrim + always-light glyph — legible over any thumbnail
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
{# The blur toggle, defined ONCE. There are three item branches in this file
|
||||
(doc / media / other) and the first cut of this feature patched only one of
|
||||
them, so docs rendered with no control at all. A macro makes "patched two of
|
||||
three" impossible rather than merely unlikely. #}
|
||||
{% macro blurtoggle(name_url, it, cls='') -%}
|
||||
<form class="blurtoggle {{ cls }}" method="post" action="/b/{{ name_url }}/blur">
|
||||
<input type="hidden" name="f" value="{{ it.name }}">
|
||||
<input type="hidden" name="on" value="{{ '0' if it.blurred else '1' }}">
|
||||
<button title="{{ 'un-blur this item' if it.blurred else 'blur this item — cosmetic only, the file is still served' }}"
|
||||
aria-label="{{ 'un-blur' if it.blurred else 'blur' }} {{ it.name }}"
|
||||
>{{ '◉ blurred' if it.blurred else '◌ blur' }}</button>
|
||||
</form>
|
||||
{%- endmacro %}
|
||||
|
||||
{% block title %}{{ name }} · The Booth{% endblock %}
|
||||
{% block content %}
|
||||
<div class="boothhead">
|
||||
@@ -109,6 +123,7 @@
|
||||
<span class="doc-spacer"></span>
|
||||
<a class="doc-act" href="view?f={{ it.url }}" title="open full page">⤢</a>
|
||||
<a class="doc-act" href="{{ it.url }}" download title="download {{ it.name }}">⬇</a>
|
||||
{{ blurtoggle(name_url, it, 'doc-act') }}
|
||||
<button type="button" class="doc-act doc-close" title="close (hide for now)" aria-label="close">✕</button>
|
||||
</summary>
|
||||
{% if it.rendered_html %}
|
||||
@@ -142,17 +157,17 @@
|
||||
<a class="dl" href="{{ it.url }}" download>⬇ {{ it.name }}</a>
|
||||
{% endif %}
|
||||
{% if it.kind == 'other' %}
|
||||
{% if it.caption %}<figcaption><span class="cap-text">{{ it.caption }}</span></figcaption>{% endif %}
|
||||
{# Always render the caption row now: it carries the blur toggle, and
|
||||
"no caption" is not a reason to deny an item its controls. #}
|
||||
<figcaption>
|
||||
{% if it.caption %}<span class="cap-text">{{ it.caption }}</span>{% endif %}
|
||||
{{ blurtoggle(name_url, it) }}
|
||||
</figcaption>
|
||||
{% else %}
|
||||
<figcaption>
|
||||
<a class="dl-link" href="{{ it.url }}" download title="download {{ it.name }}">⬇</a>
|
||||
<span class="cap-text">{{ it.caption or it.name }}</span>
|
||||
<form class="blurtoggle" method="post" action="/b/{{ name_url }}/blur">
|
||||
<input type="hidden" name="f" value="{{ it.name }}">
|
||||
<input type="hidden" name="on" value="{{ '0' if it.blurred else '1' }}">
|
||||
<button title="{{ 'un-blur this item' if it.blurred else 'blur this item (cosmetic only — the file is still served)' }}"
|
||||
aria-label="{{ 'un-blur' if it.blurred else 'blur' }} {{ it.name }}">{{ '◉' if it.blurred else '◌' }}</button>
|
||||
</form>
|
||||
{{ blurtoggle(name_url, it) }}
|
||||
</figcaption>
|
||||
{% endif %}
|
||||
</figure>
|
||||
@@ -200,6 +215,10 @@
|
||||
"collapse". Collapse stays available via the rest of the summary bar. With
|
||||
JS off the button is inert and collapse via <details> still works. */
|
||||
(function () {
|
||||
/* A form inside <summary> would otherwise collapse the doc on submit. */
|
||||
document.querySelectorAll('.doc-bar .blurtoggle').forEach(function (f) {
|
||||
f.addEventListener('click', function (ev) { ev.stopPropagation(); });
|
||||
});
|
||||
document.querySelectorAll('.doc-close').forEach(function (btn) {
|
||||
btn.addEventListener('click', function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -1447,3 +1447,43 @@ def test_blur_applies_to_inline_docs_not_just_images(client):
|
||||
assert body.count('<figure class="item item-doc blurred"') == 1, (
|
||||
"exactly the blurred doc, not every doc"
|
||||
)
|
||||
|
||||
|
||||
def test_every_item_kind_gets_exactly_one_blur_toggle(client):
|
||||
"""The regression guard for this whole feature's recurring bug.
|
||||
|
||||
booth.html has THREE item branches — doc, media, other — and each pass at
|
||||
this feature patched some and missed others: first the blurred class landed
|
||||
on media only, then the toggle landed on media only and the operator asked
|
||||
"no UI option to blur/unblur?". A count assertion across mixed kinds is the
|
||||
check that actually catches it; a spot check on one item never will.
|
||||
"""
|
||||
c, root = client
|
||||
d = root / "bo"
|
||||
d.mkdir()
|
||||
(d / "note.txt").write_text("doc branch") # -> item-doc
|
||||
_png(d / "pic.png") # -> item-image
|
||||
(d / "blob.bin").write_bytes(b"\x00\x01binary") # -> item-other
|
||||
|
||||
body = c.get("/b/bo/").text
|
||||
figures = body.count('<figure class="item item-')
|
||||
toggles = body.count('class="blurtoggle')
|
||||
assert figures == 3, f"expected all three kinds to render, got {figures}"
|
||||
assert toggles == figures, (
|
||||
f"{figures} items but {toggles} blur toggles — a branch was missed again"
|
||||
)
|
||||
|
||||
|
||||
def test_blur_toggle_posts_the_opposite_state(client):
|
||||
"""The button must flip, not just set. A toggle hard-coded to on=1 looks
|
||||
identical in the markup and silently cannot un-blur."""
|
||||
c, root = client
|
||||
d = root / "bo"
|
||||
d.mkdir()
|
||||
_png(d / "pic.png")
|
||||
|
||||
assert 'name="on" value="1"' in c.get("/b/bo/").text
|
||||
set_blurred(d, "pic.png", True)
|
||||
body = c.get("/b/bo/").text
|
||||
assert 'name="on" value="0"' in body, "a blurred item must offer un-blur"
|
||||
assert "◉ blurred" in body
|
||||
|
||||
Reference in New Issue
Block a user