Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bba621223 | ||
|
|
da5000b727 | ||
|
|
3539611aa7 | ||
|
|
1f82c2653a | ||
|
|
e24b2a9495 | ||
|
|
73cd12d9de | ||
|
|
d113ce8c5f | ||
|
|
4b1888eebb | ||
|
|
1e733f3662 | ||
|
|
6eccacd982 | ||
|
|
5508d21e0a | ||
|
|
49bc6ca54a | ||
|
|
b58172dcdd | ||
|
|
4fdfc0c385 | ||
|
|
e2886ccc95 | ||
|
|
427df2ca92 | ||
|
|
1a717ede16 | ||
|
|
d3abeb197a | ||
|
|
dee5fb5752 | ||
|
|
9de1166661 | ||
|
|
c0add7b6de | ||
|
|
d29986e51d | ||
|
|
cd8969a3a2 | ||
|
|
e8cbd8e0f3 | ||
|
|
d40044e0c4 | ||
|
|
41865c702a | ||
|
|
56b8ff2072 |
@@ -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=
|
||||
|
||||
@@ -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é.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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(),
|
||||
}
|
||||
|
||||
@@ -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'application" title="Changer d'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).
|
||||
|
||||
@@ -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
@@ -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 |
@@ -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
@@ -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)";
|
||||
|
||||
Vendored
+18
-6
@@ -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
@@ -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");
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user