Compare commits

...
Author SHA1 Message Date
Spark 8bba621223 Correctif : origine ShaarIt en connect-src (CSP) — la recherche fédérée passe
FlowDeck CI / lint (push) Failing after 13s
FlowDeck CI / test (pull_request) Failing after 1s
FlowDeck CI / docker (pull_request) Skipped
FlowDeck CI / lint (pull_request) Failing after 12s
FlowDeck CI / test (push) Failing after 19m33s
FlowDeck CI / docker (push) Skipped
La liste des sœurs du connect-src (Fusion #69, panneau Alertes)
n'avait pas été étendue à ShaarIt lors du branchement de la
recherche fédérée (#111) : le navigateur bloquait l'appel de la
page /recherche vers l'API ShaarIt avant même le CORS, et la
section affichait « Injoignable depuis cet hôte (CORS ou réseau) »
alors que le CORS de ShaarIt était correct. Test CSP ajouté.
2026-11-04 18:42:57 +00:00
bruno da5000b727 Merge pull request '7.84.11 — recherche fédérée : ShaarIt, 5e source de « Rechercher partout »' (#46) from recherche-shaarit into main
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-11-04 12:11:07 -05:00
Spark 3539611aa7 7.84.11 — recherche fédérée : ShaarIt, 5e source de « Rechercher partout »
FlowDeck CI / lint (push) Failing after 9s
FlowDeck CI / lint (pull_request) Failing after 4s
FlowDeck CI / test (pull_request) Failing after 1s
FlowDeck CI / docker (pull_request) Skipped
FlowDeck CI / test (push) Failing after 29m57s
FlowDeck CI / docker (push) Skipped
- static/js/recherche.js re-vendangé depuis commons v0.2.1 (source
  canonique ui/recherche/) : source ShaarIt (bases, onglet,
  normaliseur — lien profond {base}shaare/<shorturl>).
- Gabarit + route /recherche : data-base-shaarit (bases.shaarit),
  onglet et section « ShaarIt — bookmarks ».
- recherche_cors : origines ShaarIt prod/dev + clé sœur shaarit.
- Tests : recherche.test.js 83 assertions (5 sections, normaliseur
  ShaarIt), onglets Python et allowlist (12 origines) à jour.
2026-11-04 17:10:30 +00:00
bruno 1f82c2653a Merge pull request 'Flowdeck 7.84.10 : barre v0.2.0 — onglet ShaarIt + garde data-theme' (#45) from barre-v020-7-84-10 into main
FlowDeck CI / test (push) Failing after 1s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (push) Failing after 13s
2026-11-04 11:16:39 -05:00
bruno e24b2a9495 Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (CHANGELOG.md)
FlowDeck CI / test (push) Failing after 3s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (push) Failing after 10s
FlowDeck CI / test (pull_request) Failing after 1s
FlowDeck CI / docker (pull_request) Skipped
FlowDeck CI / lint (pull_request) Failing after 14s
2026-11-04 03:44:01 +00:00
bruno 73cd12d9de Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (VERSION) 2026-11-04 03:44:01 +00:00
bruno d113ce8c5f Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (tests/test_draco_urls_devprod.py) 2026-11-04 03:44:01 +00:00
bruno 4b1888eebb Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (tests/test_draco_bar_texte.py) 2026-11-04 03:44:01 +00:00
bruno 1e733f3662 Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (.env.example) 2026-11-04 03:44:01 +00:00
bruno 6eccacd982 Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (app/config.py) 2026-11-04 03:44:01 +00:00
bruno 5508d21e0a Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (app/services/draco_urls.py) 2026-11-04 03:44:01 +00:00
bruno 49bc6ca54a Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (app/templates/_draco_bar.html) 2026-11-04 03:44:01 +00:00
bruno b58172dcdd Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (static/img/logos/shaarit.svg) 2026-11-04 03:44:01 +00:00
bruno 4fdfc0c385 Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (static/js/vendor/draco-barre.js) 2026-11-04 03:44:01 +00:00
bruno e2886ccc95 Flowdeck 7.84.10 : barre v0.2.0, onglet ShaarIt + garde data-theme (static/css/draco-barre.css) 2026-11-04 03:44:01 +00:00
bruno 427df2ca92 Merge pull request 'Flowdeck 7.84.9 : barre v0.1.11 — marque monochrome, anneau rouge avatar' (#44) from barre-v0111-7-84-9 into main
FlowDeck CI / lint (push) Failing after 0s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-10-29 23:09:35 -04:00
bruno 1a717ede16 Flowdeck 7.84.9 : étoile Spark monochrome pour la marque
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (pull_request) Failing after 1s
FlowDeck CI / test (pull_request) Failing after 1s
FlowDeck CI / docker (pull_request) Skipped
2026-10-29 23:09:19 -04:00
bruno d3abeb197a Flowdeck 7.84.9 : barre v0.1.11, marque monochrome, anneau avatar (CHANGELOG.md)
FlowDeck CI / lint (push) Failing after 0s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-10-29 23:09:17 -04:00
bruno dee5fb5752 Flowdeck 7.84.9 : barre v0.1.11, marque monochrome, anneau avatar (VERSION)
FlowDeck CI / lint (push) Failing after 0s
FlowDeck CI / test (push) Failing after 1s
FlowDeck CI / docker (push) Skipped
2026-10-29 23:09:15 -04:00
bruno 9de1166661 Flowdeck 7.84.9 : barre v0.1.11, marque monochrome, anneau avatar (static/css/draco-barre.css)
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (push) Failing after 1m54s
2026-10-29 23:09:13 -04:00
bruno c0add7b6de Flowdeck 7.84.9 : barre v0.1.11, marque monochrome, anneau avatar (app/templates/_draco_bar.html)
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / test (push) Failing after 1s
FlowDeck CI / docker (push) Skipped
2026-10-29 23:09:12 -04:00
bruno d29986e51d Merge pull request 'Flowdeck 7.84.8 : barre v0.1.10, rouge Spark, avatar flottant, Alertes cloche' (#43) from barre-rouge-spark-7-84-8 into main
FlowDeck CI / test (push) Failing after 1s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (push) Failing after 1m52s
2026-10-29 22:55:18 -04:00
bruno cd8969a3a2 Flowdeck 7.84.8 : avatar Spark flottant (commons v0.1.10)
FlowDeck CI / lint (push) Failing after 0s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (pull_request) Failing after 1s
FlowDeck CI / test (pull_request) Failing after 0s
FlowDeck CI / docker (pull_request) Skipped
2026-10-29 22:55:00 -04:00
bruno e8cbd8e0f3 Flowdeck 7.84.8 : barre v0.1.10, rouge Spark, avatar flottant, Alertes cloche (CHANGELOG.md)
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-10-29 22:54:57 -04:00
bruno d40044e0c4 Flowdeck 7.84.8 : barre v0.1.10, rouge Spark, avatar flottant, Alertes cloche (VERSION)
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-10-29 22:54:56 -04:00
bruno 41865c702a Flowdeck 7.84.8 : barre v0.1.10, rouge Spark, avatar flottant, Alertes cloche (static/css/draco-barre.css)
FlowDeck CI / lint (push) Failing after 0s
FlowDeck CI / test (push) Failing after 0s
FlowDeck CI / docker (push) Skipped
2026-10-29 22:54:54 -04:00
bruno 56b8ff2072 Flowdeck 7.84.8 : barre v0.1.10, rouge Spark, avatar flottant, Alertes cloche (app/templates/_draco_bar.html)
FlowDeck CI / lint (push) Failing after 4m22s
FlowDeck CI / test (push) Failing after 3h8m21s
FlowDeck CI / docker (push) Skipped
2026-10-29 22:54:53 -04:00
22 changed files with 298 additions and 57 deletions
+1
View File
@@ -38,6 +38,7 @@ OBSIGATE_CONSUMER_TOKEN=
DRACO_AGENT_URL= # Service d'agents Spark — défaut https://agent.dracodev.net ; dev : https://agent-dev.dracodev.net
TIKTAK_URL= # TikTak (preset MCP, barre, refs) — défaut https://tiktak.dracodev.net ; dev : https://tiktak-dev.dracodev.net
KANBANTE_URL= # Kanbante (barre, refs) — défaut https://kanbante.dracodev.net ; dev : https://kanbante-dev.dracodev.net
SHAARIT_URL= # ShaarIt (barre v0.2.0) — défaut https://shaarit.dracodev.net ; dev : https://shaarit-dev.dracodev.net
FLOWDECK_URL= # URL publique de CETTE instance — défaut : dérivée d'OAUTH_REDIRECT_URI, sinon https://flowdeck.dracodev.net ; dev : https://flowdeck-dev.dracodev.net
# SSO draco (Fusion ACC-3.2) — secret partagé avec l'émettrice ObsiGate.
DRACO_SESSION_SECRET=
+31
View File
@@ -1,3 +1,34 @@
## v7.84.11 (2026-11-04) — Recherche fédérée : ShaarIt, 5e source de « Rechercher partout »
- **`static/js/recherche.js` re-vendangé depuis dracodev-commons v0.2.1** (source canonique `ui/recherche/`, contrat `contrats/recherche-federee.md`) : ShaarIt devient une source de la page `/recherche` comme les autres — bases prod/dev, onglet, normaliseur (lien profond canonique `{base}shaare/<shorturl>`, contexte « Bookmark · privé · tags », repli recherche native `/?q=`). Le fichier reste **strictement identique** chez les cinq hôtes.
- **Gabarit et route `/recherche`** : onglet et section « ShaarIt — bookmarks » ; la route expose désormais `bases.shaarit` (la clé existait dans `draco_urls.toutes()` depuis 7.84.10) et le gabarit rend `data-base-shaarit`.
- **CORS de recherche** (`app/middleware/recherche_cors.py`) : origines officielles ShaarIt prod et dev ajoutées aux listes explicites, `shaarit` ajoutée aux clés sœurs résolues — `GET /api/search` de Flowdeck devient lisible depuis les pages de ShaarIt, et réciproquement.
- Tests : `tests/recherche.test.js` porté à 5 sections (83 assertions, dont le normaliseur ShaarIt) ; `tests/test_recherche_onglets.py` (onglets attendus) et `tests/test_recherche.py` (allowlist à 12 origines) mis à jour. Fonds préexistant inchangé (`test_webhooks_crud`, échec confirmé sur main vierge).
## v7.84.10 (2026-11-03) — Barre commune v0.2.0 : onglet ShaarIt, garde data-theme
- **Barre d'apps v0.2.0 vendangée (dracodev-commons @70c837a)** : `static/css/draco-barre.css` et `static/js/vendor/draco-barre.js` remplacés verbatim par la v0.2.0 (hachages SHA-256 vérifiés contre la source) ; fragment `app/templates/_draco_bar.html` porté à la v0.2.0, le reste de son intégration Flowdeck (URL `draco_urls()`, `onAction`, nonce CSP, `asset_version`) est inchangé.
- **Onglet ShaarIt** : 5e application (bookmarks, réécriture Rust de Shaarli) — nouvel onglet `data-app="shaarit"` après Kanbante et avant l'onglet Console Spark, logo `static/img/logos/shaarit.svg` ajouté tel quel (copie verbatim de `ui/barre-apps/logos/` de commons). Le menu de la marque, construit depuis les onglets, l'inclut automatiquement.
- **URL ShaarIt par le même mécanisme que les autres** : `app/services/draco_urls.py` gagne la clé `shaarit` (défaut officiel `https://shaarit.dracodev.net` ; explicite `SHAARIT_URL` / `settings.shaarit_url` ; dérivation par l'hôte de la requête et par l'hôte de l'agent : `shaarit-dev` en dev) et `draco_urls().shaarit` alimente le href de l'onglet — aucune URL codée en dur hors des défauts officiels du résolveur.
- **⚠️ Changement de comportement visible — garde `data-theme`** : le CSS v0.2.0 préfixe ses blocs `prefers-color-scheme` par `html:not([data-theme])`. La copie v0.1.11 n'avait pas cette garde : c'est le **piège connu de Flowdeck** — système en mode sombre + `fd_theme` clair (`data-theme="light"` posé) ⇒ la barre s'affichait quand même en sombre. La re-vendange corrige ce point : la barre suit désormais le thème choisi dans l'app. Signalé en tête de la PR pour arbitrage à la fusion.
- **Cache** : le bump 7.84.9 → 7.84.10 change aussi `asset_version`, donc les navigateurs ne garderont pas l'ancienne barre.
## v7.84.9 (2026-10-29) — Barre commune : marque monochrome, anneau rouge avatar
- **Barre d'apps v0.1.11 vendangée (dracodev-commons)** : `static/css/draco-barre.css` remplacé verbatim par la v0.1.11 ; `static/js/vendor/draco-barre.js` inchangé (déjà identique à commons, JS inchangé en v0.1.11) ; fragment `app/templates/_draco_bar.html` porté à la v0.1.11, le reste de son intégration Flowdeck (URL `draco_urls()`, `onAction`, nonce CSP, `asset_version`) est inchangé.
- **Marque monochrome** : la marque n'est plus le logo rouge — c'est l'étoile Spark monochrome (`static/img/brand-spark.png`, copie de `ui/barre-apps/brand-spark.png` de commons, étoile blanche sur fond transparent, 26 px), blanche sur barre foncée et noire sur barre claire via le filtre du CSS vendangé. L'onglet agent et son entrée de menu GARDENT le logo rouge.
- **Anneau rouge avatar** : le bouton avatar flottant bas-centre gagne un anneau rouge `#c00000` (bordure 3 px du CSS vendangé, `var(--agent)`).
- **Thème** : la barre suit `data-theme` sur `<html>` (choix Flowdeck `fd_theme`, défaut light) — les règles thème de commons s'appliquent telles quelles, aucune surcharge propre n'a été ajoutée.
- **Cache** : le bump 7.84.8 → 7.84.9 change aussi `asset_version`, donc les navigateurs ne garderont pas l'ancienne barre.
## v7.84.8 (2026-10-29) — Barre commune : rouge Spark, avatar flottant, Alertes cloche seule
- **Barre d'apps v0.1.10 vendangée (dracodev-commons)** : `static/css/draco-barre.css` remplacé verbatim par la v0.1.10 ; `static/js/vendor/draco-barre.js` inchangé (déjà identique à commons entre v0.1.9 et v0.1.10) ; fragment `app/templates/_draco_bar.html` porté à la v0.1.10, le reste de son intégration Flowdeck (URL `draco_urls()`, `onAction`, nonce CSP, `asset_version`) est inchangé.
- **Rouge Spark** : `--agent` rose `#c2437e` → rouge `#c00000` (échantillonné dans le logo Spark) ; onglet agent `--app #c00000`, marque = logo Spark (26 px) au lieu du ✦.
- **Bouton Spark rose supprimé** de la barre, remplacé par le bouton avatar flottant bas-centre (cercle personnage Spark, `static/img/avatar-spark.png`, `data-act="spark"` → le panneau Spark de l'hôte, comme l'ancien bouton).
- **Alertes** : cloche seule, sans libellé — bouton moins large.
- **Cache** : le bump 7.84.7 → 7.84.8 change aussi `asset_version`, donc les navigateurs ne garderont pas l'ancienne barre.
## v7.84.7 (2026-10-29) — Barre commune : les 5 logos dans le menu et la barre
- **Barre d'apps v0.1.9 vendangée (dracodev-commons)** : `static/js/vendor/draco-barre.js` et `static/css/draco-barre.css` remplacés verbatim par la v0.1.9 (les fichiers vendangés précédents étaient identiques à l'octet près à ceux de commons avant la v0.1.9, aucune adaptation locale n'est perdue) ; fragment `app/templates/_draco_bar.html` porté à la v0.1.9, le reste de son intégration Flowdeck (URL `draco_urls()`, `onAction`, nonce CSP) est inchangé.
+1 -1
View File
@@ -1 +1 @@
7.84.7
7.84.12
+2
View File
@@ -156,6 +156,7 @@ class Settings(BaseSettings):
# DRACO_AGENT_URL=https://agent-dev.dracodev.net
# TIKTAK_URL=https://tiktak-dev.dracodev.net
# KANBANTE_URL=https://kanbante-dev.dracodev.net
# SHAARIT_URL=https://shaarit-dev.dracodev.net
# FLOWDECK_URL=https://flowdeck-dev.dracodev.net
draco_sso_url: str = "https://og.dracodev.net" # ObsiGate (émettrice SSO)
# ── Coffre ObsiGate, côté consommateur Flowdeck (Phase 4, issue #22) ──
@@ -168,6 +169,7 @@ class Settings(BaseSettings):
draco_agent_url: str = "https://agent.dracodev.net" # service d'agents (Spark)
tiktak_url: str = "https://tiktak.dracodev.net"
kanbante_url: str = "https://kanbante.dracodev.net"
shaarit_url: str = "https://shaarit.dracodev.net"
# URL publique de CETTE instance ; vide = dérivée de
# OAUTH_REDIRECT_URI, puis d'APP_BASE_URL (si non locale), puis du
# nom officiel.
+3 -1
View File
@@ -43,6 +43,7 @@ PROD_ORIGINS = frozenset({
"https://flowdeck.dracodev.net",
"https://tiktak.dracodev.net",
"https://kanbante.dracodev.net",
"https://shaarit.dracodev.net",
})
DEV_ORIGINS = frozenset({
@@ -50,6 +51,7 @@ DEV_ORIGINS = frozenset({
"https://flowdeck-dev.dracodev.net",
"https://tiktak-dev.dracodev.net",
"https://kanbante-dev.dracodev.net",
"https://shaarit-dev.dracodev.net",
})
AGENT_ORIGINS = frozenset({
@@ -59,7 +61,7 @@ AGENT_ORIGINS = frozenset({
# Clés de draco_urls.toutes() qui sont des applications sœurs.
_SOEUR_KEYS = ("obsigate", "flowdeck", "tiktak", "kanbante")
_SOEUR_KEYS = ("obsigate", "flowdeck", "tiktak", "kanbante", "shaarit")
def is_dev_host(hostname: str | None) -> bool:
+8 -1
View File
@@ -178,10 +178,17 @@ class ContentSecurityPolicyMiddleware(BaseHTTPMiddleware):
# notifications des applications soeurs depuis cet hôte ;
# leurs origines rejoignent connect-src (même résolution
# draco_urls que les liens de la barre et frame-src).
# Fusion #111 : la recherche fédérée appelle en plus
# l'API de ShaarIt depuis la page /recherche — sans son
# origine ici, le navigateur bloque l'appel et la section
# ShaarIt affichait « Injoignable depuis cet hôte (CORS ou
# réseau) » alors que le CORS de ShaarIt était correct
# (retour d'usage du 2026-11-04).
_toutes69 = draco_urls.toutes()
sisters = " ".join(sorted({
draco_urls.origine(_toutes69[k])
for k in ("obsigate", "flowdeck", "tiktak", "kanbante")
for k in ("obsigate", "flowdeck", "tiktak", "kanbante",
"shaarit")
} - {""}))
response.headers[self.CSP_HEADER] = self.CSP_VALUE.format(
nonce=nonce, host=host, agent=agent, sisters=sisters
+1 -1
View File
@@ -70,5 +70,5 @@ def recherche_page(request: Request, q: str = Query(default="")):
return HTMLResponse(template.render(
request=request, q=q.strip(), user=user,
bases={k: bases[k] for k in ("obsigate", "flowdeck", "tiktak",
"kanbante", "agent")},
"kanbante", "shaarit", "agent")},
))
+12 -3
View File
@@ -12,6 +12,7 @@ car il est passé par ``environment:``/``env_file:``) :
DRACO_AGENT_URL=https://agent-dev.dracodev.net
TIKTAK_URL=https://tiktak-dev.dracodev.net
KANBANTE_URL=https://kanbante-dev.dracodev.net
SHAARIT_URL=https://shaarit-dev.dracodev.net
FLOWDECK_URL=https://flowdeck-dev.dracodev.net (ou OAUTH_REDIRECT_URI)
Priorité de résolution, à chaque appel (les tests et les modules qui
@@ -19,15 +20,16 @@ lisaient déjà ``os.environ`` au moment de l'appel gardent ce
comportement) : variable d'environnement > ``app.config.settings`` >
défaut officiel. Aucun défaut ne contient ``-dev``.
**Dérivation d'environnement (v7.82.3)** — pour les quatre URL
sœurs (ObsiGate, Flowdeck, TikTak, Kanbante), entre la configuration
**Dérivation d'environnement (v7.82.3)** — pour les URL
sœurs (ObsiGate, Flowdeck, TikTak, Kanbante, et ShaarIt depuis la
barre v0.2.0), entre la configuration
explicite et le défaut officiel : si l'origine de l'agent telle que
l'app la connaît déjà (``AGENT_EVENEMENTS_URL``, via
``agent_evenements_url()``) a un hôte de la forme
``agent<SUFFIXE>.dracodev.net`` (SUFFIXE éventuellement vide,
typiquement ``-dev``), l'URL sœur est dérivée :
``https://<slug><SUFFIXE>.dracodev.net`` (slugs : ``og``,
``flowdeck``, ``tiktak``, ``kanbante``). Ainsi une installation dev
``flowdeck``, ``tiktak``, ``kanbante``, ``shaarit``). Ainsi une installation dev
qui ne déclare que l'agent pointe ses liens inter-apps en ``-dev``
sans quatre variables de plus. Si l'hôte de l'agent ne correspond
pas au motif (autre domaine, LAN, IP), **aucune dérivation** : on
@@ -94,6 +96,7 @@ OFFICIELLES = {
"agent": "https://agent.dracodev.net",
"tiktak": "https://tiktak.dracodev.net",
"kanbante": "https://kanbante.dracodev.net",
"shaarit": "https://shaarit.dracodev.net",
"flowdeck": "https://flowdeck.dracodev.net",
"gitea": "https://git.dracodev.net",
}
@@ -119,6 +122,7 @@ _SLUGS_SOEURS = {
"flowdeck": "flowdeck",
"tiktak": "tiktak",
"kanbante": "kanbante",
"shaarit": "shaarit",
}
# Hôte de l'agent : agent<SUFFIXE>.dracodev.net (SUFFIXE = ``-dev``…).
@@ -274,6 +278,10 @@ def kanbante_url() -> str:
return _resoudre_soeur("KANBANTE_URL", "kanbante_url", "kanbante")
def shaarit_url() -> str:
return _resoudre_soeur("SHAARIT_URL", "shaarit_url", "shaarit")
def gitea_url() -> str:
return _resoudre("GITEA_URL", "gitea_url", "gitea")
@@ -356,6 +364,7 @@ def toutes() -> dict[str, str]:
"agent": agent_url(),
"tiktak": tiktak_url(),
"kanbante": kanbante_url(),
"shaarit": shaarit_url(),
"flowdeck": flowdeck_url(),
"gitea": gitea_url(),
}
+34 -11
View File
@@ -1,7 +1,8 @@
{# Barre d'apps commune dracodev — Fusion ACC-1.1 (issue #17).
Snippet FIGÉ vendangé depuis Projets/dracodev-commons v0.1.9
(ui/barre-apps/) : ne pas modifier la structure ici ; toute évolution
passe par une nouvelle version de commons.
Snippet FIGÉ vendangé depuis Projets/dracodev-commons v0.2.0
(ui/barre-apps/, commit 70c837a de la branche
shaarit-phase3-barre-contrats) : ne pas modifier la structure ici ;
toute évolution passe par une nouvelle version de commons.
CSS/JS : /static/css/draco-barre.css, /static/js/vendor/draco-barre.js.
v7.82.0 (dev/prod) : les href des onglets ne sont plus figés —
l'hôte les injecte depuis sa config (draco_urls(), noms officiels
@@ -13,13 +14,35 @@
fournis, tels quels, servis depuis /static/img/logos/ (copie de
ui/barre-apps/logos/ de dracodev-commons) — jamais de glyphe
texte, jamais de redessin ; l'entrée « Console Spark » du menu
clone l'icône de l'onglet agent (draco-barre.js). #}
clone l'icône de l'onglet agent (draco-barre.js).
v0.1.10 (2026-10-29) : --agent rose → rouge Spark #c00000 ;
marque ✦ → logo Spark (26 px) ; bouton « Spark » rose SUPPRIMÉ
de la barre, remplacé par le bouton avatar flottant bas-centre
(static/img/avatar-spark.png, data-act="spark" → panneau de l'hôte) ;
Alertes = cloche seule, sans libellé. JS inchangé depuis v0.1.9.
v0.1.11 (2026-10-29) : marque = étoile Spark monochrome
(static/img/brand-spark.png, copie de ui/barre-apps/brand-spark.png
de commons — blanche sur barre foncée, noire sur barre claire
via le filtre du CSS vendangé) ; onglet agent et son entrée de
menu GARDENT le logo rouge ; anneau rouge #c00000 autour du
bouton avatar flottant (bordure du CSS vendangé). Thème de la
barre : data-theme sur <html> (fd_theme, défaut light) — les
règles thème de commons s'appliquent, aucune surcharge propre
n'est nécessaire.
v0.2.0 : 6e onglet ShaarIt (data-app="shaarit",
draco_urls().shaarit, logo static/img/logos/shaarit.svg copie
verbatim de commons), après Kanbante et avant l'onglet agent ;
le CSS vendangé gagne la GARDE data-theme : ses blocs
prefers-color-scheme sont préfixés html:not([data-theme]) —
c'est le correctif du piège connu chez Flowdeck (barre sombre
forcée quand le système est sombre mais que fd_theme = light) :
changement de comportement visible, signalé en tête de la PR. #}
<link rel="stylesheet" href="/static/css/draco-alertes.css?v={{ asset_version }}">
<header class="draco-bar" data-current="flowdeck">
<div class="draco-bar-zone draco-bar-gauche">
<button type="button" class="draco-icon-btn" data-act="rail" aria-label="Barre latérale">☰</button>
<a class="draco-brand" href="/" data-act="apps" aria-haspopup="true" aria-expanded="false" aria-label="dracodev — changer d&#39;application" title="Changer d&#39;application">
<span class="draco-brand-mark">✦</span><span class="draco-brand-name">dracodev</span>
<span class="draco-brand-mark"><img src="/static/img/brand-spark.png" alt="" width="26" height="26"></span><span class="draco-brand-name">dracodev</span>
</a>
<nav class="draco-apptabs" aria-label="Applications">
<a class="draco-apptab" data-app="obsigate" href="{{ draco_urls().obsigate }}/">
@@ -40,8 +63,12 @@
<span class="draco-apptab-txt"><strong>Kanbante</strong><small>Kanban</small></span>
<span class="draco-badge" data-badge="kanbante" hidden>0</span>
</a>
<a class="draco-apptab" data-app="shaarit" href="{{ draco_urls().shaarit }}/">
<span class="draco-apptab-ico" style="--app:#059669" data-icon="shaarit"><img src="/static/img/logos/shaarit.svg" alt="ShaarIt" width="30" height="30"></span>
<span class="draco-apptab-txt"><strong>ShaarIt</strong><small>Bookmarks</small></span>
</a>
<a class="draco-apptab" data-app="agent" href="{{ draco_urls().agent }}/">
<span class="draco-apptab-ico" style="--app:#d21f2c" data-icon="agent"><img src="/static/img/logos/spark.png" alt="Console Spark" width="30" height="30"></span>
<span class="draco-apptab-ico" style="--app:#c00000" data-icon="agent"><img src="/static/img/logos/spark.png" alt="Console Spark" width="30" height="30"></span>
<span class="draco-apptab-txt"><strong>Console Spark</strong><small>Agent commun</small></span>
</a>
</nav>
@@ -62,16 +89,12 @@
</div>
<button type="button" class="draco-alertes" data-act="alertes" aria-label="Alertes unifiées">
<svg viewBox="0 0 24 24" width="19" height="19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>
<span class="draco-alertes-txt">Alertes</span>
<span class="draco-badge" data-badge="alertes" hidden>0</span>
</button>
<button type="button" class="draco-spark" data-act="spark" aria-label="Ouvrir Spark, l'agent commun">
<span class="draco-spark-ico">✦</span><span class="draco-spark-txt">Spark</span>
<span class="draco-badge" data-badge="spark" hidden>0</span>
</button>
<button type="button" class="draco-avatar" data-act="compte" aria-label="Compte" aria-haspopup="menu" aria-expanded="false" title="{{ user.get('full_name') if user and user.get('full_name') else '' }}">{{ user.full_name[:1].upper() if user and user.get('full_name') else 'F' }}</button>
</div>
</header>
<button type="button" class="draco-spark-flottant" data-act="spark" aria-label="Ouvrir Spark, l'agent commun"><img src="/static/img/avatar-spark.png" alt="" width="64" height="64"></button>
<div class="draco-bar-rappel" data-act="rappel-entete" hidden>✦ dracodev — afficher l'en-tête</div>
<script nonce="{{ csp_nonce() }}">
/* Configuration Flowdeck de la barre commune (Fusion ACC-1.1).
+7 -1
View File
@@ -16,9 +16,10 @@
data-base-flowdeck="{{ bases.flowdeck }}"
data-base-tiktak="{{ bases.tiktak }}"
data-base-kanbante="{{ bases.kanbante }}"
data-base-shaarit="{{ bases.shaarit }}"
data-base-console="{{ bases.agent }}">
<h1>Rechercher partout</h1>
<p class="rp-hint">Notes ObsiGate, pages Flowdeck, tâches TikTak, cartes Kanbante — dans l'environnement courant (dev reste dev). « / » ou Ctrl+K pour rechercher, ↑/↓ puis Entrée pour ouvrir un résultat. « Tout voir » bascule vers l'onglet de l'application, sans quitter la page.</p>
<p class="rp-hint">Notes ObsiGate, pages Flowdeck, tâches TikTak, cartes Kanbante, bookmarks ShaarIt — dans l'environnement courant (dev reste dev). « / » ou Ctrl+K pour rechercher, ↑/↓ puis Entrée pour ouvrir un résultat. « Tout voir » bascule vers l'onglet de l'application, sans quitter la page.</p>
<div class="rp-form">
<input id="rp-q" class="rp-input" type="search" name="q" value="{{ q }}"
placeholder="Rechercher partout…" autocomplete="off" autofocus>
@@ -29,6 +30,7 @@
<button type="button" class="rp-tab" role="tab" aria-selected="false" tabindex="-1" data-rp-tab="flowdeck">Flowdeck <span class="rp-tab-count"></span></button>
<button type="button" class="rp-tab" role="tab" aria-selected="false" tabindex="-1" data-rp-tab="tiktak">TikTak <span class="rp-tab-count"></span></button>
<button type="button" class="rp-tab" role="tab" aria-selected="false" tabindex="-1" data-rp-tab="kanbante">Kanbante <span class="rp-tab-count"></span></button>
<button type="button" class="rp-tab" role="tab" aria-selected="false" tabindex="-1" data-rp-tab="shaarit">ShaarIt <span class="rp-tab-count"></span></button>
<button type="button" class="rp-tab" role="tab" aria-selected="false" tabindex="-1" data-rp-tab="console">Console Spark <span class="rp-tab-count"></span></button>
</nav>
<section class="rp-section" data-rp-section="obsigate">
@@ -47,6 +49,10 @@
<div class="rp-head"><h2>Kanbante — cartes</h2><span class="rp-count"></span><a class="rp-all" href="#">Tout voir</a></div>
<div class="rp-body"><p class="rp-state">Tapez une recherche ci-dessus.</p></div>
</section>
<section class="rp-section" data-rp-section="shaarit">
<div class="rp-head"><h2>ShaarIt — bookmarks</h2><span class="rp-count"></span><a class="rp-all" href="#">Tout voir</a></div>
<div class="rp-body"><p class="rp-state">Tapez une recherche ci-dessus.</p></div>
</section>
<section class="rp-section" data-rp-section="console">
<div class="rp-head"><h2>Console Spark — discussions</h2><span class="rp-count"></span><a class="rp-all" href="#">Tout voir</a></div>
<div class="rp-body"><p class="rp-state">Tapez une recherche ci-dessus.</p></div>
+47 -13
View File
@@ -7,14 +7,28 @@
menu des applications) ; l'avatar est un vrai <button>.
Correctif fusion #70 (SANS bump) : bouton trois points et menu par
défaut supprimés ; le menu de la marque gagne « Console Spark ».
v0.1.10 : rose --agent → rouge Spark #c00000 ; marque = logo Spark ;
bouton Spark supprimé (remplacé par l'avatar flottant bas-centre) ;
Alertes icône seule.
v0.1.11 : marque = étoile Spark monochrome (blanche barre foncée,
noire barre claire) ; anneau rouge #c00000 autour de l'avatar flottant.
v0.2.0 : nouvel onglet « ShaarIt » (data-app="shaarit", --app émeraude
#059669, logo logos/shaarit.svg via {{LOGO_SHAARIT}}) avant l'onglet
agent ; GARDE data-theme : les blocs prefers-color-scheme ne
s'appliquent que si <html> n'a AUCUN attribut data-theme
(html:not([data-theme])) — avant ce correctif, un hôte ayant choisi
le clair via data-theme="light" héritait quand même du sombre
système (piège constaté dans les copies vendangées de
Kanbante/Flowdeck ; les re-vendanges de la v0.2.0 le corrigent).
Jetons repris du prototype validé Console v7.2.
Thème : clair par défaut, sombre via [data-theme="dark"] sur <html>
ou prefers-color-scheme si l'app intégratrice ne force rien. */
ou prefers-color-scheme si l'app intégratrice ne force rien
(aucun attribut data-theme sur <html>). */
.draco-bar {
--bg: #f2f2f0; --surface: #ffffff; --surface-2: #e9eae6; --surface-3: #f6f6f3;
--ink: #1b1d1a; --ink-2: #5c615a; --ink-3: #787d75; --line: #dedfd9;
--agent: #c2437e; --danger: #cf4444;
--agent: #c00000; --danger: #cf4444;
position: relative;
height: 60px; box-sizing: border-box;
display: grid; grid-template-columns: auto minmax(200px, 520px) auto;
@@ -22,8 +36,12 @@
background: var(--surface); border-bottom: 1px solid var(--line);
font: 500 14px/1.3 system-ui, "Segoe UI", sans-serif; color: var(--ink);
}
/* Garde data-theme (v0.2.0) : le sombre système ne s'applique QUE si
* l'hôte n'a posé aucun data-theme sur <html>. Sans cette garde, un
* hôte en data-theme="light" sur un système sombre affichait la barre
* sombre malgré son choix explicite. */
@media (prefers-color-scheme: dark) {
.draco-bar { --bg:#101211; --surface:#1a1c1a; --surface-2:#242624; --surface-3:#20221f;
html:not([data-theme]) .draco-bar { --bg:#101211; --surface:#1a1c1a; --surface-2:#242624; --surface-3:#20221f;
--ink:#eceee9; --ink-2:#a6aba2; --ink-3:#8b9089; --line:#2e302c; }
}
[data-theme="dark"] .draco-bar { --bg:#101211; --surface:#1a1c1a; --surface-2:#242624; --surface-3:#20221f;
@@ -37,7 +55,15 @@
.draco-brand { display: flex; align-items: center; gap: 7px; color: var(--ink); text-decoration: none; font-weight: 800; cursor: pointer; border-radius: 10px; padding: 4px 6px; margin: -4px -6px; }
.draco-brand:hover { background: var(--surface-2); }
.draco-brand:focus-visible { outline: 2px solid var(--agent); outline-offset: 2px; }
.draco-brand-mark { color: var(--agent); font-size: 24px; line-height: 1; }
.draco-brand-mark { line-height: 0; }
/* Marque monochrome v0.1.11 : étoile blanche extraite du logo rouge,
fond transparent — noire sur barre claire (filtre), blanche sur
barre foncée (filtre annulé par les règles thème ci-dessous). */
.draco-brand-mark img { width: 26px; height: 26px; object-fit: cover; display: block; filter: brightness(0); }
@media (prefers-color-scheme: dark) {
html:not([data-theme]) .draco-bar .draco-brand-mark img { filter: none; }
}
[data-theme="dark"] .draco-bar .draco-brand-mark img { filter: none; }
/* Menu des applications — construit par le JS depuis les onglets */
.draco-apps-menu {
@@ -138,18 +164,23 @@
/* Alertes — la cloche ne disparaît JAMAIS (correctif v7.2) */
.draco-alertes {
position: relative; display: flex; align-items: center; gap: 7px; height: 38px;
padding: 0 11px; border-radius: 10px; border: 1px solid var(--line);
position: relative; display: flex; align-items: center; height: 38px;
padding: 0 9px; border-radius: 10px; border: 1px solid var(--line);
background: transparent; color: var(--ink); font: inherit; cursor: pointer;
}
/* Spark */
.draco-spark {
position: relative; display: flex; align-items: center; gap: 7px; height: 38px;
padding: 0 14px; border: 0; border-radius: 10px; cursor: pointer;
background: var(--agent); color: #fff; font: inherit; font-weight: 700;
/* Bouton avatar Spark flottant — bas-centre, ouvre le panneau Spark
de l'hôte via data-act="spark" (délégation globale existante).
La console Spark ne l'inclut pas : son fragment est propre. */
.draco-spark-flottant {
position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
z-index: 65; width: 64px; height: 64px; padding: 0; border: 0;
border-radius: 50%; background: transparent; cursor: pointer;
box-shadow: 0 8px 28px rgb(0 0 0 / .45);
}
.draco-spark .draco-badge { border-color: var(--agent); }
.draco-spark-flottant img { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; display: block; box-sizing: border-box; border: 3px solid var(--agent); }
.draco-spark-flottant:hover { transform: translateX(-50%) scale(1.05); }
.draco-spark-flottant:focus-visible { outline: 2px solid var(--agent); outline-offset: 3px; }
.draco-icon-btn {
width: 38px; height: 38px; border-radius: 10px; border: 1px solid transparent;
@@ -176,7 +207,7 @@
.draco-bar { grid-template-columns: auto minmax(200px, 440px) auto; }
}
@media (max-width: 1200px) {
.draco-search-txt, .draco-search kbd, .draco-alertes-txt, .draco-spark-txt { display: none; }
.draco-search-txt, .draco-search kbd { display: none; }
.draco-search { width: 38px; padding: 0; justify-content: center; }
.draco-bar { grid-template-columns: auto auto 1fr; }
.draco-bar-droite { justify-content: flex-end; }
@@ -200,6 +231,9 @@
.draco-bar[data-current="flowdeck"] .draco-apptab[data-app="flowdeck"],
.draco-bar[data-current="tiktak"] .draco-apptab[data-app="tiktak"],
.draco-bar[data-current="kanbante"] .draco-apptab[data-app="kanbante"],
/* v0.2.0 : sur ShaarIt elle-même (data-current="shaarit"), c'est
l'onglet ShaarIt qui reste visible en mobile. */
.draco-bar[data-current="shaarit"] .draco-apptab[data-app="shaarit"],
/* Chantier 5 : sur la console elle-même (data-current="agent",
programme console #71), c'est l'onglet Console Spark qui reste
visible en mobile — les 4 hôtes gardent leur propre courant. */
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<!-- ShaarIt — signet (bookmark) blanc sur fond émeraude #059669,
couleur signature de l'app dans la barre (#059669, distincte du
teal Kanbante #0d9488). Fond arrondi comme les logos PNG 180 px
des autres apps ; géométrie simple, aucun redessin d'un logo
existant : le signet EST le symbole de l'app (bookmarks). -->
<rect x="8" y="8" width="224" height="224" rx="48" fill="#059669"/>
<path d="M 82 52 L 158 52 L 158 190 L 120 152 L 82 190 Z" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 569 B

+39 -9
View File
@@ -2,11 +2,13 @@
* chantier 2+3 : onglets, « Tout voir », focus).
*
* Vanilla, sans dépendance. Un seul fichier vendangé tel quel par chaque
* hôte (ObsiGate, Flowdeck, TikTak, Kanbante) : la configuration vient
* des attributs data-* de #recherche, rendus côté serveur par l'hôte.
* hôte (ObsiGate, Flowdeck, TikTak, Kanbante, ShaarIt) : la configuration
* vient des attributs data-* de #recherche, rendus côté serveur par
* l'hôte. Source canonique : dracodev-commons, ui/recherche/
* (contrat : contrats/recherche-federee.md).
*
* Présentation en onglets : Tout, ObsiGate, Flowdeck, TikTak, Kanbante,
* Console Spark. « Tout » = les 4 sections groupées, chaque liste
* ShaarIt, Console Spark. « Tout » = les 5 sections groupées, chaque liste
* tronquée à PER_APP_LIMIT ; un onglet application = tous les résultats
* de cette application (aucune troncature côté client — la requête
* ObsiGate part avec limit=FULL_LIMIT, les autres API renvoient leur
@@ -46,6 +48,11 @@
* -> { groups: [{ full_name, rows: [{ issue, column }] }] }
* Lien profond : {base}board/<full_name> (la carte n'a pas d'URL propre
* adressable — même limite que la page /search native de Kanbante).
* - ShaarIt : GET {base}/api/search?q=
* -> { links: [{ id, title, url, description, tags, shorturl,
* private }] }
* Lien profond (contrat liens-profonds) : {base}shaare/<shorturl>.
* Recherche native adressable : {base}?q=<q> (page d'accueil).
* - Console Spark : GET {base-console}/api/v1/recherche?q=&limite=
* (agent-commun, authentifié par le cookie SSO draco_session) ->
* { q, domain, limite_par_type, resultats[], par_type,
@@ -87,8 +94,9 @@
* Authentification cross-origin : les appels sœurs partent avec
* credentials:"include". Le navigateur envoie alors les cookies de
* chaque origine, dont le cookie SSO partagé draco_session
* (Domain=.dracodev.net), accepté par les 4 API de recherche
* (ObsiGate et TikTak nativement, Flowdeck et Kanbante via #68)
* (Domain=.dracodev.net), accepté par les 5 API de recherche
* (ObsiGate et TikTak nativement, Flowdeck et Kanbante via #68,
* ShaarIt via la fédération v0.2.1 de commons)
* et par GET /api/v1/recherche de la console (identity() accepte
* draco_session). Chaque sœur n'autorise en CORS que les origines
* sœurs (GET sur l'endpoint de recherche uniquement, pas de "*") ;
@@ -109,12 +117,14 @@
flowdeck: "https://flowdeck.dracodev.net/",
tiktak: "https://tiktak.dracodev.net/",
kanbante: "https://kanbante.dracodev.net/",
shaarit: "https://shaarit.dracodev.net/",
};
var DEV = {
obsigate: "https://og-dev.dracodev.net/",
flowdeck: "https://flowdeck-dev.dracodev.net/",
tiktak: "https://tiktak-dev.dracodev.net/",
kanbante: "https://kanbante-dev.dracodev.net/",
shaarit: "https://shaarit-dev.dracodev.net/",
};
var PROD_AGENT = "https://agent.dracodev.net/";
var DEV_AGENT = "https://agent-dev.dracodev.net/";
@@ -125,6 +135,7 @@
{ key: "flowdeck", label: "Flowdeck" },
{ key: "tiktak", label: "TikTak" },
{ key: "kanbante", label: "Kanbante" },
{ key: "shaarit", label: "ShaarIt" },
];
var CONSOLE_APP = { key: "console", label: "Console Spark" };
var CONSOLE_TYPE_LABELS = {
@@ -145,7 +156,7 @@
planification: "/?section=planifie",
identite: "/?section=identites",
};
var TABS = ["all", "obsigate", "flowdeck", "tiktak", "kanbante", "console"];
var TABS = ["all", "obsigate", "flowdeck", "tiktak", "kanbante", "shaarit", "console"];
var COLUMN_LABELS = {
backlog: "Backlog",
in_progress: "En cours",
@@ -227,7 +238,7 @@
return TABS.indexOf(v) !== -1 ? v : "all";
}
/* Sections visibles pour un onglet. « Tout » = les 4 sections
/* Sections visibles pour un onglet. « Tout » = les 5 sections
* groupées (vue #68) ; la console n'y figure pas (voir en-tête). */
function visibleKeys(tab) {
tab = parseTab(tab);
@@ -256,7 +267,7 @@
* Chargement -> "…", ok/vide -> total ("0" honnête),
* erreur/indisponible/inactif -> "" (pas de faux 0).
* Tout : "…" tant qu'une section charge, sinon somme des totaux
* connus, "" si les 4 sections sont en erreur/inactives. */
* connus, "" si les 5 sections sont en erreur/inactives. */
function tabCounts(states) {
states = states || {};
var out = {};
@@ -367,6 +378,22 @@
};
});
}
if (app === "shaarit") {
return cut(data.links || []).map(function (l) {
var tags = (l.tags || []).filter(function (t) { return !!t; });
var ctx = "Bookmark";
if (l.private) ctx += " · privé";
if (tags.length) ctx += " · " + tags.join(", ");
return {
title: l.title || l.url || "Sans titre",
snippet: snippetOf(l.description || ""),
context: ctx,
/* Lien profond canonique (contrats/liens-profonds.md) ;
* sans shorturl, repli sur l'URL du bookmark lui-même. */
url: l.shorturl ? base + "shaare/" + l.shorturl : l.url || null,
};
});
}
return [];
}
@@ -455,13 +482,16 @@
if (app === "tiktak" || app === "kanbante") {
return base + "search?q=" + encodeURIComponent(q || "");
}
if (app === "shaarit") {
return base + "?q=" + encodeURIComponent(q || "");
}
/* ObsiGate et Flowdeck n'ont pas de page de recherche adressable :
* leur recherche native vit dans la palette (Ctrl+K) de l'app. */
return base;
}
function nativeSearchLabel(app) {
if (app === "tiktak" || app === "kanbante") {
if (app === "tiktak" || app === "kanbante" || app === "shaarit") {
return "Ouvrir la recherche dans " + appLabel(app);
}
return "Ouvrir " + appLabel(app) + " (recherche via la palette Ctrl+K)";
+18 -6
View File
@@ -8,7 +8,12 @@
* correctif fusion #70 (SANS bump de version, publication séparée) :
* bouton trois points et son menu par défaut SUPPRIMÉS, « Console Spark »
* ajoutée au menu de la marque, fermeture/focus corrigés (voir
* « Menu des applications », « Focus » et « Écouteurs globaux »).
* « Menu des applications », « Focus » et « Écouteurs globaux ») ;
* v0.2.0 : 5e application ShaarIt — clé urls.shaarit, défaut officiel
* https://shaarit.dracodev.net/, dérivation -dev shaarit-dev (même
* contrat que les autres apps), entrée APP_LABELS ; l'onglet
* (data-app="shaarit", après Kanbante, avant agent) est repris
* automatiquement par la construction du menu de la marque.
*
* Aucun gestionnaire inline : un seul écouteur délégué sur le document
* (patron validé par le prototype — les onclick du HTML généré sont
@@ -26,6 +31,7 @@
* flowdeck: "https://flowdeck.dracodev.net/",
* tiktak: "https://tiktak.dracodev.net/",
* kanbante: "https://kanbante.dracodev.net/",
* shaarit: "https://shaarit.dracodev.net/",
* agent: "https://agent.dracodev.net/",
* spark: "https://spark.dracodev.net/"
* },
@@ -54,10 +60,13 @@
* La clé spark ne correspond à aucun onglet de la barre : elle est
* résolue et exposée via DRACO_BAR.urls pour le panneau Spark
* (panneau-agent), qui suit le même contrat d'injection. La clé agent,
* elle, a un onglet depuis le chantier 5 (ordre : ObsiGate, Flowdeck,
* TikTak, Kanbante, Console Spark) : elle suit donc l'étape « href
* elle, a un onglet depuis le chantier 5 (ordre, v0.2.0 : ObsiGate,
* Flowdeck, TikTak, Kanbante, ShaarIt, Console Spark) : elle suit
* donc l'étape « href
* rendu » comme les autres onglets, en plus d'alimenter le menu de
* la marque et DRACO_BAR.urls.
* la marque et DRACO_BAR.urls. Même résolution pour shaarit (v0.2.0) :
* explicite > dérivation -dev shaarit-dev > href rendu
* ({{URL_SHAARIT}}) > défaut officiel.
* Actions émises (data-act) : rail, palette, domaine (data-dom),
* alertes, spark, rappel-entete. L'app intégratrice décide de chacune
* (ouvrir la palette, le panneau Spark, changer de domaine…).
@@ -144,6 +153,7 @@
flowdeck: "https://flowdeck.dracodev.net/",
tiktak: "https://tiktak.dracodev.net/",
kanbante: "https://kanbante.dracodev.net/",
shaarit: "https://shaarit.dracodev.net/",
agent: "https://agent.dracodev.net/",
spark: "https://spark.dracodev.net/"
};
@@ -157,12 +167,13 @@
flowdeck: "https://flowdeck-dev.dracodev.net/",
tiktak: "https://tiktak-dev.dracodev.net/",
kanbante: "https://kanbante-dev.dracodev.net/",
shaarit: "https://shaarit-dev.dracodev.net/",
agent: "https://agent-dev.dracodev.net/"
};
var APP_LABELS = {
obsigate: "ObsiGate", flowdeck: "Flowdeck",
tiktak: "TikTak", kanbante: "Kanbante"
tiktak: "TikTak", kanbante: "Kanbante", shaarit: "ShaarIt"
};
/* Vrai si hostname est un hôte -dev dracodev.net : première étiquette
@@ -365,7 +376,8 @@
if (badge) a.appendChild(badge.cloneNode(true));
appsMenu.appendChild(a);
});
/* Entrée « Console Spark » (#70) : après Kanbante, vers la console
/* Entrée « Console Spark » (#70) : après les onglets d'apps
* (ObsiGate…ShaarIt, v0.2.0), vers la console
* de l'agent commun (URL agent résolue — explicite > -dev >
* défaut prod ; aucun hôte spark-dev inventé). data-app="agent"
* l'intègre à refreshMenuHrefs/applyUrls/setCurrent comme les
+21 -5
View File
@@ -61,8 +61,18 @@ items = rp.normalize("kanbante", { groups: [{ full_name: "Projets/site", rows: [
ok(items[0].title === "#12 Bug" && items[0].url === "https://kanbante-dev.dracodev.net/board/Projets/site", "lien tableau Kanbante");
ok(items[0].context === "Projets/site · En cours", "contexte Kanbante");
/* normalize ShaarIt (commons v0.2.1) : lien profond shaare/<shorturl>,
* contexte privé + tags, repli sur l'URL du bookmark sans shorturl */
items = rp.normalize("shaarit", { links: [{ title: "Yandex", url: "https://yandex.com", description: "moteur", tags: ["recherche", "web"], shorturl: "xYz9", private: true }] }, "https://shaarit-dev.dracodev.net/", "recherche");
ok(items[0].title === "Yandex" && items[0].url === "https://shaarit-dev.dracodev.net/shaare/xYz9", "lien profond ShaarIt (shaare/<shorturl>)");
ok(items[0].context === "Bookmark · privé · recherche, web", "contexte ShaarIt (privé + tags)");
ok(items[0].snippet === "moteur", "extrait ShaarIt = description");
items = rp.normalize("shaarit", { links: [{ title: "", url: "https://exemple.fr", tags: [], shorturl: "" }] }, "https://shaarit.dracodev.net/", "x");
ok(items[0].url === "https://exemple.fr" && items[0].title === "https://exemple.fr", "ShaarIt sans shorturl : repli URL + titre");
/* nativeSearchUrl */
ok(rp.nativeSearchUrl("tiktak", "https://tiktak-dev.dracodev.net/", "q") === "https://tiktak-dev.dracodev.net/search?q=q", "repli TikTak");
ok(rp.nativeSearchUrl("shaarit", "https://shaarit-dev.dracodev.net/", "q") === "https://shaarit-dev.dracodev.net/?q=q", "repli ShaarIt = accueil ?q=");
ok(rp.nativeSearchUrl("obsigate", "https://og-dev.dracodev.net/", "q") === "https://og-dev.dracodev.net/", "repli ObsiGate = accueil (palette)");
@@ -70,15 +80,17 @@ ok(rp.nativeSearchUrl("obsigate", "https://og-dev.dracodev.net/", "q") === "http
/* parseTab */
ok(rp.parseTab("obsigate") === "obsigate", "onglet valide conservé");
ok(rp.parseTab("shaarit") === "shaarit", "onglet shaarit valide");
ok(rp.parseTab("console") === "console", "onglet console valide");
ok(rp.parseTab("bidon") === "all", "onglet inconnu -> Tout");
ok(rp.parseTab(undefined) === "all", "onglet absent -> Tout");
/* visibleKeys : Tout = les 4 sections groupées, sans la console */
ok(JSON.stringify(rp.visibleKeys("all")) === JSON.stringify(["obsigate", "flowdeck", "tiktak", "kanbante"]), "Tout = 4 sections, pas la console");
/* visibleKeys : Tout = les 5 sections groupées, sans la console */
ok(JSON.stringify(rp.visibleKeys("all")) === JSON.stringify(["obsigate", "flowdeck", "tiktak", "kanbante", "shaarit"]), "Tout = 5 sections, pas la console");
ok(JSON.stringify(rp.visibleKeys("tiktak")) === JSON.stringify(["tiktak"]), "onglet app = sa seule section");
ok(JSON.stringify(rp.visibleKeys("shaarit")) === JSON.stringify(["shaarit"]), "onglet shaarit = sa seule section");
ok(JSON.stringify(rp.visibleKeys("console")) === JSON.stringify(["console"]), "onglet console = sa seule section");
ok(JSON.stringify(rp.visibleKeys("bidon")) === JSON.stringify(["obsigate", "flowdeck", "tiktak", "kanbante"]), "onglet inconnu -> Tout");
ok(JSON.stringify(rp.visibleKeys("bidon")) === JSON.stringify(["obsigate", "flowdeck", "tiktak", "kanbante", "shaarit"]), "onglet inconnu -> Tout");
/* pageItems : troncature Tout (8), complet dans l'onglet de l'app */
let douze = Array.from({ length: 12 }, (_, i) => ({ title: "T" + i }));
@@ -91,9 +103,11 @@ let c = rp.tabCounts({
flowdeck: { status: "ok", total: 3 },
tiktak: { status: "empty", total: 0 },
kanbante: { status: "error", total: 0 },
shaarit: { status: "ok", total: 2 },
console: { status: "idle", total: 0 },
});
ok(c.obsigate === "…", "compteur … pendant le chargement");
ok(c.shaarit === "2", "compteur shaarit");
ok(c.flowdeck === "3" && c.tiktak === "0", "compteur total, vide honnête à 0");
ok(c.kanbante === "" && c.console === "", "erreur/inactif : pas de faux 0");
ok(c.all === "…", "Tout = … tant qu'une section charge");
@@ -102,18 +116,20 @@ c = rp.tabCounts({
flowdeck: { status: "ok", total: 2 },
tiktak: { status: "empty", total: 0 },
kanbante: { status: "error", total: 0 },
shaarit: { status: "ok", total: 1 },
console: { status: "ok", total: 4 },
});
ok(c.all === "7", "Tout = somme des totaux connus");
ok(c.all === "8", "Tout = somme des totaux connus (5 sections)");
ok(c.console === "4", "compteur console");
c = rp.tabCounts({
obsigate: { status: "error", total: 0 },
flowdeck: { status: "error", total: 0 },
tiktak: { status: "error", total: 0 },
kanbante: { status: "error", total: 0 },
shaarit: { status: "error", total: 0 },
console: { status: "idle", total: 0 },
});
ok(c.all === "", "Tout vide si les 4 sections sont en erreur");
ok(c.all === "", "Tout vide si les 5 sections sont en erreur");
/* buildSearchUrl : état onglet partageable */
ok(rp.buildSearchUrl("café", "tiktak") === "?q=caf%C3%A9&tab=tiktak", "URL q+onglet");
+20
View File
@@ -33,6 +33,26 @@ def test_aucun_texte_chantier_rendu():
assert rendu.lstrip().startswith("<link")
def test_barre_v020_onglet_shaarit_et_garde_data_theme():
"""Barre v0.2.0 (commons @70c837a) : onglet ShaarIt entre Kanbante
et l'onglet agent, logo fourni tel quel, CSS vendangé verbatim
avec la garde data-theme sur prefers-color-scheme."""
gabarit = (ROOT / "app/templates/_draco_bar.html").read_text()
rendu = _hors_commentaires(gabarit)
nav = rendu.split('<nav class="draco-apptabs"')[1].split("</nav>")[0]
assert nav.index('data-app="kanbante"') < nav.index('data-app="shaarit"')
assert nav.index('data-app="shaarit"') < nav.index('data-app="agent"')
assert 'href="{{ draco_urls().shaarit }}/"' in nav
assert 'data-icon="shaarit"><img src="/static/img/logos/shaarit.svg"' in nav
assert (ROOT / "static/img/logos/shaarit.svg").is_file()
css = (ROOT / "static/css/draco-barre.css").read_text()
assert "html:not([data-theme]) .draco-bar {" in css
assert "html:not([data-theme]) .draco-bar .draco-brand-mark img" in css
js = (ROOT / "static/js/vendor/draco-barre.js").read_text()
assert 'shaarit: "https://shaarit.dracodev.net/"' in js
assert 'shaarit: "https://shaarit-dev.dracodev.net/"' in js
def test_barre_intacte_apres_suppression():
"""Le 5e onglet Console Spark et la marque survivent a la suppression."""
gabarit = (ROOT / "app/templates/_draco_bar.html").read_text()
+19 -1
View File
@@ -12,7 +12,8 @@ import pytest
from app.services import draco_urls
VARS = ("DRACO_SSO_URL", "DRACO_AGENT_URL", "TIKTAK_URL", "KANBANTE_URL",
"FLOWDECK_URL", "OAUTH_REDIRECT_URI", "AGENT_EVENEMENTS_URL")
"SHAARIT_URL", "FLOWDECK_URL", "OAUTH_REDIRECT_URI",
"AGENT_EVENEMENTS_URL")
@pytest.fixture(autouse=True)
@@ -45,6 +46,23 @@ def test_surcharge_dev_par_env(monkeypatch):
assert urls["flowdeck"] == "https://flowdeck-dev.dracodev.net"
def test_shaarit_defaut_derivation_et_explicite(monkeypatch, avec_hote):
# Barre v0.2.0 : ShaarIt suit le même mécanisme que les autres
# sœurs — défaut officiel, dérivation -dev (agent connu puis
# hôte de la requête), explicite prioritaire.
assert draco_urls.shaarit_url() == "https://shaarit.dracodev.net"
assert draco_urls.toutes()["shaarit"] == "https://shaarit.dracodev.net"
monkeypatch.setenv(
"AGENT_EVENEMENTS_URL",
"https://agent-dev.dracodev.net/api/v1/evenements")
assert draco_urls.shaarit_url() == "https://shaarit-dev.dracodev.net"
monkeypatch.delenv("AGENT_EVENEMENTS_URL")
avec_hote("flowdeck-dev.dracodev.net")
assert draco_urls.shaarit_url() == "https://shaarit-dev.dracodev.net"
monkeypatch.setenv("SHAARIT_URL", "https://bm.example.net/")
assert draco_urls.shaarit_url() == "https://bm.example.net"
def test_flowdeck_url_derivée_oauth_redirect(monkeypatch):
monkeypatch.setenv(
"OAUTH_REDIRECT_URI", "https://flowdeck-dev.dracodev.net/auth/callback")
+5 -2
View File
@@ -38,17 +38,20 @@ def test_origin_of():
def test_sister_origins_dev():
# Correctif 2026-10-28 : 10 origines officielles (8 sœurs + 2 agent), prod ET dev,
# quelle que soit la résolution.
# Correctif 2026-10-28 : origines officielles prod ET dev, quelle
# que soit la résolution ; ShaarIt ajoutée par la fédération
# v0.2.1 (12 origines : 10 sœurs + 2 agent).
assert sister_origins(RESOLVED_DEV) == {
"https://og.dracodev.net",
"https://flowdeck.dracodev.net",
"https://tiktak.dracodev.net",
"https://kanbante.dracodev.net",
"https://shaarit.dracodev.net",
"https://og-dev.dracodev.net",
"https://flowdeck-dev.dracodev.net",
"https://tiktak-dev.dracodev.net",
"https://kanbante-dev.dracodev.net",
"https://shaarit-dev.dracodev.net",
"https://agent.dracodev.net",
"https://agent-dev.dracodev.net",
}
+17
View File
@@ -0,0 +1,17 @@
"""Recherche fédérée « Rechercher partout » (fusion #111) — garde-fous
côté Flowdeck : l'API /api/search et les origines sœurs sont couvertes
par les suites Node et les tests CORS ; ici, la CSP des pages, qui
conditionne les appels cross-origin de la page /recherche."""
def test_csp_connect_src_contient_shaarit(client):
"""La CSP des pages doit autoriser l'origine de ShaarIt en
connect-src, sinon le navigateur bloque l'appel de la page
/recherche avant même le CORS et la section ShaarIt affiche
« Injoignable depuis cet hôte » (retour d'usage du 2026-11-04 :
la liste des sœurs du connect-src, écrite pour les Alertes
#69, n'avait pas été étendue à ShaarIt)."""
r = client.get("/")
csp = r.headers.get("content-security-policy", "")
assert "connect-src" in csp
assert "https://shaarit.dracodev.net" in csp, csp
+3 -2
View File
@@ -40,14 +40,15 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
ONGLETS_ATTENDUS = ["all", "obsigate", "flowdeck", "tiktak", "kanbante",
"console"]
"shaarit", "console"]
def test_gabarit_recherche_onglets_complets():
gabarit = (ROOT / "app/templates/recherche.html").read_text()
trouves = re.findall(r'data-rp-tab="([^"]+)"', gabarit)
assert trouves == ONGLETS_ATTENDUS, trouves
for section in ("obsigate", "flowdeck", "tiktak", "kanbante", "console"):
for section in ("obsigate", "flowdeck", "tiktak", "kanbante", "shaarit",
"console"):
assert f'data-rp-section="{section}"' in gabarit
assert 'id="rp-q"' in gabarit and "autofocus" in gabarit