fix: page Configurations inutilisable en mode mobile BUG-071
This commit is contained in:
@@ -1486,6 +1486,18 @@
|
||||
<div class="editor-modal" id="config-modal">
|
||||
<div class="editor-container">
|
||||
<div class="editor-header">
|
||||
<button
|
||||
class="help-hamburger"
|
||||
id="config-hamburger"
|
||||
data-i18n-attr="title:config.toc_toggle;aria-label:config.toc_toggle"
|
||||
title="Afficher le sommaire"
|
||||
aria-label="Afficher le sommaire"
|
||||
>
|
||||
<i
|
||||
data-lucide="menu"
|
||||
style="width: 18px; height: 18px"
|
||||
></i>
|
||||
</button>
|
||||
<div class="editor-title" data-i18n="header.menu_config">Configurations</div>
|
||||
<div class="editor-actions">
|
||||
<button
|
||||
|
||||
@@ -767,6 +767,10 @@ function initConfigModal() {
|
||||
openBtn.addEventListener("click", async () => {
|
||||
modal.classList.add("active");
|
||||
closeHeaderMenu();
|
||||
// BUG-071: reset the TOC to the CSS default (mobile: hidden, desktop:
|
||||
// visible) like the help modal does on open.
|
||||
var configNavOnOpen = document.getElementById("config-nav");
|
||||
if (configNavOnOpen) configNavOnOpen.style.display = '';
|
||||
renderConfigFilters();
|
||||
loadConfigFields();
|
||||
loadDiagnostics();
|
||||
@@ -886,6 +890,44 @@ function initConfigModal() {
|
||||
});
|
||||
}
|
||||
|
||||
// BUG-071: mobile table of contents. #config-nav shares the .help-nav
|
||||
// rule that hides it below 768px, but — unlike the help modal — the config
|
||||
// modal had no toggle to reveal it, leaving mobile users with no way to
|
||||
// reach a section. The header hamburger opens it as a top block; picking
|
||||
// a section smooth-scrolls inside the modal and collapses it on mobile.
|
||||
var configNav = document.getElementById("config-nav");
|
||||
var configHamburger = document.getElementById("config-hamburger");
|
||||
function _isConfigMobile() { return window.innerWidth <= 768; }
|
||||
function _setConfigNav(open) {
|
||||
if (!configNav) return;
|
||||
configNav.style.display = open ? "flex" : "none";
|
||||
if (configHamburger) configHamburger.classList.toggle("active", !!open);
|
||||
}
|
||||
if (configHamburger) {
|
||||
configHamburger.addEventListener("click", function(e) {
|
||||
e.stopPropagation();
|
||||
var hidden = !configNav || configNav.style.display === "none" || configNav.style.display === "";
|
||||
_setConfigNav(hidden);
|
||||
});
|
||||
}
|
||||
if (configNav) {
|
||||
configNav.querySelectorAll(".help-nav-link").forEach(function(a) {
|
||||
a.addEventListener("click", function(e) {
|
||||
var hash = a.getAttribute("href");
|
||||
if (!hash || hash.charAt(0) !== "#") return;
|
||||
var target = document.getElementById(hash.slice(1));
|
||||
if (!target) return;
|
||||
e.preventDefault();
|
||||
configNav.querySelectorAll(".help-nav-link").forEach(function(o) { o.classList.remove("active"); });
|
||||
a.classList.add("active");
|
||||
if (typeof target.scrollIntoView === "function") {
|
||||
target.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
if (_isConfigMobile()) _setConfigNav(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape" && modal.classList.contains("active")) {
|
||||
closeConfigModal();
|
||||
|
||||
+13
-6
@@ -5,6 +5,7 @@
|
||||
* Static DOM: data-i18n="key" → textContent
|
||||
* data-i18n-placeholder="key" → placeholder
|
||||
* data-i18n-attr:title="key" → title attribute
|
||||
* data-i18n-attr="a:k1;b:k2" → several attributes (";"-separated)
|
||||
* data-i18n-html="key" → innerHTML (use sparingly)
|
||||
* Dynamic JS: import { t } from './i18n.js'; t('key', {param: 'val'})
|
||||
* Live reload: setLocale('en') updates every data-i18n element instantly.
|
||||
@@ -183,13 +184,19 @@ function _applyDOM() {
|
||||
el.innerHTML = t(key);
|
||||
});
|
||||
|
||||
// data-i18n-attr:TITLE → sets any attribute
|
||||
// data-i18n-attr:ATTR:key[;ATTR:key…] → sets any attribute(s).
|
||||
// Single-pair form (data-i18n-attr="title:key") is preserved; multiple
|
||||
// pairs are separated with ";" (BUG-071: the config TOC toggle needs both
|
||||
// title and aria-label translated).
|
||||
document.querySelectorAll('[data-i18n-attr]').forEach(function (el) {
|
||||
const raw = el.getAttribute('data-i18n-attr');
|
||||
const colon = raw.indexOf(':');
|
||||
if (colon === -1) return;
|
||||
const attr = raw.substring(0, colon);
|
||||
const key = raw.substring(colon + 1);
|
||||
el.setAttribute(attr, t(key));
|
||||
raw.split(';').forEach(function (pair) {
|
||||
const colon = pair.indexOf(':');
|
||||
if (colon === -1) return;
|
||||
const attr = pair.substring(0, colon).trim();
|
||||
const key = pair.substring(colon + 1).trim();
|
||||
if (!attr || !key) return;
|
||||
el.setAttribute(attr, t(key));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -569,6 +569,7 @@
|
||||
"config.test": "Test",
|
||||
"config.timeout_label": "Search timeout (ms)",
|
||||
"config.title": "Settings",
|
||||
"config.toc_toggle": "Show contents",
|
||||
"config.title_boost": "Title boost",
|
||||
"config.title_boost_hint": "Relevance multiplier for title matches",
|
||||
"config.title_boost_label": "Title boost",
|
||||
|
||||
@@ -569,6 +569,7 @@
|
||||
"config.test": "Tester",
|
||||
"config.timeout_label": "Timeout recherche (ms)",
|
||||
"config.title": "Configuration",
|
||||
"config.toc_toggle": "Afficher le sommaire",
|
||||
"config.title_boost": "Boost titre",
|
||||
"config.title_boost_hint": "Multiplicateur de pertinence pour les correspondances dans le titre",
|
||||
"config.title_boost_label": "Boost titre",
|
||||
|
||||
@@ -4723,6 +4723,82 @@ body.resizing-v {
|
||||
}
|
||||
}
|
||||
|
||||
/* BUG-071: Configurations modal — mobile usability (viewport ≤ 768px).
|
||||
#config-nav shares the .help-nav rule that hides it, but the config modal
|
||||
had no toggle (unlike the help modal): the header hamburger
|
||||
(#config-hamburger, same .help-hamburger treatment) reveals it as a
|
||||
collapsible top block. Two-column grids and fixed-width add-rows are
|
||||
stacked/wrapped so nothing overflows a 360px viewport. */
|
||||
@media (max-width: 768px) {
|
||||
/* TOC as a collapsible top block (JS toggles inline display flex/none,
|
||||
which wins over the hiding rule); the list scrolls within a capped nav. */
|
||||
#config-modal #config-nav {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
max-height: 46vh;
|
||||
}
|
||||
/* Two-column grids → single column. */
|
||||
#config-modal .ai-default-grid,
|
||||
#config-modal .ai-provider-fields {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
/* Add-rows (tokens, webhooks, tag filters) wrap instead of overflowing. */
|
||||
#config-modal .config-add-row,
|
||||
#config-modal .config-add-pattern {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#config-modal .config-add-row .config-input,
|
||||
#config-modal .config-add-pattern .config-input,
|
||||
#config-modal .config-add-row .config-select {
|
||||
flex: 1 1 140px;
|
||||
width: auto !important; /* override fixed inline widths (180/140/100px) */
|
||||
min-width: 0;
|
||||
}
|
||||
#config-modal .config-add-row .config-btn-add,
|
||||
#config-modal .config-add-pattern .config-btn-add {
|
||||
flex: 1 1 auto;
|
||||
min-height: 44px;
|
||||
}
|
||||
/* Webhook / token / share rows wrap; long URLs and meta take their own
|
||||
line instead of squeezing the delete control off-screen. */
|
||||
#config-modal .webhook-item,
|
||||
#config-modal .token-item,
|
||||
#config-modal .share-item {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#config-modal .webhook-url,
|
||||
#config-modal .token-meta,
|
||||
#config-modal .share-url {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
#config-modal .webhook-delete,
|
||||
#config-modal .token-delete,
|
||||
#config-modal .share-revoke {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
}
|
||||
/* Sticky AI-keys footer: full-width touch-friendly buttons. */
|
||||
#config-modal .ai-keys-footer .config-btn-save,
|
||||
#config-modal .ai-keys-footer .config-btn-secondary {
|
||||
flex: 1 1 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
/* Long inline code in tips (ex. MCP usage snippet) must wrap. */
|
||||
#config-modal .qh-tip {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#config-modal .qh-tip span {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Toast notifications --- */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
|
||||
Reference in New Issue
Block a user