Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80852374a8 |
+18
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
|||||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||||
> [Unreleased](#unreleased). La dernière version livrée est **2.19.0**.
|
> [Unreleased](#unreleased). La dernière version livrée est **2.19.1**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,6 +14,23 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.19.1] — 2026-09-23
|
||||||
|
|
||||||
|
### Corrigé
|
||||||
|
|
||||||
|
- **#110 — lisibilité et positionnement du lecteur média** : en mode mobile, la
|
||||||
|
barre audio passe en grille (barre de progression pleine largeur sur sa propre
|
||||||
|
ligne) et les actions secondaires (précédent/suivant/agrandir/volume) sont
|
||||||
|
masquées pour éviter tout chevauchement ; le volume est aussi masqué sur les
|
||||||
|
écrans intermédiaires en desktop, et `overflow: hidden` empêche le débordement
|
||||||
|
hors de la pilule. La mini-fenêtre vidéo n'est plus ré-aimantée sur les bords :
|
||||||
|
elle se déplace et se redimensionne **librement** (position absolue mémorisée,
|
||||||
|
centre autorisé), le glisser fonctionnant désormais depuis n'importe quel point
|
||||||
|
de la fenêtre (boutons/curseurs/poignée exclus, seuil de 4 px pour préserver
|
||||||
|
les contrôles natifs de la vidéo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.19.0] — 2026-09-23
|
## [2.19.0] — 2026-09-23
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -966,8 +966,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.19.0).
|
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.19.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Projet : ObsiGate | Version : 2.19.0 | Dernière mise à jour : Septembre 2026*
|
*Projet : ObsiGate | Version : 2.19.1 | Dernière mise à jour : Septembre 2026*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -1142,8 +1142,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.19.0).
|
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.19.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Project: ObsiGate | Version: 2.19.0 | Last updated: September 2026*
|
*Project: ObsiGate | Version: 2.19.1 | Last updated: September 2026*
|
||||||
|
|||||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.19.0"
|
version = "2.19.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.19.0"
|
version = "2.19.1"
|
||||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
authors = ["Bruno Charest"]
|
authors = ["Bruno Charest"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
"productName": "ObsiGate",
|
"productName": "ObsiGate",
|
||||||
"version": "2.19.0",
|
"version": "2.19.1",
|
||||||
"identifier": "com.obsigate.desktop",
|
"identifier": "com.obsigate.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend",
|
"frontendDist": "../frontend",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# ObsiGate — Roadmap
|
# ObsiGate — Roadmap
|
||||||
|
|
||||||
> **Version :** 2.19.0 | **Dernière mise à jour :** 2026-09-23
|
> **Version :** 2.19.1 | **Dernière mise à jour :** 2026-09-23
|
||||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||||
> vers les fonctionnalités livrées.
|
> vers les fonctionnalités livrées.
|
||||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||||
|
|||||||
@@ -134,12 +134,15 @@ global `window.NowPlaying`.
|
|||||||
- **Panneau étendu** : carte centrale (bottom-sheet sur mobile) avec artwork,
|
- **Panneau étendu** : carte centrale (bottom-sheet sur mobile) avec artwork,
|
||||||
scrub large, volume, vitesse 0,5–2×, précédent/suivant et actions
|
scrub large, volume, vitesse 0,5–2×, précédent/suivant et actions
|
||||||
ouvrir/télécharger/fermer.
|
ouvrir/télécharger/fermer.
|
||||||
- **Mini-vidéo flottante** (`.np-dock--video`) : déplaçable (aimantation au coin
|
- **Mini-vidéo flottante** (`.np-dock--video`) : déplaçable **librement** depuis
|
||||||
le plus proche) et redimensionnable, géométrie persistée ; sur mobile elle se
|
n'importe quel point de la fenêtre (position absolue mémorisée, centre autorisé
|
||||||
fixe au-dessus de la barre d'outils.
|
— aucune aimantation aux bords) et redimensionnable, géométrie persistée ; sur
|
||||||
|
mobile elle se fixe au-dessus de la barre d'outils.
|
||||||
- **Mobile** : mini-player au-dessus de la barre 64 px
|
- **Mobile** : mini-player au-dessus de la barre 64 px
|
||||||
(`bottom: calc(64px + env(safe-area-inset-bottom))`), `viewport-fit=cover`
|
(`bottom: calc(64px + env(safe-area-inset-bottom))`), `viewport-fit=cover`
|
||||||
ajouté, et `body.np-active` ajoute le décalage du contenu.
|
ajouté, et `body.np-active` ajoute le décalage du contenu. La barre audio passe
|
||||||
|
en grille (progression sur sa propre ligne) et masque les actions secondaires
|
||||||
|
pour éviter tout chevauchement.
|
||||||
|
|
||||||
### Comportements
|
### Comportements
|
||||||
|
|
||||||
|
|||||||
+67
-47
@@ -365,9 +365,13 @@ function buildAudioDock() {
|
|||||||
art.addEventListener('click', (e) => { stopEvent(e); toggleExpanded(); });
|
art.addEventListener('click', (e) => { stopEvent(e); toggleExpanded(); });
|
||||||
dock.appendChild(art);
|
dock.appendChild(art);
|
||||||
|
|
||||||
const info = npEl('div', { class: 'np-dock-info' });
|
const info = npEl('div', { class: 'np-dock-info', title: t('player.expand'), role: 'button', tabindex: '0' });
|
||||||
info.appendChild(npEl('span', { class: 'np-dock-title' }));
|
info.appendChild(npEl('span', { class: 'np-dock-title' }));
|
||||||
info.appendChild(npEl('span', { class: 'np-dock-sub' }));
|
info.appendChild(npEl('span', { class: 'np-dock-sub' }));
|
||||||
|
info.addEventListener('click', (e) => { stopEvent(e); toggleExpanded(); });
|
||||||
|
info.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleExpanded(); }
|
||||||
|
});
|
||||||
dock.appendChild(info);
|
dock.appendChild(info);
|
||||||
|
|
||||||
const controls = npEl('div', { class: 'np-dock-controls' });
|
const controls = npEl('div', { class: 'np-dock-controls' });
|
||||||
@@ -425,7 +429,7 @@ function buildVideoDock() {
|
|||||||
dock.appendChild(frame);
|
dock.appendChild(frame);
|
||||||
const resize = npEl('div', { class: 'np-video-resize', title: t('player.resize'), 'aria-hidden': 'true' });
|
const resize = npEl('div', { class: 'np-video-resize', title: t('player.resize'), 'aria-hidden': 'true' });
|
||||||
dock.appendChild(resize);
|
dock.appendChild(resize);
|
||||||
bindVideoDrag(dock, grip);
|
bindVideoDrag(dock);
|
||||||
bindVideoResize(dock, resize);
|
bindVideoResize(dock, resize);
|
||||||
return dock;
|
return dock;
|
||||||
}
|
}
|
||||||
@@ -833,75 +837,91 @@ function restore() {
|
|||||||
notify(t('player.resume'), 'info');
|
notify(t('player.resume'), 'info');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Video dock drag & resize ──────────────────────────────────────────────────
|
// ── Video dock drag & resize (free positioning anywhere in the viewport) ──────
|
||||||
const VIDEO_MIN = { w: 240, h: 150 };
|
const VIDEO_MIN = { w: 240, h: 150 };
|
||||||
|
const VIDEO_DEFAULT = { w: 360, h: 220 };
|
||||||
|
|
||||||
|
function clampToViewport(left, top, w, h) {
|
||||||
|
const margin = 8;
|
||||||
|
const maxLeft = Math.max(margin, window.innerWidth - w - margin);
|
||||||
|
const maxTop = Math.max(margin, window.innerHeight - h - margin);
|
||||||
|
return {
|
||||||
|
left: Math.min(Math.max(margin, left), maxLeft),
|
||||||
|
top: Math.min(Math.max(margin, top), maxTop),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function applyVideoGeometry() {
|
function applyVideoGeometry() {
|
||||||
if (!S.dock || S.dockKind !== 'video') return;
|
if (!S.dock || S.dockKind !== 'video') return;
|
||||||
const pos = readPosition();
|
if (isMobile()) {
|
||||||
const mobile = isMobile();
|
|
||||||
if (mobile) {
|
|
||||||
S.dock.style.removeProperty('left');
|
S.dock.style.removeProperty('left');
|
||||||
S.dock.style.removeProperty('top');
|
S.dock.style.removeProperty('top');
|
||||||
S.dock.style.removeProperty('width');
|
S.dock.style.removeProperty('width');
|
||||||
S.dock.style.removeProperty('height');
|
S.dock.style.removeProperty('height');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const w = pos && pos.w ? pos.w : 360;
|
const pos = readPosition() || {};
|
||||||
const h = pos && pos.h ? pos.h : 220;
|
const w = pos.w || VIDEO_DEFAULT.w;
|
||||||
|
const h = pos.h || VIDEO_DEFAULT.h;
|
||||||
S.dock.style.width = w + 'px';
|
S.dock.style.width = w + 'px';
|
||||||
S.dock.style.height = h + 'px';
|
S.dock.style.height = h + 'px';
|
||||||
const vw = window.innerWidth, vh = window.innerHeight;
|
|
||||||
const margin = 16;
|
const margin = 16;
|
||||||
const corner = (pos && pos.corner) || 'br';
|
const left = typeof pos.left === 'number' ? pos.left : window.innerWidth - w - margin;
|
||||||
let left, top;
|
const top = typeof pos.top === 'number' ? pos.top : window.innerHeight - h - 24;
|
||||||
if (corner.includes('l')) left = margin; else left = vw - w - margin;
|
const c = clampToViewport(left, top, w, h);
|
||||||
if (corner.includes('t')) top = margin + 56; else top = vh - h - 24;
|
S.dock.style.left = c.left + 'px';
|
||||||
S.dock.style.left = Math.max(margin, left) + 'px';
|
S.dock.style.top = c.top + 'px';
|
||||||
S.dock.style.top = Math.max(margin, top) + 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nearestCorner(left, top, w, h) {
|
// Drag from anywhere on the mini-window (buttons, sliders and the resize handle
|
||||||
const midX = left + w / 2, midY = top + h / 2;
|
// excluded). A small movement threshold keeps clicks on the native video
|
||||||
const vw = window.innerWidth, vh = window.innerHeight;
|
// controls working; no pointer capture is used so those controls keep working.
|
||||||
return `${midY < vh / 2 ? 't' : 'b'}${midX < vw / 2 ? 'l' : 'r'}`;
|
function bindVideoDrag(dock) {
|
||||||
}
|
dock.addEventListener('pointerdown', (e) => {
|
||||||
|
if (isMobile() || e.button !== 0) return;
|
||||||
function bindVideoDrag(dock, grip) {
|
if (e.target.closest('button, input, select, textarea, a, .np-video-resize')) return;
|
||||||
if (!grip) return;
|
|
||||||
grip.style.cursor = 'grab';
|
|
||||||
grip.addEventListener('pointerdown', (e) => {
|
|
||||||
if (isMobile()) return;
|
|
||||||
e.preventDefault();
|
|
||||||
const rect = dock.getBoundingClientRect();
|
const rect = dock.getBoundingClientRect();
|
||||||
S._drag = { dx: e.clientX - rect.left, dy: e.clientY - rect.top, w: rect.width, h: rect.height };
|
S._drag = {
|
||||||
grip.setPointerCapture?.(e.pointerId);
|
dx: e.clientX - rect.left,
|
||||||
grip.style.cursor = 'grabbing';
|
dy: e.clientY - rect.top,
|
||||||
|
startX: e.clientX,
|
||||||
|
startY: e.clientY,
|
||||||
|
moved: false,
|
||||||
|
};
|
||||||
document.addEventListener('pointermove', onDragMove);
|
document.addEventListener('pointermove', onDragMove);
|
||||||
document.addEventListener('pointerup', onDragEnd);
|
document.addEventListener('pointerup', onDragEnd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragMove(e) {
|
function onDragMove(e) {
|
||||||
if (!S._drag || !S.dock) return;
|
if (!S._drag || S._drag.resize || !S.dock) return;
|
||||||
const margin = 8;
|
if (!S._drag.moved) {
|
||||||
|
if (Math.abs(e.clientX - S._drag.startX) < 4 && Math.abs(e.clientY - S._drag.startY) < 4) return;
|
||||||
|
S._drag.moved = true;
|
||||||
|
S.dock.classList.add('np-dragging');
|
||||||
|
}
|
||||||
const w = S.dock.offsetWidth, h = S.dock.offsetHeight;
|
const w = S.dock.offsetWidth, h = S.dock.offsetHeight;
|
||||||
let left = e.clientX - S._drag.dx;
|
const c = clampToViewport(e.clientX - S._drag.dx, e.clientY - S._drag.dy, w, h);
|
||||||
let top = e.clientY - S._drag.dy;
|
S.dock.style.left = c.left + 'px';
|
||||||
left = Math.max(margin, Math.min(window.innerWidth - w - margin, left));
|
S.dock.style.top = c.top + 'px';
|
||||||
top = Math.max(margin, Math.min(window.innerHeight - h - margin, top));
|
|
||||||
S.dock.style.left = left + 'px';
|
|
||||||
S.dock.style.top = top + 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragEnd(e) {
|
function onDragEnd() {
|
||||||
document.removeEventListener('pointermove', onDragMove);
|
document.removeEventListener('pointermove', onDragMove);
|
||||||
document.removeEventListener('pointerup', onDragEnd);
|
document.removeEventListener('pointerup', onDragEnd);
|
||||||
if (!S.dock) return;
|
if (!S.dock) { S._drag = null; return; }
|
||||||
const r = S.dock.getBoundingClientRect();
|
S.dock.classList.remove('np-dragging');
|
||||||
const corner = nearestCorner(r.left, r.top, r.width, r.height);
|
if (S._drag && S._drag.moved) {
|
||||||
writePosition({ corner, w: Math.round(r.width), h: Math.round(r.height) });
|
const r = S.dock.getBoundingClientRect();
|
||||||
applyVideoGeometry();
|
const pos = readPosition() || {};
|
||||||
void e;
|
writePosition({
|
||||||
|
...pos,
|
||||||
|
left: Math.round(r.left),
|
||||||
|
top: Math.round(r.top),
|
||||||
|
w: Math.round(r.width),
|
||||||
|
h: Math.round(r.height),
|
||||||
|
});
|
||||||
|
}
|
||||||
S._drag = null;
|
S._drag = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -932,8 +952,8 @@ function onResizeEnd() {
|
|||||||
document.removeEventListener('pointerup', onResizeEnd);
|
document.removeEventListener('pointerup', onResizeEnd);
|
||||||
if (S.dock) {
|
if (S.dock) {
|
||||||
const r = S.dock.getBoundingClientRect();
|
const r = S.dock.getBoundingClientRect();
|
||||||
const pos = readPosition() || {};
|
const c = clampToViewport(r.left, r.top, r.width, r.height);
|
||||||
writePosition({ ...pos, w: Math.round(r.width), h: Math.round(r.height) });
|
writePosition({ left: Math.round(c.left), top: Math.round(c.top), w: Math.round(r.width), h: Math.round(r.height) });
|
||||||
applyVideoGeometry();
|
applyVideoGeometry();
|
||||||
}
|
}
|
||||||
S._drag = null;
|
S._drag = null;
|
||||||
|
|||||||
+43
-6
@@ -9913,6 +9913,7 @@ body.desktop-mode .editor-container {
|
|||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.np-dock--audio .np-media {
|
.np-dock--audio .np-media {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -9931,11 +9932,12 @@ body.desktop-mode .editor-container {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.np-dock-info {
|
.np-dock-info {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 120px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.np-dock-title {
|
.np-dock-title {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
@@ -9958,8 +9960,8 @@ body.desktop-mode .editor-container {
|
|||||||
gap: 2px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
.np-dock-progress {
|
.np-dock-progress {
|
||||||
flex: 1 1 180px;
|
flex: 1 1 160px;
|
||||||
min-width: 120px;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -10209,6 +10211,13 @@ body.desktop-mode .editor-container {
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mid-size screens: drop the volume slider before the row gets cramped. */
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.np-dock--audio .np-volume {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.np-dock--audio {
|
.np-dock--audio {
|
||||||
left: 8px;
|
left: 8px;
|
||||||
@@ -10216,11 +10225,39 @@ body.desktop-mode .editor-container {
|
|||||||
bottom: calc(64px + env(safe-area-inset-bottom, 0px) + 8px);
|
bottom: calc(64px + env(safe-area-inset-bottom, 0px) + 8px);
|
||||||
transform: none;
|
transform: none;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"progress progress progress progress"
|
||||||
|
"art info controls actions";
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px 8px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
.np-dock-progress {
|
.np-dock--audio .np-dock-art {
|
||||||
flex-basis: 90px;
|
grid-area: art;
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
}
|
}
|
||||||
.np-volume {
|
.np-dock--audio .np-dock-info {
|
||||||
|
grid-area: info;
|
||||||
|
}
|
||||||
|
.np-dock--audio .np-dock-controls {
|
||||||
|
grid-area: controls;
|
||||||
|
}
|
||||||
|
.np-dock--audio .np-dock-progress {
|
||||||
|
grid-area: progress;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.np-dock--audio .np-dock-actions {
|
||||||
|
grid-area: actions;
|
||||||
|
}
|
||||||
|
/* Compact mobile: keep play / return / close; expand via the artwork. */
|
||||||
|
.np-dock--audio [data-np="prev"],
|
||||||
|
.np-dock--audio [data-np="next"],
|
||||||
|
.np-dock--audio [data-np="expand"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.np-dock--video {
|
.np-dock--video {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsigate",
|
"name": "obsigate",
|
||||||
"version": "2.19.0",
|
"version": "2.19.1",
|
||||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||||
"main": "patch.js",
|
"main": "patch.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
@@ -134,4 +134,30 @@ test.describe('Now Playing — lecture persistante (#110)', () => {
|
|||||||
await expect(mini).toBeVisible({ timeout: 10000 });
|
await expect(mini).toBeVisible({ timeout: 10000 });
|
||||||
await expect(mini.locator('video.np-media--video')).toBeVisible();
|
await expect(mini.locator('video.np-media--video')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('la mini-fenêtre vidéo peut être déplacée librement (centre)', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
|
await openFile(page, 'TestVault', 'sample-video.webm');
|
||||||
|
await expect(page.locator('#content-area video.np-media--video')).toBeVisible({ timeout: 10000 });
|
||||||
|
await openFile(page, 'TestVault', 'note1.md');
|
||||||
|
|
||||||
|
const mini = page.locator('#now-playing-host .np-dock--video');
|
||||||
|
await expect(mini).toBeVisible({ timeout: 10000 });
|
||||||
|
const before = await mini.evaluate((el) => ({ left: parseFloat(el.style.left), top: parseFloat(el.style.top) }));
|
||||||
|
|
||||||
|
const box = await mini.boundingBox();
|
||||||
|
await page.mouse.move(box.x + box.width / 2, box.y + 12);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(640, 360, { steps: 12 });
|
||||||
|
await page.mouse.up();
|
||||||
|
|
||||||
|
const after = await mini.evaluate((el) => ({ left: parseFloat(el.style.left), top: parseFloat(el.style.top) }));
|
||||||
|
expect(after.left).not.toBe(before.left);
|
||||||
|
expect(after.top).not.toBe(before.top);
|
||||||
|
// Doit pouvoir rester au centre (pas de re-aimantation sur les bords).
|
||||||
|
expect(after.left).toBeGreaterThan(50);
|
||||||
|
expect(after.left).toBeLessThan(900);
|
||||||
|
expect(after.top).toBeGreaterThan(20);
|
||||||
|
expect(after.top).toBeLessThan(600);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ test("now-playing exposes the public controller API and is initialised at boot",
|
|||||||
assert.match(app, /initNowPlaying\(\)/);
|
assert.match(app, /initNowPlaying\(\)/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("video mini-window is freely positioned (no corner snapping)", () => {
|
||||||
|
assert.doesNotMatch(np, /nearestCorner/);
|
||||||
|
assert.match(np, /function clampToViewport/);
|
||||||
|
assert.match(np, /left: Math\.round\(r\.left\),\s*\n\s*top: Math\.round\(r\.top\)/);
|
||||||
|
assert.match(np, /bindVideoDrag\(dock\);/);
|
||||||
|
});
|
||||||
|
|
||||||
// ── Static checks: CSS ─────────────────────────────────────────────────────
|
// ── Static checks: CSS ─────────────────────────────────────────────────────
|
||||||
test("style.css defines the media viewers and the Now Playing dock", () => {
|
test("style.css defines the media viewers and the Now Playing dock", () => {
|
||||||
assert.match(css, /\.audio-viewer-container/);
|
assert.match(css, /\.audio-viewer-container/);
|
||||||
@@ -131,6 +138,11 @@ test("style.css defines the media viewers and the Now Playing dock", () => {
|
|||||||
assert.match(css, /body\.np-active \.content-area/);
|
assert.match(css, /body\.np-active \.content-area/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("mobile dock uses a grid so actions never overlap the progress bar", () => {
|
||||||
|
assert.match(css, /grid-template-areas:\s*\n\s*"progress progress progress progress"/);
|
||||||
|
assert.match(css, /\.np-dock--audio \[data-np="expand"\]/);
|
||||||
|
});
|
||||||
|
|
||||||
test("style.css no longer references undefined --surface1 / --text-dim vars", () => {
|
test("style.css no longer references undefined --surface1 / --text-dim vars", () => {
|
||||||
assert.doesNotMatch(css, /var\(--surface1\)/);
|
assert.doesNotMatch(css, /var\(--surface1\)/);
|
||||||
assert.doesNotMatch(css, /var\(--text-dim\)/);
|
assert.doesNotMatch(css, /var\(--text-dim\)/);
|
||||||
|
|||||||
Reference in New Issue
Block a user