Compare commits

...
6 Commits
Author SHA1 Message Date
Spark 287afa1537 fix: CSP — frame-src autorise la console du service d'agents (mode console du panneau Spark) BUG-119
CI / lint (push) Successful in 3m5s
CI / security (push) Failing after 1m49s
CI / test (push) Successful in 5m19s
CI / build (push) Successful in 1m36s
CI / e2e (push) Successful in 18m9s
2026-10-24 03:10:34 +00:00
Spark b0ac6dfdb9 docs: #222 livré (2.77.0) — statut Roadmap
CI / lint (push) Successful in 2m58s
CI / security (push) Failing after 1m46s
CI / test (push) Successful in 5m10s
CI / build (push) Successful in 1m39s
CI / e2e (push) Successful in 18m2s
2026-10-23 04:13:51 +00:00
Spark 7f06606300 feat: panneau Spark V3 — la console en surimpression pleine page #222 2026-10-23 04:13:37 +00:00
Spark 985af3a294 docs: #221 livré (2.76.1) — statut Roadmap
CI / lint (push) Successful in 2m54s
CI / security (push) Failing after 1m40s
CI / test (push) Successful in 4m59s
CI / build (push) Failing after 11m14s
CI / e2e (push) Skipped
2026-10-22 21:21:27 +00:00
Spark 440cfdcc31 fix: bouton « Console ↗ » dans le panneau Spark #221
CI / lint (push) Successful in 2m53s
CI / security (push) Failing after 1m39s
CI / test (push) Successful in 5m3s
CI / build (push) Successful in 1m39s
CI / e2e (push) Successful in 18m1s
2026-10-22 21:20:51 +00:00
Spark 0ca92c3723 docs: #220 livré (2.76.0) — statut Roadmap
CI / lint (push) Successful in 2m52s
CI / security (push) Failing after 1m38s
CI / test (push) Successful in 5m3s
CI / build (push) Successful in 1m29s
CI / e2e (push) Successful in 17m47s
2026-10-22 19:59:24 +00:00
16 changed files with 412 additions and 16 deletions
+55 -1
View File
@@ -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.76.0**.
> [Unreleased](#unreleased). La dernière version livrée est **2.77.1**.
---
@@ -14,6 +14,60 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---
## [2.77.1] — 2026-10-24
### Corrigé
- **Panneau Spark : « mode console » bloqué par la CSP (BUG-119, Fusion)** —
la surimpression du panneau Spark V3 (#222) charge la console du service
d'agents (`https://agent.dracodev.net`) dans une iframe, mais la CSP servie
limitait `frame-src` à `'self' blob:` : le navigateur refusait le
chargement (« This content is blocked »). `frame-src` autorise désormais
aussi `https://agent.dracodev.net` — déjà présente dans `connect-src`
pour le panneau texte ; aucune autre directive n'est modifiée. Test de
non-régression dans `tests/test_security_headers.py`. Redéploiement
Docker requis pour l'activer côté ObsiGate.
---
## [2.77.0] — 2026-10-23
### Modifié
- **Panneau Spark V3 : la console en surimpression pleine page
(#222, Fusion)** — le bouton « Console ⤢ » du panneau Spark
(asset vendangé, octet-identique dans les 4 apps) n'ouvre plus
la console web du service d'agents en nouvel onglet (#221) :
elle s'ouvre **dans la même page**, en surimpression née à la
largeur du panneau et étendue à toute la fenêtre par
transition, avec le lien profond conservé (`?disc=<id>` et
`?app=obsigate`). Sa barre offre les deux sorties : « Mode
sidebar » (retour à l'agent en barre latérale) et « Fermer
l'agent ». Les styles sont injectés par le panneau lui-même ;
côté service (0.20.0), la console autorise l'encadrement par
les 4 applications. Même livraison dans TikTak 0.60.0,
Flowdeck 7.81.0 et Kanbante 0.29.0. Redéploiement Docker
requis pour l'activer côté ObsiGate.
---
## [2.76.1] — 2026-10-22
### Ajouté
- **Panneau Spark : bouton « Console ↗ » (#221, Fusion)** — la
barre des discussions du panneau Spark (asset vendangé,
octet-identique dans les 4 apps) gagne un bouton « Console ↗ »
qui ouvre la console web du service d'agents
(https://agent.dracodev.net/) dans un nouvel onglet, sur la
discussion actuellement affichée (`?disc=<id>`, URL construite
au clic) et avec l'app d'origine (`?app=obsigate`, déduite du
sous-domaine `og`). Même livraison dans TikTak 0.59.1,
Flowdeck 7.80.1 et Kanbante 0.28.1. Redéploiement Docker
requis pour l'activer côté ObsiGate.
---
## [2.76.0] — 2026-10-22
### Ajouté
+3 -3
View File
@@ -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.
[![Version](https://img.shields.io/badge/Version-2.76.0-blue.svg)]()
[![Version](https://img.shields.io/badge/Version-2.77.1-blue.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/)
[![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/)
@@ -977,8 +977,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.76.0).
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.77.1).
---
*Projet : ObsiGate | Version : 2.76.0 | Dernière mise à jour : Septembre 2026*
*Projet : ObsiGate | Version : 2.77.1 | Dernière mise à jour : Septembre 2026*
+3 -3
View File
@@ -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.
[![Version](https://img.shields.io/badge/Version-2.76.0-blue.svg)]()
[![Version](https://img.shields.io/badge/Version-2.77.1-blue.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/)
[![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/)
@@ -1152,8 +1152,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.76.0).
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.77.1).
---
*Project: ObsiGate | Version: 2.76.0 | Last updated: September 2026*
*Project: ObsiGate | Version: 2.77.1 | Last updated: September 2026*
+1 -1
View File
@@ -1 +1 @@
2.76.0
2.77.1
+1 -1
View File
@@ -194,7 +194,7 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
"connect-src 'self' blob: https://esm.sh https://unpkg.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com https://cdn.jsdelivr.net https://agent.dracodev.net; "
"font-src 'self' data: https://fonts.gstatic.com https://esm.sh; "
"worker-src 'self' blob:; "
"frame-src 'self' blob:; "
"frame-src 'self' blob: https://agent.dracodev.net; "
"object-src 'none'; "
"base-uri 'self'; "
"form-action 'self'; "
+1 -1
View File
@@ -2626,7 +2626,7 @@ dependencies = [
[[package]]
name = "obsigate-desktop"
version = "2.76.0"
version = "2.77.1"
dependencies = [
"chrono",
"env_logger",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "obsigate-desktop"
version = "2.76.0"
version = "2.77.1"
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
authors = ["Bruno Charest"]
edition = "2021"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
"productName": "ObsiGate",
"version": "2.76.0",
"version": "2.77.1",
"identifier": "com.obsigate.desktop",
"build": {
"frontendDist": "../frontend",
+2
View File
@@ -225,6 +225,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| *BUG-115* | Éditeur Forge : cases à cocher collées au texte dans l'aperçu, aperçu qui flashe à la frappe, menu « dimensions du tableau » coupé hors écran sur un document court | 🟢 corrigé | P2 | 📱 frontend / éditeur | IA | `frontend/editor-poc.html` (CSS aperçu, `renderPreview`, `showTableForm`, `showLangPicker`) | Ouvrir un `.md` avec `- [ ]`, activer l'aperçu → texte collé au checkbox + flash « Rendering... » ; `/table` sur un document d'une ligne → forme partiellement visible | (1) règle `.task-list-item input[type=checkbox] { margin-right: 6px }` ajoutée au `<style>` de l'aperçu (miroir `style.css:2303`) ; (2) `renderPreview` ne remplace plus le contenu par le spinner (affiché seulement si vide) + flag `_previewDirty` pour rejouer la dernière frappe ; (3) `.inline-form`/`.lang-picker` en `position: fixed` (coordonnées viewport) avec clamp bas/droite comme le menu slash + `stopPropagation` sur les clics qi/slash (le listener document refermait la forme) | Vérifié en Playwright (instance E2E) : marginRight 6 px + `list-style: none`, MutationObserver : aucun « Rendering... » pendant 14 frappes et dernier état rendu, forme table `bottom 193 < vh 900` sur document 1 ligne + insertion OK |
| *BUG-116* | Éditeur Forge : après insertion d'un tableau (ex. 4 lignes), la textarea reste d'une seule ligne au lieu de s'agrandir | 🟢 corrigé | P2 | 📱 frontend / éditeur | IA | `frontend/editor-poc.html` (`insertAtCursor`, `wrapSelection`) | Insérer un tableau via le menu : le contenu contient bien les nouvelles lignes mais la hauteur de la textarea ne change pas | Les mutations programmatiques de `ta.value` ne déclenchent pas l'événement `input` — seul handler qui rappelait `autoHeight()`/`updateLineNumbers()` (les handlers clavier, eux, les appelaient à la main). Ajout des deux appels dans `insertAtCursor` et `wrapSelection`, les deux points de passage de toutes les insertions (tableau, code, mermaid, checklist, gras multi-ligne) | Vérifié en Playwright sur l'instance E2E (qi → Table, 4 lignes, sur doc d'une ligne) : hauteur 51 → 189 px, 8 lignes rendues, gutter aligné | ### TODOs techniques (améliorations / nouvelles tâches)
| *BUG-117* | Drag & drop des fichiers dans la sidebar : le déplacement d'un répertoire à un autre ne fait plus rien, sans message | 🟢 corrigé | P1 | 📱 frontend / sidebar | IA | `frontend/js/dragdrop.js` (`onDrop`) | Glisser un fichier sur un dossier : aucune requête `/api/move`, aucun toast | La détection `isExternalFiles` dans `onDrop` (`items.length > 0`, ajoutée par #89) comptait aussi les formats string du drag interne (`tree:vault/path/type` posés par `setData` au `dragstart`) : tout déplacement partait dans la branche upload où rien ne passe le filtre `kind === 'file'` → drop avalé silencieusement. Corrigé à la racine : `dragState` (interne) prioritaire + items réellement fichiers. Livré avec #201 : menu contextuel Déplacer/Dupliquer réutilisant `promptTargetFolder`, emplacement actuel pré-sélectionné | Vérifié Playwright instance E2E : glisser `note1.md` → `d1` = toast « Moved file to d1 » + chemin déplacé ; 4 tests `TestDuplicateApi` |
| *BUG-119* | Panneau Spark : le « mode console » (surimpression iframe de la console du service d'agents) est bloqué par le navigateur (« This content is blocked ») | 🟢 corrigé | P1 | ⚙️ backend | IA | `backend/main.py` (`SecurityHeadersMiddleware`) | Panneau Spark → « Console ⤢ » : l'iframe reste sur la page d'erreur du navigateur | `frame-src 'self' blob: https://agent.dracodev.net` dans la CSP servie ; test `tests/test_security_headers.py::test_csp_frame_src_allows_agent_console` | Cause : `frame-src` n'autorisait pas l'origine du service d'agents, pourtant déjà présente dans `connect-src` (le panneau texte fonctionnait). Aucune autre directive modifiée. Redéploiement Docker requis |
| # | Titre | Statut | Priorité | Scope | Assigné | Zone (fichier) | Cmd de repro | Correctif / Commit | Notes |
|---|---|---|---|---|---|---|---|---|---|
@@ -357,6 +358,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| 2026-10-21 | BUG-118 | Correction | `frontend/index.html`, `frontend/draco-barre.css`, `tests/test_draco_barre.py`, `CHANGELOG.md`, `docs/ISSUES_TODOLIST.md` | **BUG-118 — la barre d'apps Fusion était inutilisable en mobile** (retours de Bruno sur captures téléphone). Deux causes : (1) le palier ≤ 700 px de la CSS commune gardait onglets entiers et pastilles domaines — la barre débordait ; la source dracodev-commons passe en **v0.1.2** (spec des barres, § Mobile : marque, onglet de l'app courante, alertes, Spark, avatar ; rail/recherche/domaines/menu masqués au profit des contrôles natifs). (2) Le bouton ☰ de la barre cliquait `sidebar-toggle-btn` (bureau) au lieu de `hamburger-btn` (tiroir mobile) : sur téléphone il ne faisait rien, doublé du vrai hamburger d'ObsiGate juste en dessous. Rail devenu contextuel (`matchMedia` ≤ 768 px, point de bascule d'ObsiGate). Test de non-régression `tests/test_draco_barre.py` (2 tests) | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-10-15 | BUG-117 + #201 | Correction + feature | `frontend/js/dragdrop.js`, `frontend/js/ui.js`, `frontend/js/upload.js`, `frontend/locales/fr.json`, `frontend/locales/en.json`, `backend/services/mutations.py`, `backend/routers/files_write.py`, `backend/schemas.py`, `tests/test_api_main.py`, `CHANGELOG.md`, `docs/ROADMAP.md`, `docs/ISSUES_TODOLIST.md` | **BUG-117 — le drag & drop interne de la sidebar était mort en silence.** `onDrop` classait tout dépôt « externe » dès que `dataTransfer.items` était non vide — or un glisser interne dépose 4 formats string (`tree:*`) : chaque déplacement partait dans la branche upload où le filtre `kind === 'file'` ne trouvait rien — aucun appel API, aucun toast. Détection corrigée (vrais fichiers seulement, `dragState` prioritaire). Livré avec **#201 : menu contextuel Déplacer/Dupliquer** (fichiers et dossiers), modal `promptTargetFolder` réutilisé (+ `opts`, emplacement actuel pré-sélectionné), endpoint `POST /api/duplicate/{vault}` (`duplicate_path`, index incrémental, SSE/webhooks, refus inside-self), i18n FR/EN, 4 tests `TestDuplicateApi` + validation Playwright E2E complète | 🟢 corrigé/livré (en attente vérif utilisateur) |
| 2026-10-09 | #194 | Correction (isolation) | `backend/auth/middleware.py`, `backend/services/recent.py`, `backend/services/search.py`, `backend/routers/search.py`, `backend/routers/config.py`, `backend/routers/conflicts.py`, `backend/routers/files_read.py`, `backend/routers/history.py`, `backend/push.py`, `backend/user_home.py`, `tests/test_user_home.py`, `tests/test_auth_api.py`, `docs/features/user-home-194.md`, `CHANGELOG.md` | **#194 — l'admin voyait le home de chaque utilisateur** (retour utilisateur) : `vaults: ["*"]` ouvrait **tous** les vaults, donc `home-<user>` compris. Correctif à la racine : `check_vault_access` exige un octroi **explicite** pour tout vault `home-*` (nouveau `is_home_vault()`), et les filtres « * » écrits en dur dans les routes ont été remplacés par cet appel unique — dashboard (`routers/config.py`), conflits, liens retour, favoris, abonnements push (`/api/search/replace` utilisait déjà `is_vault_allowed`). `/api/search` filtre désormais les bruts **avant** pagination via `search_vaults(is_allowed=…)` (sinon notes personnelles d'un autre compte dans les résultats). `_grant` n'écarte plus les comptes « * » → l'admin reçoit son propre `home-admin` (auto-réparé au démarrage). Vérifié : `test_user_home.py` 12→**14/14** (dont « * » sans homes + filtrage recherche), `test_auth_api.py` 2/2 (admin ne voit **plus** `home-alice`, alice voit le sien), suite **1705 passed / 2 skipped**, ruff backend 0, mypy 114 fichiers 0, **live Playwright instance 2020** : arborescence admin = `home-admin, TestDir, TestVault` (plus de `home-bruno`), `svg.lucide-home` rendu. Reste ouvert (fuite pré-existante, hors périmètre) : `/api/search/advanced`, `/api/tags`, `/api/tags/suggest`, `/api/tree-search`, `/api/suggest` ne filtrent pas par vault accessible | ✅ livré (en attente vérif utilisateur) |
| 2026-10-23 | BUG-119 | Correction | `backend/main.py`, `tests/test_security_headers.py`, `CHANGELOG.md`, `docs/ISSUES_TODOLIST.md`, `docs/ROADMAP.md` | **BUG-119 — le « mode console » du panneau Spark V3 (#222) était bloqué par le navigateur** (« This content is blocked ») : la console du service d'agents s'affiche dans une iframe `https://agent.dracodev.net`, mais la CSP servie par `SecurityHeadersMiddleware` limitait `frame-src` à `'self' blob:` — seul le panneau texte passait (`connect-src` autorisait déjà l'origine). Correctif strict : `frame-src 'self' blob: https://agent.dracodev.net`, toutes les autres directives inchangées à l'identique. Test de non-régression `test_csp_frame_src_allows_agent_console` (la CSP servie est analysée et la liste exacte des sources `frame-src` verrouillée). Vérifié : 100 passed (security_headers + csp_nonce + security_hardening + draco_barre + version), ruff propre sur les fichiers touchés (51 erreurs préexistantes ailleurs, inchangées), mypy 0 (120 fichiers) | 🟢 corrigé (en attente vérif utilisateur) |
---
+45 -2
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.76.0 | **Dernière mise à jour :** 2026-10-22
> **Version :** 2.77.1 | **Dernière mise à jour :** 2026-10-24
> **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)**
@@ -30,9 +30,51 @@
## 🔵 En cours
### 222. Fusion — panneau Spark V3 : la console en surimpression pleine page
- **Effort :** ~0,1 jour | **Impact :** 🟢 | **Ouvert :** 2026-10-23 | **Statut :** ✅ livré le 2026-10-23 (2.77.0, redéploiement Docker en attente)
- **Fiche :** [features/fusion-panneau-console-surimpression-222.md](./features/fusion-panneau-console-surimpression-222.md)
- **Description :** le panneau Spark (asset vendangé `frontend/draco-panneau.js`,
octet-identique dans les 4 apps) passe en V3 (demande de Bruno,
2026-10-22) : le bouton « Console ⤢ » n'ouvre plus la console du
service d'agents en nouvel onglet (#221) mais en **surimpression
pleine page dans la même page** — montée à la largeur du panneau,
étendue à toute la fenêtre par transition, iframe gardant le lien
profond (`?disc=` / `?app=`), et deux sorties dans sa barre :
« Mode sidebar » (rétraction vers le panneau) et « Fermer l'agent ».
Styles injectés par le panneau ; côté service, la console autorise
l'encadrement par les 4 apps (0.20.0). Même livraison : TikTak
0.60.0, Flowdeck 7.81.0, Kanbante 0.29.0.
- **Sous-tâches :**
- [x] Surimpression + barre « Mode sidebar » / « Fermer l'agent »
dans le panneau canonique (TikTak), recopié octet-identique
dans les 4 apps
- [x] Test `tests/test_draco_barre.py::test_panneau_console_surimpression_222`
(et révision du test #221 : le nouvel onglet disparaît)
- [x] Release v2.77.0 publiée — CI à constater, redéploiement Docker (geste de Bruno)
### 221. Fusion — panneau Spark : bouton « Console ↗ » vers la console du service
- **Effort :** ~0,1 jour | **Impact :** 🟢 | **Ouvert :** 2026-10-22 | **Statut :** ✅ livré le 2026-10-22 (2.76.1, redéploiement Docker en attente)
- **Fiche :** [features/fusion-panneau-console-221.md](./features/fusion-panneau-console-221.md)
- **Description :** le panneau Spark (asset vendangé `frontend/draco-panneau.js`,
octet-identique dans les 4 apps) gagne un bouton « Console ↗ » dans sa barre
des discussions (demande de Bruno, 2026-10-22) : il ouvre la console web du
service d'agents (https://agent.dracodev.net/) dans un nouvel onglet, sur la
discussion actuellement affichée (`?disc=<id>`, URL construite au clic) et
avec l'app d'origine (`?app=obsigate`, déduite du sous-domaine `og`). La
console consomme déjà ces deux paramètres (lien profond de discussion +
puce « Ouvert depuis »). Même livraison : TikTak 0.59.1, Flowdeck 7.80.1,
Kanbante 0.28.1.
- **Sous-tâches :**
- [x] Bouton + `ouvrirConsole()` / `appHote()` dans le panneau canonique
(TikTak), recopié octet-identique dans les 4 apps
- [x] Test `tests/test_draco_barre.py::test_panneau_bouton_console_221`
- [x] Release v2.76.1 publiée — CI à constater, redéploiement Docker (geste de Bruno)
### 220. Fusion — émission d'événements vers le journal du service d'agents (Lot 2, vague 5)
- **Effort :** ~0,5 jour | **Impact :** 🟢 | **Ouvert :** 2026-10-22 | **Statut :** 🔵 en cours
- **Effort :** ~0,5 jour | **Impact :** 🟢 | **Ouvert :** 2026-10-22 | **Statut :** ✅ livré le 2026-10-22 (2.76.0, redéploiement Docker en attente)
- **Fiche :** [features/fusion-journal-evenements-220.md](./features/fusion-journal-evenements-220.md)
- **Description :** ObsiGate émet ses mutations de notes **explicites**
vers le journal d'activité du service d'agents (contrat ACC-0.2,
@@ -1069,6 +1111,7 @@
| 166 | Assistant IA — détection & fusion de doublons (score déterministe, paires candidates, fusion backup + confirmation) | Unreleased | [features/agent-phase4-166-168-170.md](./features/agent-phase4-166-168-170.md) |
| 168 | Assistant IA — notifications externes Discord / Telegram / SMTP / webhook (déclencheurs, secrets hors config, SSRF-safe) | Unreleased | [features/agent-phase4-166-168-170.md](./features/agent-phase4-166-168-170.md) |
| 170 | Assistant IA — tâches planifiées type cron (create/append/notify, interval/daily/once, tick 60 s, échec notifié) | Unreleased | [features/agent-phase4-166-168-170.md](./features/agent-phase4-166-168-170.md) |
| BUG-119 | Panneau Spark — « mode console » bloqué par la CSP (`frame-src` sans l'origine du service d'agents) | 2.77.1 | [ISSUES_TODOLIST.md](./ISSUES_TODOLIST.md) |
---
@@ -0,0 +1,42 @@
# #221 — Fusion : bouton « Console ↗ » dans le panneau Spark
**Contexte.** Le panneau Spark (#218) et la console web du service
d'agents (Lot 2, https://agent.dracodev.net/) sont deux faces du même
service : mêmes discussions, mêmes approbations, mêmes missions. Il
manquait le lien de navigation entre les deux. Demande de Bruno
(2026-10-22) : un bouton dans le panneau qui ouvre la console.
**Changement.** Dans `frontend/draco-panneau.js` — asset **vendangé**,
maintenu en canonique dans TikTak et recopié **octet-identique** dans
les 4 applications (TikTak, Flowdeck, Kanbante, ObsiGate) :
- un bouton fantôme « Console ↗ » dans la barre des discussions, à
côté de « ☰ Discussions » et « + Nouvelle » (même classe `d-btn`,
donc visible en thème clair comme sombre, le panneau suivant le
thème de l'app hôte) ;
- au clic, `ouvrirConsole()` construit l'URL **à ce moment-là** (la
discussion peut avoir changé sans recharger le panneau) :
`https://agent.dracodev.net/?disc=<id de la discussion affichée>&app=<app hôte>` ;
- la discussion vient de l'état du panneau (`convId`, repli
`sessionStorage draco_panneau_conv`) ; l'app hôte est déduite du
sous-domaine par `appHote()` (`og` → `obsigate`, `tiktak`,
`flowdeck`, `kanbante` ; hôte inconnu → pas de paramètre) ;
- ouverture en **nouvel onglet** (`window.open(..., "_blank",
"noopener")`). Sans discussion connue : lien simple vers la
racine de la console.
La console consomme déjà les deux paramètres : `?disc=` ouvre la
discussion demandée (lien profond), `?app=` s'affiche en puce
« Ouvert depuis : … » dans « Ce que Spark voit ».
**Vérifications.** Syntaxe JS (`node --check`), test fonctionnel de
la construction d'URL (4 cas : app connue/inconnue × discussion
présente/absente), test de non-régression
`tests/test_draco_barre.py::test_panneau_bouton_console_221`
(marqueurs du bouton et de la logique dans l'asset servi), somme
md5 commune des 4 copies : `c67cc2ab8e657b4e545fa93b28c3a0c4`.
**Versions.** ObsiGate 2.76.1 (cette livraison) ; TikTak 0.59.1 et
Flowdeck 7.80.1 déployées automatiquement sur release ; Kanbante
0.28.1 publiée. **Activation côté ObsiGate : redéploiement Docker
par Bruno.**
@@ -0,0 +1,52 @@
# #222 — Fusion : panneau Spark V3, la console en surimpression pleine page
**Contexte.** Le bouton « Console ↗ » du panneau Spark (#221) ouvrait
la console web du service d'agents (https://agent.dracodev.net/)
dans un nouvel onglet : on quittait l'application en cours pour un
autre onglet. Demande de Bruno (2026-10-22) : rester dans la même
page — la console s'ouvre par-dessus l'application, avec une
animation comme celle de la barre latérale mais qui recouvre la
page au complet, et deux sorties explicites : revenir en mode
sidebar, ou fermer l'agent.
**Changement.** Dans `frontend/draco-panneau.js` — asset **vendangé**,
maintenu en canonique dans TikTak et recopié **octet-identique** dans
les 4 applications (TikTak, Flowdeck, Kanbante, ObsiGate) :
- le bouton devient « Console ⤢ » (même emplacement, barre des
discussions) et `ouvrirConsole()` n'ouvre plus d'onglet : il
monte une **surimpression** `.draco-console-sur` ancrée à droite,
née à la largeur du panneau, qui **s'étend à 100 % de la fenêtre**
par transition CSS (double `requestAnimationFrame` pour laisser
peindre la géométrie de départ) ;
- la console est chargée dans une **iframe** dont l'URL reste
construite au premier clic par `urlConsole()` : discussion
affichée (`?disc=<id>`) et app d'origine (`?app=obsigate`, déduite
du sous-domaine `og`) — le lien profond de #221 est conservé ;
- la barre de la surimpression (« ✦ Console Spark ») porte les deux
sorties demandées : **« ◧ Mode sidebar »** (`retrecirConsole()` :
l'overlay se rétracte à la largeur du panneau puis se masque,
l'iframe reste chargée) et **« ✕ Fermer l'agent »** (`fermer()` +
rétraction : panneau et console fermés) ;
- les styles de la surimpression sont **injectés par le panneau
lui-même** (`<style id="draco-console-styles">`, variables du
thème du panneau) : aucun fichier CSS à toucher côté hôte, et la
surimpression suit le thème de l'app comme le panneau.
Côté service d'agents (0.20.0) : la console autorise désormais
l'encadrement par les 4 applications (`frame-ancestors` restreint
aux sous-domaines `dracodev.net` des apps) — sans quoi l'iframe
resterait blanche.
**Vérifications.** Syntaxe JS (`node --check`) ; test
`tests/test_draco_barre.py::test_panneau_console_surimpression_222`
(marqueurs de la surimpression, des deux sorties et de l'animation
dans l'asset) ; le test #221 est révisé en conséquence (le lien
profond `?disc=` / `?app=` subsiste, l'ouverture en nouvel onglet
disparaît) ; somme md5 commune des copies :
`93026ffa14bc8ee896349e3858e12edf`.
**Versions.** ObsiGate 2.77.0 (cette livraison) ; TikTak 0.60.0 et
Flowdeck 7.81.0 déployées automatiquement sur release ; Kanbante
0.29.0 publiée. **Activation côté ObsiGate : redéploiement Docker
par Bruno.**
+138 -1
View File
@@ -18,6 +18,16 @@
* Panneau v2 (Lot 1) : rendu Markdown, largeur réglable à la souris
* (persistée), thème clair/sombre aligné sur l'app hôte
* (html[data-theme], repli prefers-color-scheme, suivi en direct).
* Bouton « Console » (barre des discussions) : ouvre la console web
* du même service (agent.dracodev.net) DANS la page, en surimpression
* pleine page (iframe) qui naît à la géométrie du panneau puis
* s'étend à toute la fenêtre ; l'URL profonde (?disc= discussion
* courante, ?app= app d'origine déduite du sous-domaine) est celle
* du premier chargement — l'iframe est créé paresseusement et
* conservé, la console garde son état entre ouvertures. La barre
* d'outils de la surimpression offre « Mode sidebar » (retour au
* panneau seul, animation inverse) et « Fermer l'agent » (referme
* surimpression et panneau).
* États honnêtes : service injoignable / session non reconnue / LLM non
* configuré / exécuteur non configuré sont affichés tels quels. */
(function () {
@@ -44,6 +54,124 @@
catch (e) { return null; }
}
/* ── Console web (même service, surimpression pleine page) ─── */
function appHote() {
var h = (window.location.hostname || "").split(".")[0];
var correspondance = { tiktak: "tiktak", flowdeck: "flowdeck",
kanbante: "kanbante", og: "obsigate" };
return correspondance[h] || null;
}
function urlConsole() {
var params = [];
var id = convId || convStockee();
if (id) params.push("disc=" + encodeURIComponent(id));
var app = appHote();
if (app) params.push("app=" + encodeURIComponent(app));
return API + "/" + (params.length ? "?" + params.join("&") : "");
}
/* La surimpression vit entièrement dans ce fichier (DOM + styles
* injectés) : les apps hôtes n'ont que ce fichier à recopier. Son
* thème suit celui du panneau (data-draco-theme synchronisé par
* appliquerTheme) et sa palette reprend les mêmes valeurs que
* draco-panneau.css. L'iframe est créé au premier clic puis
* conservé : rouvrir la console ne le recharge pas. */
var surEl = null, surCadre = null, surOuverte = false;
var STYLES_CONSOLE =
".draco-console-sur{" +
"--p-fond:#fbfbfa;--p-surface:#ffffff;--p-bord:#e4e5e0;--p-texte:#242624;" +
"--p-chip:#f4f5f1;--p-chip-bord:#d6d8d1;--p-chip-survol:#e9ebe4;--p-accent:#7c5cd6;" +
"position:fixed;top:0;right:0;height:100dvh;width:388px;max-width:100vw;z-index:1000;" +
"background:var(--p-fond);color:var(--p-texte);" +
"box-shadow:-12px 0 36px rgb(0 0 0 / .22);" +
"display:flex;flex-direction:column;transition:width .3s ease;" +
"font:14px/1.5 system-ui,\"Segoe UI\",sans-serif}" +
".draco-console-sur[data-draco-theme=\"dark\"]{" +
"--p-fond:#17181a;--p-surface:#1f2023;--p-bord:#33353a;--p-texte:#eceeeb;" +
"--p-chip:#26272b;--p-chip-bord:#3a3c41;--p-chip-survol:#2e3036}" +
".draco-console-sur[hidden]{display:none}" +
".draco-console-barre{display:flex;align-items:center;gap:9px;flex:none;" +
"padding:9px 12px;border-bottom:1px solid var(--p-bord);background:var(--p-surface)}" +
".draco-console-barre .dc-titre{font-weight:700;font-size:14px;" +
"display:flex;align-items:center;gap:8px;white-space:nowrap}" +
".draco-console-barre .dc-etoile{color:var(--p-accent);font-size:17px}" +
".draco-console-barre .dc-actions{margin-left:auto;display:flex;gap:7px}" +
".draco-console-barre button{border:1px solid var(--p-chip-bord);" +
"background:var(--p-chip);color:var(--p-texte);border-radius:999px;" +
"padding:5px 12px;font:600 12.5px system-ui;cursor:pointer;white-space:nowrap}" +
".draco-console-barre button:hover{background:var(--p-chip-survol)}" +
".draco-console-cadre{flex:1;width:100%;border:0;display:block;background:var(--p-fond)}";
function assurerSurimpression() {
if (surEl) return;
if (!document.getElementById("draco-console-styles")) {
var st = document.createElement("style");
st.id = "draco-console-styles";
st.textContent = STYLES_CONSOLE;
document.head.appendChild(st);
}
surEl = document.createElement("div");
surEl.className = "draco-console-sur";
surEl.setAttribute("aria-label", "Console Spark");
surEl.hidden = true;
surEl.innerHTML =
'<div class="draco-console-barre">' +
' <span class="dc-titre"><span class="dc-etoile">✦</span> Console Spark</span>' +
' <span class="dc-actions">' +
' <button type="button" data-dc="sidebar" title="Réduire la console et revenir au panneau latéral">◧ Mode sidebar</button>' +
' <button type="button" data-dc="fermer" title="Fermer la console et le panneau Spark">✕ Fermer l\'agent</button>' +
' </span></div>' +
'<iframe class="draco-console-cadre" title="Console Spark" allow="microphone"></iframe>';
document.body.appendChild(surEl);
surCadre = surEl.querySelector("iframe");
surEl.querySelector('[data-dc="sidebar"]')
.addEventListener("click", modeSidebarConsole);
surEl.querySelector('[data-dc="fermer"]')
.addEventListener("click", fermerAgentConsole);
appliquerTheme();
}
function largeurPanneau() {
if (root) {
var w = root.getBoundingClientRect().width;
if (w > 0) return Math.round(w);
}
return 388;
}
function ouvrirConsole() {
assurerSurimpression();
if (!surCadre.getAttribute("src")) surCadre.src = urlConsole();
if (surOuverte) return;
surOuverte = true;
/* Naissance à la géométrie du panneau (ancrée à droite), puis
* extension à toute la page ; le double rAF laisse le temps de
* peindre la largeur de départ, sinon la transition ne joue pas. */
surEl.style.width = largeurPanneau() + "px";
surEl.hidden = false;
requestAnimationFrame(function () {
requestAnimationFrame(function () { surEl.style.width = "100%"; });
});
}
function retrecirConsole() {
if (!surEl || !surOuverte) return;
surOuverte = false;
var fini = false;
function terminer() {
if (fini) return;
fini = true;
surEl.removeEventListener("transitionend", surFin);
surEl.hidden = true; // l'iframe reste chargé, juste masqué
}
function surFin(ev) { if (ev.propertyName === "width") terminer(); }
surEl.addEventListener("transitionend", surFin);
setTimeout(terminer, 380); // filet si transitionend ne vient pas
surEl.style.width = largeurPanneau() + "px";
}
function modeSidebarConsole() { retrecirConsole(); }
function fermerAgentConsole() { fermer(); retrecirConsole(); }
/* ── Thème : suivre l'app hôte ─────────────────────────────── */
function themeHote() {
var t = document.documentElement.getAttribute("data-theme");
@@ -57,6 +185,7 @@
}
function appliquerTheme() {
if (root) root.setAttribute("data-draco-theme", themeHote());
if (surEl) surEl.setAttribute("data-draco-theme", themeHote());
}
function surveillerTheme() {
try {
@@ -122,6 +251,7 @@
' <span class="d-titre">Discussion principale</span>' +
' <button type="button" class="d-btn" data-disc="liste">☰ Discussions</button>' +
' <button type="button" class="d-btn" data-disc="nouvelle">+ Nouvelle</button>' +
' <button type="button" class="d-btn" data-disc="console" title="Ouvrir la console Spark (agent.dracodev.net) en pleine page par-dessus l\'application, sur cette discussion">Console ⤢</button>' +
'</div>' +
'<div class="draco-discussions" hidden></div>' +
'<div class="draco-panneau-outils">' +
@@ -145,6 +275,7 @@
root.querySelector(".fermer").addEventListener("click", fermer);
root.querySelector('[data-disc="liste"]').addEventListener("click", basculerTiroir);
root.querySelector('[data-disc="nouvelle"]').addEventListener("click", nouvelleDiscussion);
root.querySelector('[data-disc="console"]').addEventListener("click", ouvrirConsole);
root.querySelector('[data-outil="aujourdhui"]').addEventListener("click", chargerAujourdhui);
root.querySelector('[data-outil="approbations"]').addEventListener("click", chargerApprobations);
root.querySelector('[data-outil="missions"]').addEventListener("click", ouvrirMissions);
@@ -155,7 +286,13 @@
appliquerLargeur();
window.addEventListener("resize", appliquerLargeur);
document.addEventListener("keydown", function (ev) {
if (ev.key === "Escape" && root.classList.contains("open")) fermer();
if (ev.key !== "Escape") return;
/* Surimpression ouverte : Échap = retour au panneau seul
* (« Mode sidebar »). Quand le focus est dans l'iframe de la
* console (autre origine), l'événement ne remonte pas
* jusqu'ici : il ne peut donc pas fermer par surprise. */
if (surOuverte) { modeSidebarConsole(); return; }
if (root.classList.contains("open")) fermer();
});
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "obsigate",
"version": "2.76.0",
"version": "2.77.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.",
"main": "patch.js",
"directories": {
+39
View File
@@ -58,6 +58,45 @@ def test_panneau_spark_integre_218():
assert "https://agent.dracodev.net" in panneau
def test_panneau_bouton_console_221():
# #221 : le panneau Spark (asset vendangé, octet-identique dans
# les 4 apps) porte un bouton « Console » qui mène à la console
# web du service, sur la discussion courante (?disc=) et avec
# l'app d'origine (?app= déduite du sous-domaine : og → obsigate).
# Révisé par #222 : l'ouverture en nouvel onglet a disparu (la
# console s'ouvre en surimpression, voir le test #222) ; le lien
# profond, lui, subsiste via urlConsole().
panneau = (FRONTEND / "draco-panneau.js").read_text(encoding="utf-8")
assert 'data-disc="console"' in panneau
assert "ouvrirConsole" in panneau
assert "urlConsole" in panneau
assert '"disc=" + encodeURIComponent(id)' in panneau
assert '"app=" + encodeURIComponent(app)' in panneau
assert 'og: "obsigate"' in panneau
assert 'window.open(' not in panneau
def test_panneau_console_surimpression_222():
# #222 : le bouton « Console ⤢ » ouvre la console dans la même
# page, en surimpression née à la largeur du panneau et étendue
# à toute la fenêtre (transition sur la largeur), avec deux
# sorties : « Mode sidebar » et « Fermer l'agent ». L'URL de
# l'iframe garde le lien profond de #221 (urlConsole()).
panneau = (FRONTEND / "draco-panneau.js").read_text(encoding="utf-8")
assert "Console ⤢" in panneau
assert "draco-console-sur" in panneau
assert "assurerSurimpression" in panneau
assert "draco-console-styles" in panneau
assert 'surCadre.src = urlConsole()' in panneau
assert 'surEl.style.width = "100%"' in panneau
assert 'data-dc="sidebar"' in panneau
assert "Mode sidebar" in panneau
assert "modeSidebarConsole" in panneau
assert 'data-dc="fermer"' in panneau
assert "Fermer l'agent" in panneau
assert "fermerAgentConsole" in panneau
def test_csp_ouvre_le_service_d_agents_218():
# Le panneau parle au service d'agents : la CSP doit nommer son
# hôte en connect-src, sans ouvrir de générique supplémentaire.
+27
View File
@@ -72,3 +72,30 @@ def test_security_headers_present(client):
csp = resp.headers.get("content-security-policy", "")
assert "object-src 'none'" in csp
assert "frame-ancestors 'self'" in csp
def _csp_directives(csp: str) -> dict[str, list[str]]:
"""Découpe une politique CSP en ``{directive: [sources]}``."""
directives: dict[str, list[str]] = {}
for part in csp.split(";"):
tokens = part.split()
if tokens:
directives[tokens[0]] = tokens[1:]
return directives
def test_csp_frame_src_allows_agent_console(client):
"""BUG-119 : le « mode console » du panneau Spark charge la console du
service d'agents (``https://agent.dracodev.net``) dans une iframe —
``frame-src`` doit autoriser cette origine, sinon le navigateur bloque
le chargement (« This content is blocked »). Les sources historiques
(``'self'`` et ``blob:``, visualiseur PDF) restent en place et aucune
autre origine n'est admise."""
resp = client.get("/api/health")
csp = resp.headers.get("content-security-policy", "")
directives = _csp_directives(csp)
assert directives.get("frame-src") == [
"'self'",
"blob:",
"https://agent.dracodev.net",
]