Merge pull request 'Page Projets : copie d'URL dans le menu ⋯ + refonte thème barre Fusion/Spark (#44, #45)' (#46) from menu-copie-url-44-45 into main
CI / test (push) Successful in 4m2s
CI / test (push) Successful in 4m2s
This commit was merged in pull request #46.
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
## 0.38.0 — 2026-10-28 — Page Projets : copie d'URL et refonte du menu ⋯
|
||||||
|
|
||||||
|
- **Copie d'URL (#44)** : chaque tuile projet (grille, y compris Épinglés) propose désormais « 🔗 Copier l'URL du projet » dans son menu ⋯ — l'URL Gitea du dépôt est copiée dans le presse-papiers, avec confirmation « URL du projet copiée » et repli (textarea + execCommand) quand l'API Clipboard n'est pas disponible.
|
||||||
|
- **Menu ⋯ (#45)** : refonte sur les jetons de la barre d'apps commune (draco-barre.css, prototype Console v7.2 / console Spark) — fond opaque, bordure, rayon 14 px, ombre portée, survol/focus visibles, séparateur avant l'action de copie. Corrige la cause du menu transparent : il utilisait `var(--surface-1)`, variable jamais définie.
|
||||||
|
- **Thèmes sombre et clair** : jetons clairs par défaut, variante sombre via `[data-theme="dark"]` sur `<html>` et repli `prefers-color-scheme`, comme la barre Fusion.
|
||||||
|
|
||||||
## 0.37.6 — 2026-10-28 — Corrections console/recherche : CORS console Spark
|
## 0.37.6 — 2026-10-28 — Corrections console/recherche : CORS console Spark
|
||||||
|
|
||||||
- **CORS recherche (PR #43)** : la recherche Kanbante autorise désormais la console Spark (agent prod+dev) en inter-origines — les pages console peuvent appeler la recherche de l'hôte sans erreur CORS/réseau.
|
- **CORS recherche (PR #43)** : la recherche Kanbante autorise désormais la console Spark (agent prod+dev) en inter-origines — les pages console peuvent appeler la recherche de l'hôte sans erreur CORS/réseau.
|
||||||
|
|||||||
+1
-1
@@ -177,7 +177,7 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
APP_VERSION = "0.37.6"
|
APP_VERSION = "0.38.0"
|
||||||
|
|
||||||
|
|
||||||
def _env_bool(name: str, default: bool) -> bool:
|
def _env_bool(name: str, default: bool) -> bool:
|
||||||
|
|||||||
@@ -109,6 +109,8 @@
|
|||||||
<a href="/sprint/{{ full }}">📖 Récit de sprint</a>
|
<a href="/sprint/{{ full }}">📖 Récit de sprint</a>
|
||||||
{% if repo.html_url %}
|
{% if repo.html_url %}
|
||||||
<a href="{{ repo.html_url }}" target="_blank" rel="noopener">🔗 Gitea ↗</a>
|
<a href="{{ repo.html_url }}" target="_blank" rel="noopener">🔗 Gitea ↗</a>
|
||||||
|
<div class="card-menu-sep" role="separator"></div>
|
||||||
|
<button type="button" class="card-menu-item" data-copy-url="{{ repo.html_url }}" title="Copier l'URL Gitea de {{ repo.name }}" aria-label="Copier l'URL du projet {{ repo.name }}">🔗 Copier l'URL du projet</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+48
-8
@@ -1486,20 +1486,60 @@ body.sidebar-collapsed .side-foot { justify-content: center; }
|
|||||||
padding: 2px 7px; border-radius: 8px; line-height: 1;
|
padding: 2px 7px; border-radius: 8px; line-height: 1;
|
||||||
}
|
}
|
||||||
.card-menu-btn:hover { background: var(--surface-2); color: var(--text); }
|
.card-menu-btn:hover { background: var(--surface-2); color: var(--text); }
|
||||||
|
.card-menu-btn[aria-expanded="true"] { background: var(--surface-2); color: var(--text); }
|
||||||
|
/* Menu ⋯ des tuiles projets — refonte issues #44/#45.
|
||||||
|
Jetons repris de la barre d'apps commune (draco-barre.css,
|
||||||
|
prototype validé Console v7.2, aussi utilisé par la console
|
||||||
|
Spark) : clair par défaut, sombre via [data-theme="dark"]
|
||||||
|
ou prefers-color-scheme. Correctif du bug d'origine : le
|
||||||
|
menu utilisait var(--surface-1), variable jamais définie,
|
||||||
|
donc aucun fond — le menu était transparent et illisible. */
|
||||||
.card-menu {
|
.card-menu {
|
||||||
|
--menu-bg: #ffffff; --menu-bg-2: #e9eae6; --menu-ink: #1b1d1a;
|
||||||
|
--menu-ink-2: #5c615a; --menu-line: #dedfd9; --menu-agent: #c2437e;
|
||||||
position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
|
position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
|
||||||
min-width: 210px; display: flex; flex-direction: column; gap: 2px;
|
min-width: 248px; display: flex; flex-direction: column; gap: 2px;
|
||||||
background: var(--surface-1); border: 1px solid var(--border);
|
background: var(--menu-bg); border: 1px solid var(--menu-line);
|
||||||
border-radius: 12px; padding: 6px;
|
border-radius: 14px; padding: 6px; color: var(--menu-ink);
|
||||||
box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 16px 44px rgb(0 0 0 / .28);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.card-menu {
|
||||||
|
--menu-bg: #1a1c1a; --menu-bg-2: #242624; --menu-ink: #eceee9;
|
||||||
|
--menu-ink-2: #a6aba2; --menu-line: #2e302c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .card-menu {
|
||||||
|
--menu-bg: #1a1c1a; --menu-bg-2: #242624; --menu-ink: #eceee9;
|
||||||
|
--menu-ink-2: #a6aba2; --menu-line: #2e302c;
|
||||||
}
|
}
|
||||||
.card-menu[hidden] { display: none !important; }
|
.card-menu[hidden] { display: none !important; }
|
||||||
.card-menu a {
|
.card-menu a, .card-menu-item {
|
||||||
display: flex; align-items: center; min-height: 32px;
|
display: flex; align-items: center; min-height: 32px;
|
||||||
padding: 8px 10px; border-radius: 8px;
|
padding: 8px 10px; border-radius: 10px;
|
||||||
color: var(--text-2); font-size: 0.92rem;
|
color: var(--menu-ink-2); font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
.card-menu-item {
|
||||||
|
width: 100%; box-sizing: border-box; border: 0;
|
||||||
|
background: transparent; font: inherit; text-align: left; cursor: pointer;
|
||||||
|
}
|
||||||
|
.card-menu a:hover, .card-menu a:focus-visible,
|
||||||
|
.card-menu-item:hover, .card-menu-item:focus-visible {
|
||||||
|
background: var(--menu-bg-2); color: var(--menu-ink); text-decoration: none;
|
||||||
|
}
|
||||||
|
.card-menu a:focus-visible, .card-menu-item:focus-visible {
|
||||||
|
outline: 2px solid var(--menu-agent); outline-offset: 1px;
|
||||||
|
}
|
||||||
|
.card-menu-sep { height: 1px; margin: 6px 10px; background: var(--menu-line); }
|
||||||
|
/* Confirmation de copie — pastille fixe, lisible sur fond
|
||||||
|
clair comme sombre, même patron que la barre (draco). */
|
||||||
|
.kanbante-toast {
|
||||||
|
position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
|
||||||
|
z-index: 3000; background: #1f1f1f; color: #eee;
|
||||||
|
padding: 10px 16px; border-radius: 10px;
|
||||||
|
font: 500 13.5px system-ui, "Segoe UI", sans-serif;
|
||||||
|
box-shadow: 0 6px 24px rgb(0 0 0 / .35);
|
||||||
}
|
}
|
||||||
.card-menu a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
|
|
||||||
.proj-table {
|
.proj-table {
|
||||||
width: calc(100% - 48px); margin: 0 24px 18px;
|
width: calc(100% - 48px); margin: 0 24px 18px;
|
||||||
border-collapse: collapse; font-size: 0.93rem;
|
border-collapse: collapse; font-size: 0.93rem;
|
||||||
|
|||||||
@@ -176,6 +176,47 @@
|
|||||||
if (e.key === 'Escape') closeMenus();
|
if (e.key === 'Escape') closeMenus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ── Copier l'URL du projet (#44) ─────────────────────── */
|
||||||
|
function showCopyToast(message) {
|
||||||
|
var toast = document.createElement('div');
|
||||||
|
toast.className = 'kanbante-toast';
|
||||||
|
toast.setAttribute('role', 'status');
|
||||||
|
toast.textContent = message;
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
setTimeout(function () { toast.remove(); }, 2600);
|
||||||
|
}
|
||||||
|
function copyTextFallback(text) {
|
||||||
|
var area = document.createElement('textarea');
|
||||||
|
area.value = text;
|
||||||
|
area.style.cssText = 'position:fixed;opacity:0;pointer-events:none;';
|
||||||
|
document.body.appendChild(area);
|
||||||
|
area.select();
|
||||||
|
var ok = false;
|
||||||
|
try { ok = document.execCommand('copy'); } catch (e) { ok = false; }
|
||||||
|
area.remove();
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
function copyText(text) {
|
||||||
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
|
return navigator.clipboard.writeText(text).then(
|
||||||
|
function () { return true; },
|
||||||
|
function () { return copyTextFallback(text); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve(copyTextFallback(text));
|
||||||
|
}
|
||||||
|
document.querySelectorAll('[data-copy-url]').forEach(function (btn) {
|
||||||
|
btn.addEventListener('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var url = btn.getAttribute('data-copy-url') || '';
|
||||||
|
if (!url) return;
|
||||||
|
copyText(url).then(function (ok) {
|
||||||
|
closeMenus();
|
||||||
|
showCopyToast(ok ? 'URL du projet copiée' : 'La copie a échoué');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/* Le tri des tableaux denses (#22) vit dans tablesort.js,
|
/* Le tri des tableaux denses (#22) vit dans tablesort.js,
|
||||||
partagé avec /forecast et /health (0.19.0). */
|
partagé avec /forecast et /health (0.19.0). */
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""Menu ⋯ des tuiles projets : copie d'URL (#44) et thème
|
||||||
|
opaque barre Fusion / Spark, sombre et clair (#45).
|
||||||
|
|
||||||
|
Le menu était transparent parce que son fond utilisait
|
||||||
|
var(--surface-1), variable jamais définie dans :root.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_card_menu_offers_copy_url(client, fake_gitea):
|
||||||
|
page = client.get("/")
|
||||||
|
assert "Copier l'URL du projet" in page.text
|
||||||
|
assert 'data-copy-url="http://gitea.test/Projets/demo"' in page.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_card_menu_theme_is_opaque_and_dual(client, fake_gitea):
|
||||||
|
css = client.get("/static/css/style.css").text
|
||||||
|
# Le menu ne repose plus sur la variable indéfinie.
|
||||||
|
menu = css.split(".card-menu {", 1)[1].split("}", 1)[0]
|
||||||
|
assert "var(--surface-1)" not in menu
|
||||||
|
assert "var(--menu-bg)" in menu
|
||||||
|
# Jetons barre Fusion / Console v7.2, clair par défaut…
|
||||||
|
assert "--menu-bg: #ffffff" in css
|
||||||
|
# …et variante sombre explicite, comme draco-barre.css.
|
||||||
|
assert '[data-theme="dark"] .card-menu' in css
|
||||||
|
assert "--menu-bg: #1a1c1a" in css
|
||||||
|
assert "prefers-color-scheme: dark" in css
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_js_copy_contract(client, fake_gitea):
|
||||||
|
js = client.get("/static/js/dashboard.js").text
|
||||||
|
assert "data-copy-url" in js
|
||||||
|
assert "navigator.clipboard" in js
|
||||||
|
assert "execCommand('copy')" in js
|
||||||
|
assert "URL du projet copiée" in js
|
||||||
Reference in New Issue
Block a user