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>...
|
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
|
- **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
|
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;
|
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)}
|
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)}
|
.item.blurred .reveal:hover{background:var(--aus-blue);color:var(--fg-on-accent)}
|
||||||
.blurtoggle{display:inline;margin:0}
|
/* Legible on purpose. v1 was a bare muted glyph with no border and no label,
|
||||||
.blurtoggle button{font:inherit;font-size:.78rem;line-height:1;padding:0 .2rem;
|
and the operator's reaction to it was "no UI option to blur/unblur?" — a
|
||||||
cursor:pointer;border:0;background:none;color:var(--muted)}
|
control nobody can find is a control that is not there. */
|
||||||
.blurtoggle button:hover{color:var(--fg)}
|
.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. */
|
/* Cover thumbs on the index inherit the blur so the front page cannot undo it. */
|
||||||
.blurred-thumb{filter:blur(16px)}
|
.blurred-thumb{filter:blur(16px)}
|
||||||
/* opaque dark control-scrim + always-light glyph — legible over any thumbnail
|
/* opaque dark control-scrim + always-light glyph — legible over any thumbnail
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
{% extends "base.html" %}
|
{% 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 title %}{{ name }} · The Booth{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="boothhead">
|
<div class="boothhead">
|
||||||
@@ -109,6 +123,7 @@
|
|||||||
<span class="doc-spacer"></span>
|
<span class="doc-spacer"></span>
|
||||||
<a class="doc-act" href="view?f={{ it.url }}" title="open full page">⤢</a>
|
<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>
|
<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>
|
<button type="button" class="doc-act doc-close" title="close (hide for now)" aria-label="close">✕</button>
|
||||||
</summary>
|
</summary>
|
||||||
{% if it.rendered_html %}
|
{% if it.rendered_html %}
|
||||||
@@ -142,17 +157,17 @@
|
|||||||
<a class="dl" href="{{ it.url }}" download>⬇ {{ it.name }}</a>
|
<a class="dl" href="{{ it.url }}" download>⬇ {{ it.name }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if it.kind == 'other' %}
|
{% 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 %}
|
{% else %}
|
||||||
<figcaption>
|
<figcaption>
|
||||||
<a class="dl-link" href="{{ it.url }}" download title="download {{ it.name }}">⬇</a>
|
<a class="dl-link" href="{{ it.url }}" download title="download {{ it.name }}">⬇</a>
|
||||||
<span class="cap-text">{{ it.caption or it.name }}</span>
|
<span class="cap-text">{{ it.caption or it.name }}</span>
|
||||||
<form class="blurtoggle" method="post" action="/b/{{ name_url }}/blur">
|
{{ blurtoggle(name_url, it) }}
|
||||||
<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>
|
|
||||||
</figcaption>
|
</figcaption>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</figure>
|
</figure>
|
||||||
@@ -200,6 +215,10 @@
|
|||||||
"collapse". Collapse stays available via the rest of the summary bar. With
|
"collapse". Collapse stays available via the rest of the summary bar. With
|
||||||
JS off the button is inert and collapse via <details> still works. */
|
JS off the button is inert and collapse via <details> still works. */
|
||||||
(function () {
|
(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) {
|
document.querySelectorAll('.doc-close').forEach(function (btn) {
|
||||||
btn.addEventListener('click', function (ev) {
|
btn.addEventListener('click', function (ev) {
|
||||||
ev.preventDefault();
|
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, (
|
assert body.count('<figure class="item item-doc blurred"') == 1, (
|
||||||
"exactly the blurred doc, not every doc"
|
"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