A model menu that stays on the phone
Inside a chat the picker sits mid-bar with the panel buttons to its right, and its menu opened from the picker's right edge -- at 390px it spanned x = -132..226, cutting every model's name off. Below 48rem the bar is now the containing block and the menu is pinned between its edges (capped at 24rem). Opening it on a touchscreen no longer focuses the filter, which raised the keyboard over half the list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,23 @@ for 1.0.0 have something to be assembled from.
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.8.3
|
||||
|
||||
The model picker on a phone, which could not be read once a chat was open.
|
||||
|
||||
- **The model menu no longer runs off the left of the screen.** Inside a chat
|
||||
the picker sits in the middle of the top bar, with the panel buttons to its
|
||||
right, and its menu opened from the picker's right edge — so on a phone most
|
||||
of it was off the screen and every model's name was cut off. On a narrow
|
||||
screen the menu now hangs from the bar itself, edge to edge, and every name is
|
||||
whole. Wider screens are unchanged.
|
||||
|
||||
- **Opening it on a touchscreen no longer raises the keyboard.** With more than
|
||||
eight models the menu has a filter box, and it took the focus on opening — so
|
||||
the keyboard came up and covered half the list you had opened it to choose
|
||||
from. On a touchscreen the chosen model takes the focus instead; the filter is
|
||||
one tap away. With a mouse, typing straight into the filter works as before.
|
||||
|
||||
## 1.8.2
|
||||
|
||||
The model lists, made readable. Both printed every capability switch as a tag —
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""LLeMbas - a Middle-earth themed web UI for OpenAI-compatible LLM endpoints."""
|
||||
|
||||
__version__ = "1.8.2"
|
||||
__version__ = "1.8.3"
|
||||
|
||||
@@ -1377,6 +1377,23 @@ body.is-resizing .canvas__body { pointer-events: none; }
|
||||
which nothing else on the screen tells you -- gets the room back. */
|
||||
.topbar__actions .picker__label { display: none; }
|
||||
|
||||
/* And the menu is the bar's, not the picker's. Anchored to the picker it
|
||||
opens `right: 0` of a button that sits mid-bar with the panel buttons to
|
||||
its right, so a 24rem menu ran off the left edge of a 390px phone and cut
|
||||
every name in half. Taking `position` off the picker makes the bar the
|
||||
containing block: the menu spans the bar under it, whatever sits where --
|
||||
up to its usual 24rem, held at the bar's right edge by the auto margin. */
|
||||
.topbar { position: relative; }
|
||||
.topbar__actions .picker { position: static; }
|
||||
.topbar__actions .picker__menu {
|
||||
left: max(var(--sp-2), var(--safe-left));
|
||||
right: max(var(--sp-2), var(--safe-right));
|
||||
width: auto;
|
||||
max-width: 24rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
.topbar__actions .picker__list { max-height: min(22rem, 60dvh); }
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
inset: 0 auto 0 0;
|
||||
|
||||
@@ -320,6 +320,11 @@
|
||||
if (filter) {
|
||||
filter.value = "";
|
||||
applyFilter(menu, "");
|
||||
}
|
||||
// Not on a touchscreen: focusing a text field there raises the keyboard,
|
||||
// which covers half the list the finger came to choose from. The filter
|
||||
// is one tap away for whoever wants it.
|
||||
if (filter && !window.matchMedia("(hover: none)").matches) {
|
||||
filter.focus();
|
||||
} else {
|
||||
var selected = menu.querySelector(".picker__option.is-selected") ||
|
||||
|
||||
@@ -208,3 +208,19 @@ def test_the_desktop_minimum_is_still_declared():
|
||||
for token in ("--terminal-width-min", "--canvas-width-min"):
|
||||
assert f"{token}:" in TOKENS
|
||||
assert f"var({token})" in APP_CSS
|
||||
|
||||
|
||||
def test_the_topbar_model_menu_belongs_to_the_bar_on_a_phone():
|
||||
"""1.8.3. Anchored to the picker, the menu opened `right: 0` of a button that
|
||||
sits mid-bar with the panel buttons to its right, so on a 390px phone a 24rem
|
||||
menu started 132px left of the screen and every model's name was cut off.
|
||||
|
||||
Below the phone breakpoint the picker gives up `position`, which makes the bar
|
||||
the containing block, and the menu is pinned between the bar's two edges."""
|
||||
body = _media_body(APP_CSS, "48rem")
|
||||
assert re.search(r"\.topbar\s*\{\s*position:\s*relative", body)
|
||||
assert re.search(r"\.topbar__actions \.picker\s*\{\s*position:\s*static", body)
|
||||
menu = re.search(r"\.topbar__actions \.picker__menu\s*\{([^}]*)\}", body)
|
||||
assert menu, "the topbar's menu is not placed on a phone"
|
||||
for declaration in ("left:", "right:", "width: auto"):
|
||||
assert declaration in menu.group(1), f"{declaration} missing from the phone menu"
|
||||
|
||||
@@ -111,3 +111,22 @@ def test_settings_lists_the_models_with_their_tags_below_the_name(client, models
|
||||
assert listing.count("#i-eye") == 1
|
||||
assert listing.count('class="model-list__more model-list__tags"') == 2
|
||||
assert "tool_fetch" in listing
|
||||
|
||||
|
||||
def test_opening_the_picker_on_a_touchscreen_does_not_raise_the_keyboard():
|
||||
"""1.8.3. With more than eight models the menu has a filter, and `open()`
|
||||
focused it -- which on a phone raises the keyboard over half the list the
|
||||
finger came to choose from. The focus is gated on `(hover: none)`, the same
|
||||
query the stylesheet uses for touch."""
|
||||
from pathlib import Path
|
||||
|
||||
import lembas
|
||||
|
||||
js = (Path(lembas.__file__).parent / "web/static/js/ui.js").read_text(encoding="utf-8")
|
||||
start = js.index("function open(picker)")
|
||||
body = js[start : js.index("function applyFilter", start)]
|
||||
assert "filter.focus()" in body, "the filter is no longer focused anywhere -- test is blind"
|
||||
gated = r'if \(filter && !window\.matchMedia\("\(hover: none\)"\)\.matches\)\s*\{'
|
||||
assert re.search(gated + r"\s*filter\.focus\(\)", body), (
|
||||
"the filter is focused on open without asking whether this is a touchscreen"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user