Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bab272bb5c | ||
|
|
34428ea7b3 | ||
|
|
83e6a951dd | ||
|
|
3d618eb660 |
+43
-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).
|
||||
|
||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.49.0**.
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.49.4**.
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,48 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [2.49.4] — 2026-10-02
|
||||
|
||||
---
|
||||
|
||||
## [2.49.3] — 2026-10-02
|
||||
|
||||
### Corrigé
|
||||
|
||||
- **BUG-104 — commandes desktop bloquées par l'ACL Tauri** : la fenêtre desktop sert
|
||||
sa page depuis `http://127.0.0.1` (origine *remote*) et aucune commande applicative
|
||||
n'était déclarée — toutes étaient rejetées « not allowed by ACL », erreurs avalées
|
||||
par `desktop.js:invoke()`. Nouveau manifeste `desktop/permissions/commands.toml`
|
||||
(`allow-app-commands`, 19 commandes) référencé par la capability, avec un test
|
||||
garde-fou (`test_frontend_invokes_are_acl_allowed`). Le bouton « Choisir mon
|
||||
dossier » du wizard et toute la gestion vaults/dossiers desktop fonctionnent.
|
||||
- **BUG-105 — crash du desktop à l'ouverture (heap corruption `c0000374`)** : le
|
||||
setter de titre du menu contextuel des raccourcis (#77) confiait un `PROPVARIANT
|
||||
VT_LPWSTR` sur un buffer heap Rust au property store du shell, qui le libérait avec
|
||||
l'allocateur Windows — l'application se fermait 2 à 15 s après le lancement. Fonction
|
||||
supprimée (libellé assuré par `SetDescription`).
|
||||
|
||||
---
|
||||
|
||||
## [2.49.2] — 2026-10-02
|
||||
|
||||
### Corrigé
|
||||
|
||||
- Tests : `mobileBlockContaining` ignore les commentaires CSS — un sélecteur cité dans
|
||||
une annotation ne faisait plus trouver le bon bloc mobile (CI #842).
|
||||
|
||||
---
|
||||
|
||||
## [2.49.1] — 2026-10-02
|
||||
|
||||
### Corrigé
|
||||
|
||||
- **Mobile (BUG-103)** : le bas du sidebar de navigation n'est plus masqué par la barre
|
||||
d'outils du bas — `z-index` du sidebar mobile `200` → `950` (au-dessus de
|
||||
`.mobile-toolbar`, `900`).
|
||||
|
||||
---
|
||||
|
||||
## [2.49.0] — 2026-10-02
|
||||
|
||||
### 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.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.49.0).
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.49.4).
|
||||
|
||||
---
|
||||
|
||||
*Projet : ObsiGate | Version : 2.49.0 | Dernière mise à jour : Septembre 2026*
|
||||
*Projet : ObsiGate | Version : 2.49.4 | 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.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.49.0).
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.49.4).
|
||||
|
||||
---
|
||||
|
||||
*Project: ObsiGate | Version: 2.49.0 | Last updated: September 2026*
|
||||
*Project: ObsiGate | Version: 2.49.4 | Last updated: September 2026*
|
||||
|
||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.49.0"
|
||||
version = "2.49.4"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.49.0"
|
||||
version = "2.49.4"
|
||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||
authors = ["Bruno Charest"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -38,5 +38,7 @@ fn main() {
|
||||
println!("cargo:rerun-if-changed=.git/refs/heads/main");
|
||||
println!("cargo:rerun-if-changed=.git/refs/tags");
|
||||
|
||||
println!("cargo:rerun-if-changed=permissions");
|
||||
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
},
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"allow-app-commands",
|
||||
"shell:allow-open",
|
||||
"shell:allow-execute",
|
||||
"dialog:default",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"default":{"identifier":"default","description":"Default capabilities for ObsiGate Desktop","remote":{"urls":["http://127.0.0.1:*","http://localhost:*"]},"local":true,"windows":["main"],"permissions":["core:default","shell:allow-open","shell:allow-execute","dialog:default","notification:default","fs:default","process:default","store:default"]}}
|
||||
{"default":{"identifier":"default","description":"Default capabilities for ObsiGate Desktop","remote":{"urls":["http://127.0.0.1:*","http://localhost:*"]},"local":true,"windows":["main"],"permissions":["core:default","allow-app-commands","shell:allow-open","shell:allow-execute","dialog:default","notification:default","fs:default","process:default","store:default"]}}
|
||||
@@ -2210,6 +2210,12 @@
|
||||
"Identifier": {
|
||||
"description": "Permission identifier",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Commandes de l'application ObsiGate appelées depuis la page backend (http://127.0.0.1).",
|
||||
"type": "string",
|
||||
"const": "allow-app-commands",
|
||||
"markdownDescription": "Commandes de l'application ObsiGate appelées depuis la page backend (http://127.0.0.1)."
|
||||
},
|
||||
{
|
||||
"description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`",
|
||||
"type": "string",
|
||||
|
||||
@@ -2210,6 +2210,12 @@
|
||||
"Identifier": {
|
||||
"description": "Permission identifier",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Commandes de l'application ObsiGate appelées depuis la page backend (http://127.0.0.1).",
|
||||
"type": "string",
|
||||
"const": "allow-app-commands",
|
||||
"markdownDescription": "Commandes de l'application ObsiGate appelées depuis la page backend (http://127.0.0.1)."
|
||||
},
|
||||
{
|
||||
"description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`",
|
||||
"type": "string",
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Commandes applicatives ObsiGate — autorisées depuis l'origine distante.
|
||||
#
|
||||
# La fenêtre Tauri redirige vers http://127.0.0.1:<port> (page servie par le
|
||||
# backend Python). Pour Tauri v2 c'est une origine *remote* : sans entrée ACL
|
||||
# explicite, TOUTE commande de l'app (invoke_handler) est rejetée avec
|
||||
# "Command ... not allowed by ACL" — le bouton « Choisir mon dossier » et le
|
||||
# wizard ne faisaient alors rien (erreurs avalées par desktop.js:invoke()).
|
||||
#
|
||||
# Auto-généré en… non : édité à la main. Liste = tauri::generate_handler![…]
|
||||
# dans src/main.rs — ajouter toute nouvelle commande ici.
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-app-commands"
|
||||
description = "Commandes de l'application ObsiGate appelées depuis la page backend (http://127.0.0.1)."
|
||||
commands.allow = [
|
||||
"get_backend_url",
|
||||
"get_version",
|
||||
"get_config",
|
||||
"save_vault_path",
|
||||
"get_vault_path",
|
||||
"get_wizard_state",
|
||||
"complete_wizard",
|
||||
"pick_vault_folder",
|
||||
"get_system_theme",
|
||||
"restart_backend",
|
||||
"check_backend_health",
|
||||
"save_window_state",
|
||||
"get_window_state",
|
||||
"add_vault",
|
||||
"remove_vault",
|
||||
"list_vaults",
|
||||
"add_dir",
|
||||
"remove_dir",
|
||||
"list_dirs",
|
||||
]
|
||||
+19
-43
@@ -10,48 +10,23 @@ use log::{info, warn};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod imp {
|
||||
use super::*;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows::core::{Interface, HSTRING, PCWSTR};
|
||||
use windows::Win32::Foundation::PROPERTYKEY;
|
||||
use windows::Win32::System::Com::{
|
||||
CoCreateInstance, CoInitializeEx, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED,
|
||||
};
|
||||
use windows::Win32::System::Com::StructuredStorage::PROPVARIANT;
|
||||
use windows::Win32::System::Variant::VT_LPWSTR;
|
||||
use windows::Win32::UI::Shell::{
|
||||
ICustomDestinationList, IShellLinkW, SetCurrentProcessExplicitAppUserModelID,
|
||||
DestinationList, EnumerableObjectCollection, ShellLink,
|
||||
};
|
||||
use windows::Win32::UI::Shell::Common::IObjectCollection;
|
||||
use windows::Win32::UI::Shell::PropertiesSystem::IPropertyStore;
|
||||
use super::*;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows::core::{Interface, HSTRING, PCWSTR};
|
||||
use windows::Win32::System::Com::{
|
||||
CoCreateInstance, CoInitializeEx, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED,
|
||||
};
|
||||
use windows::Win32::UI::Shell::{
|
||||
ICustomDestinationList, IShellLinkW, SetCurrentProcessExplicitAppUserModelID,
|
||||
DestinationList, EnumerableObjectCollection, ShellLink,
|
||||
};
|
||||
use windows::Win32::UI::Shell::Common::IObjectCollection;
|
||||
|
||||
const APP_ID: &str = "com.obsigate.desktop";
|
||||
const APP_ID: &str = "com.obsigate.desktop";
|
||||
|
||||
// PKEY_Title (System.Title): {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, pid 2
|
||||
const PKEY_TITLE: PROPERTYKEY = PROPERTYKEY {
|
||||
fmtid: windows::core::GUID::from_u128(0xF29F85E0_4FF9_1068_AB91_08002B27B3D9),
|
||||
pid: 2,
|
||||
};
|
||||
|
||||
fn wide_null_terminated(s: &str) -> Vec<u16> {
|
||||
std::ffi::OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect()
|
||||
}
|
||||
|
||||
/// Set the jump-list display title via the shell link's property store.
|
||||
fn set_link_title(link: &IShellLinkW, title: &str) -> windows::core::Result<()> {
|
||||
let store: IPropertyStore = link.cast()?;
|
||||
let mut wide = wide_null_terminated(title);
|
||||
let mut pv: PROPVARIANT = unsafe { std::mem::zeroed() };
|
||||
unsafe {
|
||||
let inner = &mut *pv.Anonymous.Anonymous;
|
||||
inner.vt = VT_LPWSTR;
|
||||
inner.Anonymous.pwszVal = windows::core::PWSTR(wide.as_mut_ptr());
|
||||
store.SetValue(&PKEY_TITLE, &pv)?;
|
||||
store.Commit()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn wide_null_terminated(s: &str) -> Vec<u16> {
|
||||
std::ffi::OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect()
|
||||
}
|
||||
|
||||
/// Build one shell link for a vault. `arg` is the command-line argument the
|
||||
/// app expects to open that vault (see file associations / single-instance).
|
||||
@@ -70,9 +45,10 @@ mod imp {
|
||||
}
|
||||
link.SetDescription(&HSTRING::from(name))?;
|
||||
}
|
||||
// Title (display name) is best-effort — a missing title only means the
|
||||
// jump-list entry falls back to the executable name.
|
||||
let _ = set_link_title(&link, name);
|
||||
// Title (display name): the raw PROPVARIANT setter that used to live
|
||||
// here corrupted the process heap (STATUS_HEAP_CORRUPTION c0000374 —
|
||||
// the shell property store freed our Rust-allocated VT_LPWSTR buffer).
|
||||
// SetDescription below already gives jump-list entries their label.
|
||||
Ok(link)
|
||||
}
|
||||
|
||||
|
||||
@@ -1122,4 +1122,65 @@ mod tests {
|
||||
let dbg = format!("{:?}", d);
|
||||
assert!(dbg.contains("x"));
|
||||
}
|
||||
|
||||
/// Guardrail ACL : toute commande Tauri invoquée par le frontend doit
|
||||
/// figurer dans desktop/permissions/commands.toml. La page servie par le
|
||||
/// backend (http://127.0.0.1) est une origine *remote* pour Tauri v2 :
|
||||
/// sans cette entrée, la commande est rejetée « not allowed by ACL » et
|
||||
/// desktop.js:invoke() avale l'erreur → bouton silencieusement mort
|
||||
/// (bouton « Choisir mon dossier » du wizard, BUG-104).
|
||||
#[test]
|
||||
fn test_frontend_invokes_are_acl_allowed() {
|
||||
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let perms = fs::read_to_string(dir.join("permissions/commands.toml"))
|
||||
.expect("desktop/permissions/commands.toml manquant");
|
||||
let main_src = fs::read_to_string(dir.join("src/main.rs")).unwrap();
|
||||
|
||||
// Commandes déclarées dans le manifeste de permissions.
|
||||
let allow_list = perms
|
||||
.split("commands.allow")
|
||||
.nth(1)
|
||||
.and_then(|s| s.split(']').next())
|
||||
.unwrap_or("");
|
||||
let allowed: Vec<String> = allow_list
|
||||
.split('"')
|
||||
.skip(1)
|
||||
.step_by(2)
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
assert!(!allowed.is_empty(), "commands.allow vide dans commands.toml");
|
||||
|
||||
// Chaque permission doit correspondre à une commande réellement
|
||||
// enregistrée (faute de frappe → permission morte).
|
||||
for cmd in &allowed {
|
||||
assert!(
|
||||
main_src.contains(&format!("fn {cmd}(")),
|
||||
"permission allow pour la commande inconnue {cmd}"
|
||||
);
|
||||
}
|
||||
|
||||
// Toute commande invoke('…') du frontend doit être autorisée.
|
||||
let js_dir = dir.join("../frontend/js");
|
||||
let mut seen = 0usize;
|
||||
for entry in fs::read_dir(&js_dir).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
if path.extension().and_then(|e| e.to_str()) != Some("js") { continue; }
|
||||
let src = fs::read_to_string(&path).unwrap();
|
||||
let mut rest = src.as_str();
|
||||
while let Some(pos) = rest.find("invoke('") {
|
||||
rest = &rest[pos + "invoke('".len()..];
|
||||
let name: String = rest.chars().take_while(|c| c.is_ascii_alphanumeric() || *c == '_').collect();
|
||||
if !name.is_empty() {
|
||||
seen += 1;
|
||||
assert!(
|
||||
allowed.contains(&name),
|
||||
"commande {name} invoquée par {} absente de permissions/commands.toml",
|
||||
path.file_name().unwrap().to_string_lossy()
|
||||
);
|
||||
}
|
||||
rest = &rest[rest.find('\'').map(|i| i + 1).unwrap_or(rest.len())..];
|
||||
}
|
||||
}
|
||||
assert!(seen > 0, "aucun invoke('…') trouvé dans frontend/js");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||
"productName": "ObsiGate",
|
||||
"version": "2.49.0",
|
||||
"version": "2.49.4",
|
||||
"identifier": "com.obsigate.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../frontend",
|
||||
|
||||
@@ -210,6 +210,9 @@ Avant de corriger quoi que ce soit, un agent IA doit :
|
||||
| *BUG-100* | Retour à l'accueil incomplet : le clic sur le titre perd la section « Raccourcis & Astuces » et laisse la recherche globale ainsi que le filtre de la sidebar actifs | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/viewer.js` (`showWelcome`), `frontend/js/legacy.js` (`goHome`) | Ouvrir un fichier (le dashboard est détruit), puis cliquer le titre ObsiGate | `viewer.js` : `#quick-help` capturé au chargement du module et réinjecté dans le template reconstruit de `showWelcome()` ; `legacy.js::goHome` clique `#search-clear-btn` (chips + barre de résultats) et `#sidebar-filter-clear-btn` avant d'afficher l'accueil | **Cause racine :** `showWelcome()` reconstruit `#dashboard-home` depuis un template qui n'embarque pas le bloc `#quick-help` présent dans `index.html` — il ne disparaissait qu'après la première reconstruction (ouverture de fichier, puis retour). Livré avec #158 A3 |
|
||||
| *BUG-101* | Raccourcis d'onglets déclarés deux fois : un seul `Ctrl+W` fermait deux onglets (le second fermait celui ré-activé par la fermeture précédente) et `Ctrl+Tab` sautait un onglet | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/ui.js` (bloc « Keyboard shortcuts for tabs » dupliqué en fin de fichier) | Ouvrir deux onglets puis `Ctrl+W` une fois | Suppression du second bloc `document.addEventListener("keydown", …)` dupliqué (fin de `ui.js`) — un seul listener reste | Exposé par #158 : le second onglet n'était plus jamais vide auparavant (la 2ᵉ fermeture tombait sur `_activeTabId === null` et devenait sans effet). Vérifié : repro Playwright (`closes: ["fichier", "nav"]` avant → `["fichier"]` après) + suite E2E |
|
||||
| *BUG-102* | Menu contextuel qui se referme ~10 ms après son ouverture : la reflow des icônes (remplacement `<i>` → `<svg>` par lucide) déclenche un événement `scroll` capturé par `ContextMenuManager` qui referme le menu | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/js/ui.js` (`ContextMenuManager.init/show`) | Clic droit sur un fichier de la page de navigation (ou de l'arbre) juste après un rendu d'icônes | `show()` mémorise `_shownAt` ; le listener `scroll` (capture) ignore un déclenchement dans les 250 ms suivant l'ouverture | Exposé par #158 A5 (test E2E `nav-tab.spec.js` : menu résolu « hidden » alors que 6 items étaient construits). Vérifié : instrumentation Playwright (display block conservé après le scroll) + suite E2E |
|
||||
| *BUG-103* | Mobile : le bas du sidebar de navigation est masqué par la barre d'outils du bas (`z-index` 200 < 900) | 🟢 corrigé | P2 | 📱 frontend | IA | `frontend/style.css` (règle `.sidebar` du bloc `@media (max-width: 768px)`) | Vue mobile (≤768 px), sidebar ouvert depuis « Explorateur » | Passé à `z-index: 950` (au-dessus de `.mobile-toolbar`, 900) | Exposé après #158 (retour utilisateur). Vérifié : `elementFromPoint` Playwright (393×851) + test source `mobile sidebar over toolbar` (`unit.test.mjs`) |
|
||||
| *BUG-104* | Desktop : toutes les commandes Tauri invoquées depuis la page backend (`http://127.0.0.1`) rejetées « not allowed by ACL » — le bouton « Choisir mon dossier » du wizard ne fait rien | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/permissions/commands.toml` (nouveau), `desktop/capabilities/default.json`, `desktop/build.rs` | Installeur 2.49.2 : clic sur « Choisir mon dossier » → aucune fenêtre ; console `[desktop] invoke(pick_vault_folder) failed: Command … not allowed by ACL` (capturée via CDP, WebView2 `--remote-debugging-port`) | Manifeste de permissions applicatives créé (`allow-app-commands`, 19 commandes) et référencé par la capability : Tauri v2 ACL gate toute commande invoquée depuis une origine *remote*, y compris celles de l'`invoke_handler` | `cargo test` 25 passed (nouveau garde-fou `test_frontend_invokes_are_acl_allowed`) + vérif live CDP : `get_version`→2.49.2, clic → fenêtre native « Select Folder », parcours complet ajout de vault terminé |
|
||||
| *BUG-105* | Desktop : crash 2-15 s après l'ouverture de la fenêtre (`APPCRASH c0000374`, heap corruption détectée dans ntdll) — absent de v2.0.0, apparu en 2.49.2 | 🟢 corrigé | P1 | 🖥️ desktop | IA | `desktop/src/jumplist.rs` (`set_link_title`) | Installeur 2.49.2 : fenêtre visible quelques secondes puis fermeture (3/3), Event Log Windows `APPCRASH obsigate-desktop.exe … c0000374`, rien dans stderr | `set_link_title` (#77) construisait un `PROPVARIANT VT_LPWSTR` pointant un buffer heap Rust et le confiait au property store du shell, qui le libère avec l'allocateur Windows → corruption du tas ; fonction supprimée (`SetDescription` fournit déjà le libellé) | Bisect 3 états (jumplist off = stable, titre off = stable, d'origine = crash) ; run 9 min sans crash + sortie propre `EXIT=0`, zéro événement WER (avant : 3 crashes ≤20 s) |
|
||||
| | | | | | | | | | |
|
||||
### TODOs techniques (améliorations / nouvelles tâches)
|
||||
|
||||
@@ -316,6 +319,9 @@ Avant de corriger quoi que ce soit, un agent IA doit :
|
||||
| 2026-10-02 | #158 (A4-A6) | Fonctionnalité | `frontend/js/vaulthome.js`, `frontend/js/ui.js`, `frontend/js/pane-manager.js`, `frontend/js/legacy.js`, `frontend/js/sidebar.js`, `frontend/style.css`, `frontend/locales/{fr,en}.json`, `tests/e2e/nav-tab.spec.js` (nouveau), `CHANGELOG.md`, `docs/ROADMAP.md`, `docs/features/navigation-tab-158.md` | **A4** : `TabManager.openHome()` — le clic sur le titre ouvre la page d'accueil dans un **onglet Accueil** (icône Maison, `common.home` FR/EN) remonté en position 0 à chaque fois ; `deactivate()` conserve cet onglet actif, `pane-manager.js` a son propre `openHome` pour le split. **A5** : menus contextuels répertoires **et** fichiers de la page de navigation via `ContextMenuManager.show()` (avec `stopPropagation()`, le handler global referme sinon le menu) — listes vérifiées identiques à la sidebar ; la ligne d'un vault rouvre la page de navigation. **A6** : icône des répertoires à `var(--accent)` (couleur de l'arbre). **Correctif annexé** : l'effacement du filtre sidebar au retour à l'accueil n'est déclenché que s'il était actif (sinon `restoreSidebarTree()` repliait l'arbre). Vérifié : Playwright jetable 16/16, `nav-tab.spec.js` 4 tests, validate-imports/unit/i18n parity verts. | 🟢 corrigé (en attente vérif utilisateur) |
|
||||
| 2026-10-02 | BUG-101 | Correction | `frontend/js/ui.js` | **BUG-101 — le bloc « Keyboard shortcuts for tabs » existait en double dans `ui.js`** (fin de fichier) : un seul `Ctrl+W` déclenchait **deux** `close()`, le second refermant l'onglet ré-activé par le premier — observé avec #158 quand un onglet navigation survit à la fermeture du fichier ; `Ctrl+Tab` sautait également un onglet. Bloc dupliqué supprimé (un seul listener). Vérifié : repro Playwright (`close()` appelé 2× puis 1×) et suite E2E. | 🟢 corrigé (en attente vérif utilisateur) |
|
||||
| 2026-10-02 | BUG-102 | Correction | `frontend/js/ui.js` | **BUG-102 — menu contextuel refermé par sa propre ouverture** : la reflow des icônes lucide à l'affichage déclenche un `scroll` capturé qui refermait le menu ~10 ms après son apparition (constaté sur les fichiers de la page de navigation, #158 A5). `show()` mémorise `_shownAt` et le listener `scroll` ignore un déclenchement dans les 250 ms. Vérifié : instrumentation Playwright + suite E2E (`nav-tab.spec.js`). | 🟢 corrigé (en attente vérif utilisateur) |
|
||||
| 2026-10-02 | BUG-103 | Correction | `frontend/style.css` | **BUG-103 — sidebar mobile sous la barre d'outils du bas** : en mobile le `.sidebar` (`z-index: 200`) passait sous `.mobile-toolbar` (`z-index: 900`, 64 px de haut), masquant les dernières entrées de l'arbre ; passé à `950`. Vérifié : `elementFromPoint` Playwright (393×851) + test source `mobile sidebar over toolbar` (`unit.test.mjs`). | 🟢 corrigé |
|
||||
| 2026-10-02 | BUG-104 | Correction | `desktop/permissions/commands.toml` (nouveau), `desktop/capabilities/default.json`, `desktop/build.rs` | **BUG-104 — commandes Tauri rejetées par l'ACL depuis la page backend** : la fenêtre redirige vers `http://127.0.0.1:<port>`, origine *remote* pour Tauri v2, et aucune commande applicative n'était déclarée → `Command … not allowed by ACL` sur `pick_vault_folder`, `get_wizard_state`, etc. ; les erreurs sont avalées par `desktop.js:invoke()` → bouton du wizard silencieusement inopérant depuis #77. Correctif : manifeste `desktop/permissions/commands.toml` (`allow-app-commands`, 19 commandes) référencé par la capability + `rerun-if-changed=permissions` dans `build.rs`. Vérifié : `cargo test` 25 passed (nouveau garde-fou ACL), CDP live : `get_version` OK, clic → fenêtre native « Select Folder », ajout de vault complété de bout en bout. | 🟢 corrigé |
|
||||
| 2026-10-02 | BUG-105 | Correction | `desktop/src/jumplist.rs` | **BUG-105 — crash heap (c0000374) à l'ouverture du desktop** : `set_link_title` (#77) passait un `PROPVARIANT VT_LPWSTR` pointant un buffer heap Rust au property store du shell, qui le libère avec l'allocateur Windows → `STATUS_HEAP_CORRUPTION` détectée dans ntdll ≤20 s après le lancement (3/3, Event Log WER, même empreinte de pile). Fonction supprimée : le libellé des entrées vient de `SetDescription`. Vérifié : bisect sur 3 états de build, run 9 min `EXIT=0`, zéro événement WER. | 🟢 corrigé |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Version :** 2.49.0 | **Dernière mise à jour :** 2026-10-02
|
||||
> **Version :** 2.49.4 | **Dernière mise à jour :** 2026-10-02
|
||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||
> vers les fonctionnalités livrées.
|
||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||
|
||||
+3
-1
@@ -4038,7 +4038,9 @@ body.resizing-v {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 200;
|
||||
/* Par-dessus la barre d'outils mobile (.mobile-toolbar, z-index 900)
|
||||
pour ne pas masquer le bas du sidebar — BUG-103 */
|
||||
z-index: 950;
|
||||
width: 280px !important;
|
||||
min-width: 280px !important;
|
||||
max-width: 85vw !important;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsigate",
|
||||
"version": "2.49.0",
|
||||
"version": "2.49.4",
|
||||
"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",
|
||||
"directories": {
|
||||
|
||||
@@ -42,19 +42,23 @@ function test(label, fn) {
|
||||
/** Body of the first @media (max-width: 768px) block whose body contains `needle`. */
|
||||
function mobileBlockContaining(needle) {
|
||||
const marker = "@media (max-width: 768px)";
|
||||
// Les commentaires sont ignorés : un sélecteur cité dans une annotation
|
||||
// (ex. « .mobile-toolbar » dans un commentaire de style.css) ne doit pas
|
||||
// faire rétrograder ce helper sur le mauvais bloc mobile (BUG-103/CI #842).
|
||||
const source = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
||||
let idx = 0;
|
||||
while ((idx = css.indexOf(marker, idx)) !== -1) {
|
||||
const open = css.indexOf("{", idx);
|
||||
while ((idx = source.indexOf(marker, idx)) !== -1) {
|
||||
const open = source.indexOf("{", idx);
|
||||
let depth = 0;
|
||||
let end = open;
|
||||
for (; end < css.length; end++) {
|
||||
if (css[end] === "{") depth++;
|
||||
else if (css[end] === "}") {
|
||||
for (; end < source.length; end++) {
|
||||
if (source[end] === "{") depth++;
|
||||
else if (source[end] === "}") {
|
||||
depth--;
|
||||
if (depth === 0) break;
|
||||
}
|
||||
}
|
||||
const body = css.slice(open + 1, end);
|
||||
const body = source.slice(open + 1, end);
|
||||
if (body.includes(needle)) return body;
|
||||
idx = end + 1;
|
||||
}
|
||||
|
||||
@@ -307,6 +307,22 @@ function testSyntaxHighlightTheme() {
|
||||
console.log(' ✓ syntax highlighting follows the theme mode (BUG-078)');
|
||||
}
|
||||
|
||||
// ── Test mobile : le sidebar passe par-dessus la barre d'outils du bas (BUG-103) ──
|
||||
function testMobileSidebarOverToolbar() {
|
||||
const css = readFileSync(join(JS_DIR, '..', 'style.css'), 'utf-8');
|
||||
const zOf = (selector) => {
|
||||
const blocks = css.match(new RegExp('\\' + selector + ' \\{[^}]*\\}', 'g')) || [];
|
||||
return Math.max(0, ...blocks.map((b) => Number((b.match(/z-index: (\d+)/) || [])[1] || 0)));
|
||||
};
|
||||
const sidebarZ = zOf('.sidebar');
|
||||
const toolbarZ = zOf('.mobile-toolbar');
|
||||
assert.ok(
|
||||
sidebarZ > toolbarZ,
|
||||
`.sidebar z-index (${sidebarZ}) doit être supérieur à .mobile-toolbar (${toolbarZ})`,
|
||||
);
|
||||
console.log(' ✓ mobile sidebar over toolbar');
|
||||
}
|
||||
|
||||
// ── Run all tests ──────────────────────────────────────────────────────────
|
||||
async function main() {
|
||||
let passed = 0, failed = 0;
|
||||
@@ -324,6 +340,7 @@ async function main() {
|
||||
['config TOC icons', testConfigTocIcons],
|
||||
['sidebar user section', testSidebarUserSection],
|
||||
['syntax highlight theme', testSyntaxHighlightTheme],
|
||||
['mobile sidebar over toolbar', testMobileSidebarOverToolbar],
|
||||
];
|
||||
|
||||
for (const [name, fn] of tests) {
|
||||
|
||||
Reference in New Issue
Block a user