FlowDeck CI / lint (push) Successful in 43s
FlowDeck CI / test (push) Successful in 4m2s
FlowDeck CI / lint (pull_request) Successful in 42s
FlowDeck CI / test (pull_request) Successful in 4m3s
FlowDeck CI / docker (push) Successful in 1m2s
FlowDeck CI / docker (pull_request) Successful in 35s
tests/conftest.py: mutate the settings singleton (instead of rebinding) so DB + backup dir are isolated per test -> pytest-xdist safe. Real backup tests (snapshot/prune/admin API) and OAuth mock tests (Gitea/GitHub/link) replace the previous skips. init_db() now also creates webhook_subscriptions (full schema without the FastAPI lifespan). ruff check is clean; .eslintrc.json migrated to eslint.config.mjs (flat config). CI: lint job (ruff + eslint), parallel tests (-n auto), run on every branch push. VERSION 5.11.1.
1337 lines
83 KiB
Markdown
1337 lines
83 KiB
Markdown
# Changelog - FlowDeck
|
||
|
||
## v5.11.1 (2026-09-11) — v5.2.0 Infrastructure & Polish (complétion)
|
||
|
||
> Finalise le chantier v5.2.0 : la plupart des briques étaient déjà livrées
|
||
> (design tokens/components, API tokens, sessions, onboarding, backups, projets +
|
||
> sync, GitHubAdapter, Docker multi-stage) ; ce patch sécurise l'isolation des
|
||
> tests, active les tests parallèles et rend le linting vert.
|
||
|
||
- **Tests parallèles (pytest-xdist)** — `tests/conftest.py` **mute** désormais le
|
||
singleton `app.config.settings` au lieu de le remplacer. Les modules qui
|
||
importaient `settings` au chargement (ex. `app/services/backup.py`) gardaient
|
||
sinon les valeurs par défaut, ce qui rendait les tests backup instables. Chaque
|
||
test a une base SQLite temporaire + un dossier de backup dédiés → tests
|
||
parallèles lancés explicitement (`pytest -n auto` en local et en CI). La config
|
||
de base ne force plus `-n` (évite l'échec `unrecognized arguments: -n` si
|
||
`pytest-xdist` n'est pas installé).
|
||
- **Backups réellement testés** — les 2 tests précédemment `skip` (flaky) sont
|
||
remplacés par des tests réels : snapshot daté, `prune_old_backups`, `backup_due`
|
||
et API admin `/api/settings/backups`.
|
||
- **OAuth (mock)** — tests d'intégration complets dans `tests/test_v52_infra.py` :
|
||
redirect authorize → callback (Gitea + GitHub), mode `link` sur un compte local,
|
||
rejet d'un `state` invalide, construction des URLs d'autorisation.
|
||
- **Schéma complet hors lifespan** — `init_db()` crée aussi
|
||
`webhook_subscriptions` (auparavant uniquement dans le lifespan FastAPI).
|
||
- **Linting** — `ruff check app tests` passe sans erreur (corrections E701/E702,
|
||
B904, B007, E741, F821, F841, W293, UP031, E731 + config FastAPI pour B008) ;
|
||
migration de `.eslintrc.json` vers `eslint.config.mjs` (flat config, ESLint v9+).
|
||
- **CI** — nouveau job `lint` (ruff + eslint) ; tests exécutés en parallèle avec
|
||
couverture ; déclencheurs élargis à `develop`.
|
||
- **Version** — 5.11.1. **397 tests** verts.
|
||
|
||
## v5.11.0 (2026-09-11) — FlowDeck Agent : UX chat améliorée
|
||
|
||
> Quatre améliorations majeures du panneau Agent (v4.10.0 → v4.14.0) pour
|
||
> une expérience plus proche de Notion AI.
|
||
|
||
- **Auto-titre à chaque requête** — Le titre de la conversation se met à jour
|
||
après chaque échange avec l'IA (pas seulement à la création). Il reflète
|
||
le sujet courant et rend l'historique facile à parcourir.
|
||
- **Aperçu au survol / appui long** — Dans l'onglet Historique, un survol
|
||
souris (desktop) ou un appui long (mobile) ouvre un pop-up avec le premier
|
||
message utilisateur et la dernière réponse de l'agent, sans quitter la liste.
|
||
- **Normalisation des blocs `write_blocks`** — L'outil tolère désormais les
|
||
erreurs fréquentes du LLM : `text`→`content`, `heading`→`heading_1`…`heading_4`,
|
||
`bullet/list`→`bulleted_list`, génération d'ID, types inconnus→paragraphe.
|
||
Le script PowerShell (ou tout code) s'écrit correctement dans un bloc `code`.
|
||
- **Accordéon style Notion** — Le raisonnement de l'agent s'affiche dans un
|
||
composant pliable : « Pensées » (chevron, contenu complet au clic) et
|
||
« Actions » (lignes simples, passé, non-cliquables). Le tout reste visible
|
||
en permanence sous la réponse, sans encombrer l'écran.
|
||
- **Tests** — `test_write_blocks_normalizes_llm_blocks`,
|
||
`test_engine_titles_conversation_on_each_request`,
|
||
`test_suggest_title_strips_composer_prefixes`.
|
||
|
||
## v5.9.0 (2026-09-10) — AI Writing Assist (éditeur)
|
||
|
||
> L'IA Notion dans l'éditeur : commandes slash `/ai`, autocomplétion en ligne
|
||
> et remplissage automatique des propriétés d'une base, au-dessus du moteur
|
||
> FlowDeck Agent (v4.10.0) et de l'endpoint headless `/api/agent/generate`.
|
||
|
||
- **Service `app/services/ai_writing.py`** — `AIWritingService` sans outils,
|
||
six actions (`write`, `summarize`, `translate`, `continue`, `autocomplete`,
|
||
`properties`). Prompt système strict (Markdown léger, aucun préambule) et
|
||
replis déterministes hors-ligne (aucun appel réseau sans clé API).
|
||
- **Commandes slash IA** — nouveau groupe « AI » dans le menu `/` de
|
||
l'éditeur : **Write with AI**, **Summarize**, **Translate**, **Continue
|
||
writing**. `Write` ouvre le composeur inline existant ; `Summarize` et
|
||
`Continue` insèrent le résultat sous le bloc ; `Translate` remplace le
|
||
contenu de la page dans la langue demandée.
|
||
- **Autocomplétion** — module `AIAC` : après ~900 ms d'inactivité pendant la
|
||
frappe dans un bloc texte, une suggestion courte est proposée dans une
|
||
pastille « Tab » ancrée au bloc ; `Tab` l'insère, `Escape` la rejette.
|
||
- **AI properties** — bouton ✨ sur chaque ligne de la table database :
|
||
`POST /api/agent/writing/properties` propose des valeurs (Status, Priority,
|
||
Résumé…) à partir du titre/contenu, appliquées et persistées via l'API
|
||
collection existante.
|
||
- **Endpoints** — `POST /api/agent/writing` (action + prompt/contexte/langue/
|
||
préfixe) et `POST /api/agent/writing/properties` (suggestions structurées),
|
||
provider/modèle par utilisateur via les clés LLM existantes.
|
||
- **Tests** — `tests/test_ai_writing.py` (+29) : service hors-ligne, coercition
|
||
des valeurs, parsing JSON, routes et câblage des templates. Version 5.9.0.
|
||
|
||
## v5.5.0 (2026-09-08) — Partage : réorganisation & indicateurs
|
||
|
||
> Vue complète du partage : sidebar « Shared » scindée en « Par moi » /
|
||
> « Avec moi », onglet Shared de la bibliothèque filtrable, fenêtre Share
|
||
> rechargée à l'ouverture (bug de refresh), et emoji 👥 sur les fichiers
|
||
> partagés du workspace local.
|
||
|
||
- **Correctif fenêtre Share** — ouverture de la boîte de dialogue
|
||
(`toggleShareOpen`) au lieu de laisser la liste vide après un refresh ;
|
||
les partages sont rechargés (`loadShares`) à chaque ouverture.
|
||
- **Sidebar Shared** — la section affiche deux sous-groupes : « Par moi »
|
||
(partages nominatifs créés + pages en « lien ») et « Avec moi » (partages
|
||
reçus), chacun avec son propre état vide (les pages déjà dans « Par moi »
|
||
sont exclues de « Avec moi » pour éviter les doublons).
|
||
- **Bibliothèque** — onglet Shared doté d'un filtre « Tous / Par moi /
|
||
Avec moi » ; l'endpoint `GET /api/library/shared` accepte `dir=made|received|all`
|
||
et renseigne `share_dir` sur chaque élément.
|
||
- **Indicateur de partage** — la barre du document affiche « 👥 Shared »
|
||
au lieu de « 🔒 Share » dès que la page est partagée (membre, lien ou
|
||
publiée) ; état recalculé à la volée après chaque action de partage.
|
||
- **Workspace local** — les fichiers partagés affichent l'emoji 👥 juste
|
||
avant leur nom dans l'arborescence (`is_shared` exposé par
|
||
`/api/local-workspace/tree`).
|
||
- **Tests** (tests/test_sharing.py +6) : direction made/received/all de la
|
||
bibliothèque, fallback dir invalide, et rendu `pageIsShared` sur la page.
|
||
Suite complète 325 verte (+3 PDF pré-existants).
|
||
|
||
## v5.4.1 (2026-09-08) — Partage de page : correctif permissions + autocomplete membres
|
||
|
||
> Le partage à un membre existant échouait avec « Invalid permission. Use view,
|
||
> comment, or edit. » — le client envoyait `editor`/`commenter`/`viewer` alors
|
||
> que l'API n'accepte que `view`/`comment`/`edit`. Corrigé, et enrichi.
|
||
|
||
- **Correctif permissions** — défaut du champ d'invitation passé à `edit` ;
|
||
valeurs, libellés et surbrillance du menu de permission des participants
|
||
alignés sur le contrat API (`view`/`comment`/`edit`).
|
||
- **Mise à jour de permission persistée** — nouvel endpoint
|
||
`PUT /api/pages/{page_id}/share/{share_id}` ; le menu de permission d'un
|
||
participant enregistre désormais le changement (avant : visuel uniquement).
|
||
- **Autocomplete des membres existants** — en tapant dans le champ d'invitation,
|
||
recherche dans `/api/notifications/users/search?q=` (debounce 150 ms) avec
|
||
liste de suggestions (avatar couleur, login, nom complet), navigation
|
||
clavier ↑/↓/Entrée/Échap ; soi-même et membres déjà partagés exclus.
|
||
- **Partage lié au compte** — la sélection d'un membre dans les suggestions
|
||
envoie `user_id`, le partage est donc lié au compte (pas seulement à un
|
||
email) ; un email peut toujours être saisi librement.
|
||
- **Upsert anti-doublon** — partager un membre déjà présent met à jour sa
|
||
permission au lieu de créer une ligne dupliquée ; trim de l'email côté serveur.
|
||
- **Tests** (tests/test_sharing.py) : partage par user_id/email, permission
|
||
invalide rejetée, upsert, PUT permission, suppression avec unset is_shared,
|
||
erreurs d'auth. Suite complète 319 verte (+3 PDF pré-existants).
|
||
|
||
## v5.4.0 (2026-09-08) — Interactions de bloc
|
||
|
||
> Manipulation riche des blocs façon Notion : undo/redo, duplication, menu de
|
||
> bloc ⋮ (turn into, couleurs, lien vers le bloc, déplacer, supprimer),
|
||
> glisser-déposer multi-sélection et en-têtes de tableau configurables.
|
||
|
||
- **Undo/Redo** : pile d'historique (100 entrées) alimentée à chaque mutation
|
||
(frappe Entrée, retour arrière, slash, tableau, toggle, colonnes…), rappel
|
||
`Ctrl+Z` / `Ctrl+Shift+Z` ou `Ctrl+Y`, restauration avec re-focus du bloc.
|
||
- **Duplication de bloc** : `Ctrl+D`, menu bloc ou commande slash « Duplicate » ;
|
||
copie profonde (enfants columns/toggle inclus) insérée juste en dessous ;
|
||
en multi-sélection, le groupe entier est dupliqué.
|
||
- **Menu de bloc ⋮** sur la poignée (clic ou clic droit) : « Turn into »
|
||
(sous-menu conservant le contenu), Duplicate, Copy link to block
|
||
(`#fdblk-<id>`, scrolls/focus au chargement), Move to… (dialogue de
|
||
recherche des pages de l'arbre, déplacement du bloc via l'API), Delete,
|
||
couleur de texte et de fond appliquées à un bloc ou à la sélection.
|
||
- **Glisser-déposer multi-sélection** : `Shift+clic` sur la poignée ajoute un
|
||
bloc à la sélection ; déplacer un bloc sélectionné déplace tout le groupe
|
||
(reconstruction de l'ordre depuis le DOM via `data-id`).
|
||
- **Commandes slash étendues** : groupe « Actions » avec Duplicate, Turn
|
||
into…, Copy link to block et Delete block.
|
||
- **En-têtes de tableau** : toggles « Header row » / « First col » dans la
|
||
barre du tableau ; `has_header` (toujours actif par défaut) et nouveaux
|
||
`first_col_header` persistés et pris en compte dans les exports Markdown/HTML
|
||
(app/services/export.py) ainsi que que dans `blocksToMarkdown` client.
|
||
- **Intégration realtime** : méthode `syncNow`/`forceEmit` sur `window.__fdRT`
|
||
appelée après chaque mutation programmatique pour pousser immédiatement les
|
||
ops (undo, duplicate, drag, turn into, couleurs, déplacement, toggles).
|
||
- **Tests** (tests/test_block_interactions.py) : persistance des blocs avec
|
||
`has_header`/`first_col_header`/`style`, exports table Markdown/HTML,
|
||
endpoint « Move to » (get page + POST blocks sur la cible).
|
||
|
||
## v5.3.0 (2026-09-08) — Realtime : édition collaborative en direct
|
||
|
||
> Deux utilisateurs peuvent maintenant éditer une même page **en même temps**
|
||
> : les blocs, le titre, les curseurs et la présence se synchronisent via une
|
||
> connexion WebSocket, avec fusion last-write-wins et repli sur polling.
|
||
|
||
- **Passerelle WebSocket** `WS /ws/pages/{page_id}` (app/services/realtime_server.py
|
||
+ app/routers/realtime.py) : salons en mémoire par page, authentifiés via le
|
||
cookie de session (refus 4401), page introuvable/supprimée → 4404.
|
||
- **Protocole de synchronisation** : `hello`/`sync`/`op`/`ack`/`title`/`sel`/
|
||
`ping`/`peer_join`/`peer_leave` ; chaque page porte une version incrémentée à
|
||
chaque opération appliquée ; un client périmé reçoit un `sync` complet.
|
||
- **Merge last-write-wins par bloc** : insert, update (remplacement de bloc),
|
||
delete, move — appliqués dans l'ordre d'arrivée ; une frappe en cours dans un
|
||
bloc focalisé n'est jamais écrasée par une mise à jour distante.
|
||
- **Client éditeur** (app/templates/_page_editor_realtime.html) : diff local →
|
||
ops (update/delete/move/insert), envoi dès que l'utilisateur tape (debounce),
|
||
application des ops distantes avec re-render discret + re-focus du bloc actif.
|
||
- **Présence & curseurs** : avatars colorés dans la barre du haut et curseurs
|
||
de chaque pair positionnés dans le document (calque dédié, mise à jour à
|
||
l'édition/au scroll).
|
||
- **Titre synchronisé** : diffusion du titre (debounce) sans écraser un titre
|
||
en cours d'édition.
|
||
- **Fallback polling** : si le WebSocket est indisponible, rafraîchissement
|
||
toutes les 10 s de l'état serveur (adopté uniquement si pas de brouillon
|
||
local) avec reconnexion automatique.
|
||
- **Persistance** : écriture debounce (~1 s) de `content` + `title` en base,
|
||
flush immédiat à la déconnexion du dernier client.
|
||
- **Sécurité** : `connect-src` CSP étendu à `ws:` ; aucune donnée sensible
|
||
transmise (les peers n'exposent que id/login/full_name/couleur).
|
||
- **Tests** (tests/test_realtime.py) : auth, page absente, hello→sync, merge
|
||
LWW à deux clients + persistance, présence join/leave, curseurs, titre,
|
||
resync des clients périmés, `apply_op`/`merge_ops` unitaires.
|
||
|
||
## v5.2.0 (2026-09-07) - Automations : un moteur de règles (if-this-then-that)
|
||
|
||
> Nouveau moteur de règles : brancher des actions sur des événements de pages
|
||
> (création, mise à jour, suppression, déplacement), sur un planning cron ou
|
||
> sur un bouton cliquable directement dans l'éditeur.
|
||
|
||
- **Moteur de règles** : trigger (événement / cron / bouton), conditions
|
||
combinables (eq, neq, contains, is_empty, is_not_empty, changed) et actions
|
||
(webhook, changer une propriété, créer une page, notification).
|
||
- **Événements couverts** : collection créée/modifiée/supprimée, page
|
||
créée/modifiée/supprimée/déplacée (API et Canvas).
|
||
- **Déclencheur cron** : `*/N * * * *`, minute fixe, `@hourly`, `@daily`, avec
|
||
un planificateur en arrière-plan qui exécute les règles dues chaque minute.
|
||
- **Bouton d'automation** : nouveau bloc de type `button` dans l'éditeur
|
||
(menu slash) qui déclenche une règle au clic.
|
||
- **Interface** : panneau Automations dans les paramètres (créer, modifier,
|
||
activer/désactiver, lancer, supprimer, historique des exécutions).
|
||
- **Historique des exécutions** : chaque déclenchement est tracé (statut,
|
||
détail, page/collection concernée).
|
||
|
||
## v5.1.11 (2026-09-07) — Éditeur : collage intelligent (découpe en blocs)
|
||
|
||
> Coller un texte dans un bloc le plaçait en entier dans ce bloc, même s'il
|
||
> contenait plusieurs paragraphes, des listes, des titres markdown, du code ou
|
||
> un tableau.
|
||
|
||
- **Découpe automatique au collage** : le contenu collé est analysé
|
||
(`paste2b`) et réparti en plusieurs blocs selon son format (markdown ou
|
||
texte simple).
|
||
- Pris en charge : titres `#`→`####`, listes à puces (`-`, `*`, `+`), listes
|
||
numérotées, cases à cocher `- [ ]` / `- [x]` (y compris `[X]`), citations
|
||
`>` , séparateurs `---`/`***`, blocs de code (fences ```), tableaux
|
||
markdown et paragraphes.
|
||
- Texte simple : les lignes courtes (≤ 55 caractères) deviennent chacune un
|
||
bloc ; les blocs de texte longs (paragraphes) restent groupés ; une ligne
|
||
vide crée une nouvelle section.
|
||
- Comportement au curseur : le texte avant le curseur reste dans le bloc
|
||
courant (qui est réutilisé comme premier bloc si vide), puis les blocs
|
||
analysés sont insérés, et le texte après le curseur conserve sa propre
|
||
place (refocus sur le dernier bloc inséré).
|
||
- Les blocs non textuels (code, tableau, image, formules…) reçoivent toujours
|
||
le collage en texte brut, sans découpe.
|
||
|
||
## v5.1.10 (2026-09-07) — Espace local : boîte de renommage à la taille du nom
|
||
|
||
> Renommer un fichier en cliquant sur son nom ouvrait une boîte d'édition qui
|
||
> s'étendait sur toute la largeur de la ligne, et le bouton « Open » restait
|
||
> collé à droite du panneau.
|
||
|
||
- La boîte de renommage inline (`inline-rename-input`) est maintenant **large
|
||
comme le nom du fichier** : la largeur est mesurée à l'ouverture
|
||
(`_fitNameWidth`) avec un minimum de 60 px et est bornée à l'espace
|
||
disponible pour les très longs noms (le texte défile alors dans la boîte).
|
||
- Le bouton **Open** est placé **juste à droite de la boîte d'édition** (marge
|
||
6 px) au lieu du bord droit : il est déplacé à l'intérieur de la cellule
|
||
`.name.file`, devenue `inline-flex` (avec `overflow:hidden` + `max-width`),
|
||
pour que le bouton ne passe jamais à la ligne même en colonne étroite.
|
||
- S'applique aux deux rendus de l'arbre : la vue principale (x-for) et
|
||
`renderChildren` (dossiers imbriqués / arbre statique).
|
||
|
||
## v5.1.9 (2026-09-07) — Validation robuste des modèles Nvidia + logo Agent noir & blanc
|
||
|
||
> La liste Nvidia « diminuée » de la v5.1.8 contenait encore des modèles en
|
||
> erreur 404/410. Deux causes réelles : (1) le payload de validation ne
|
||
> correspondait pas à celui utilisé au runtime et un `payload` partagé entre
|
||
> les requêtes concurrentes envoyait des noms de modèles croisés (race) ;
|
||
> (2) rien ne protégeait l'usage si un modèle devenait indisponible après
|
||
> validation.
|
||
|
||
### Validation des modèles (fournisseur bruité)
|
||
- `_validate_chat_models()` utilise désormais le **payload de production**
|
||
(`messages` + `temperature: 0.2`, sans `max_tokens`) : un modèle qui répond
|
||
200 à la validation répond aussi 200 à l'usage réel.
|
||
- **Race fixée** : le payload est reconstruit pour chaque modèle (plus de nom
|
||
de modèle croisé entre requêtes concurrentes) — c'est pourquoi la v5.1.8
|
||
retombait par intermittence sur la liste brute non validée.
|
||
- Un seul payload partagé retiré ; retente **une fois** sur timeout/5xx (les
|
||
modèles lents mais fonctionnels survivent) ; rejette définitivement les 4xx
|
||
(404 modèle inconnu, 410 retiré/déprécié) ; garde les 429 (routés donc
|
||
utilisables).
|
||
|
||
### Repli automatique au runtime (model not found / gone)
|
||
- `LLMClient._http_complete` : si le modèle choisi répond **404/410**, retente
|
||
une fois avec le modèle par défaut du provider et ajoute un `notice`
|
||
expliquant le basculement — visible dans le panneau Agent (nouvel événement
|
||
SSE « notice », bandeau `.fd-ap-notice`, réinitialisé à chaque conversation).
|
||
- Le modèle réellement utilisé est persisté dans l'historique de conversation.
|
||
|
||
### Panneau Agent — logo noir & blanc thémé
|
||
- Le rond bleu en bas à droite (FAB) et le logo du header du panneau (à gauche
|
||
de « How can I help you today? ») passent de l'accent bleu + émoji 🤖 à un
|
||
rond noir/blanc qui suit le thème clair/sombre, avec une étincelle ✨
|
||
monochrome (SVG `currentColor` : rond `--text-primary`, étincelle
|
||
`--bg-primary`).
|
||
|
||
## v5.1.8 (2026-09-07) — Multi-lignes réellement conservées + modèles IA validés (Nvidia)
|
||
|
||
> Deux correctifs : le bug multi-lignes (v5.1.7 corrigeait le symptôme au
|
||
> re-rendu, mais la vraie cause était la lecture en `textContent` qui élimine
|
||
> les `<br>`) et, côté Agent, seuls les modèles réellement utilisables du
|
||
> fournisseur Nvidia sont désormais proposés dans la configuration IA.
|
||
|
||
### Éditeur — multilignes conservées (vraie cause)
|
||
- `sync()` lisait `el.textContent`, qui **supprime** les `<br>` : les sauts de
|
||
ligne étaient perdus dès la sauvegarde du bloc. Ajout de `gt()` (basé sur
|
||
`innerText`, qui rend bien les `<br>` en `\n`) utilisé par `sync()`, les
|
||
tableaux (toggle/columns) et le calcul de position du curseur.
|
||
- La scission sur Entrée (et la limite bas/haut de bloc) utilisait désormais
|
||
`splitCaret()` : un marqueur temporaire est inséré au curseur puis
|
||
`gt()`/`innerText` découpe le contenu en « avant » / « après », exactement
|
||
comme un vrai clavier. Fini le décalage `before` qui perdait la fin d'un
|
||
bloc multi-lignes.
|
||
- `_resultText` (composer IA) lit par `gt()` pour préserver les `\n`.
|
||
|
||
### Agent — validation des modèles (fournisseur bruité)
|
||
- `GET {base}/models` de Nvidia liste tout le catalogue, mais la plupart des
|
||
modèles (embeddings, rerank, génération d'images…) répondent **404** sur
|
||
`/v1/chat/completions` — l'erreur observée dans la config IA.
|
||
- `fetch_provider_models()` filtre désormais pour `nvidia` : suppression des
|
||
modèles manifestement non-chat (nom), puis « probe » réelle de chaque
|
||
candidat (`POST /chat/completions`, payload minimal, pipeline limité à 6 en
|
||
parallèle, 6 s par appel) — seuls les modèles qui répondent sont listés.
|
||
Un 429 (limite de débit) est conservé (le modèle est routé, donc utilisable).
|
||
- Filet de sécurité : si toutes les validations échouent, on garde la liste
|
||
filtrée par nom plutôt que de vider le sélecteur.
|
||
- Tests : `tests/test_llm_config.py` (5 cas) — catalogue mock, 404 chat,
|
||
retour sans validation pour les autres providers, chute de sécurité, offline.
|
||
|
||
## v5.1.7 (2026-09-07) — Retours à la ligne conservés quand on change de bloc (Entrée)
|
||
|
||
> Un bloc multi-lignes (Shift+Entrée) perdait ses retours à la ligne dès qu'on
|
||
> appuyait sur Entrée pour créer un nouveau bloc.
|
||
|
||
### Cause
|
||
- Shift+Entrée insère des `<br>` ; `sync()` lit `el.textContent`, qui contient
|
||
alors des `\n`. `mdEsc()` (render des blocs) échappait le texte mais ne
|
||
convertissait pas les `\n`, et le HTML les replie en espace → les lignes
|
||
disparaissaient au re-rendu après la scission Entrée.
|
||
|
||
### Correctif
|
||
- `mdEsc()` convertit désormais les `\n` restants en `<br>` (après les
|
||
remplacements markdown inline) : les retours à la ligne sont conservés dans
|
||
tous les types de bloc (paragraphe, listes, to-do, toggle, callout, code…).
|
||
|
||
> Conforme au comportement Notion : le titre affiche « New Page » quand il est
|
||
> vide ; les aides de blocs (« Press 'space' for AI… », « Heading 1 »…) ne
|
||
> s'affichent que lorsque le bloc a le curseur.
|
||
|
||
### Titre « New Page » (document vide)
|
||
- Le placeholder du titre utilisait `:empty` avec le texte figé « New page » et
|
||
la couleur très sombre `--text-dim`. `:empty` casse dès qu'un `<br>` est
|
||
injecté dans le contenteditable.
|
||
- Ajout de `data-placeholder="New Page"` et d'une bascule de classe `.empty`
|
||
(même mécanisme que les blocs) : le placeholder s'affiche en gris pâle
|
||
(`--text-secondary`, opacité .75) dès que le titre est vide, y compris après
|
||
effacement.
|
||
- `save()` stocke désormais un titre vide (au lieu de forcer « New page ») : le
|
||
placeholder persiste après autosave ; la sidebar garde son fallback d'affichage.
|
||
- `duplicatePage()` retombe sur « New Page » si le titre est vide.
|
||
|
||
### Aides de blocs affichées seulement au focus
|
||
- Les placeholders des blocs (« Press 'space' for AI or '/' for commands »,
|
||
« Heading 1 », « List »…) s'affichaient en permanence quand le bloc était vide.
|
||
- Les règles passent sous `:focus-within` : l'aide n'apparaît que lorsque le bloc
|
||
est focalisé (curseur dans le bloc) et disparaît dès qu'on le quitte.
|
||
- Même comportement pour l'aide « Type code... » du bloc code.
|
||
|
||
> Les textes d'aide « Press 'space' for AI or '/' for commands », « Heading 1 »,
|
||
> « List », etc. restaient affichés pendant l'édition.
|
||
|
||
### Cause
|
||
- La classe `.empty` (qui pilote les placeholders) n'était basculée qu'au
|
||
`render()`. Pendant la frappe, l'événement `input` mettait à jour `dirty` et
|
||
`autoSave` mais **jamais** `.empty` : le texte d'aide persistait sur un bloc qui
|
||
avait pourtant du contenu.
|
||
|
||
### Correctif
|
||
- Le listener `input` des blocs (rendu `render()`) bascule désormais `.empty` en
|
||
direct selon le contenu : dès qu'un caractère est saisi, `.empty` est retiré et
|
||
le placeholder disparaît ; quand le bloc est vidé, `.empty` revient et le
|
||
placeholder réapparaît (mécanisme identique pour les titres « Heading 1… »,
|
||
listes, callout, etc.).
|
||
|
||
> Résout les derniers retours UI de l'éditeur v5.1.2/5.1.3.
|
||
|
||
### Placeholder « Press 'space' for AI » — jamais affiché
|
||
- **Cause réelle** : la classe `.empty` est basculée sur l'élément interne
|
||
`[data-bid]` (sélecteur `[data-placeholder]` au render), mais la règle CSS
|
||
ciblait l'élément **externe** `.block-content` — deux éléments différents, donc
|
||
le `::before` d'aide ne se rendait **jamais**.
|
||
- La règle cible désormais `.block-content [data-bid].empty::before` (l'élément
|
||
qui porte réellement `data-placeholder` + `.empty`).
|
||
|
||
### Flèches ↑/↓ du menu `/`
|
||
- La navigation dépendait de l'endroit où se trouvait le focus (`_in` vs bloc vs
|
||
ailleurs) : `onKd` n'est lié qu'à `#_blocksCt`, l'input vit dans `#_slashMenu`
|
||
(un voisin), d'où un comportement fragile/incohérent.
|
||
- Ajout d'un écouteur `keydown` **au niveau document** (actif quand le menu est
|
||
ouvert) : ↑/↓/Entrée/Échap fonctionnent quel que soit le focus, plus de double
|
||
déclenchement.
|
||
- La sélection `.selected` passe en `!important` pour toujours gagner sur
|
||
`.slash-item:hover`, et l'élément sélectionné est auto-défilé (`scrollIntoView`).
|
||
|
||
### Item « vocabulaire / vocabulaire » du menu `/` mal positionné
|
||
- Les icônes texte `'lightbulb'` (Callout) et `'image'` (Image) étaient injectées
|
||
en clair dans la pastille d'icône, provoquant un texte décalé/débordant.
|
||
- Remplacées par les emojis `💡` (Callout) et `🖼️` (Image).
|
||
|
||
### Composer « Edit with AI » — largeur
|
||
- Le composer AI utilisait une largeur fixe `420px` ; il s'aligne désormais sur la
|
||
largeur du bloc ciblé (min 280px, max 1200px).
|
||
|
||
|
||
|
||
> Résout les régressions du composer AI et de l'éditeur Notion-style v5.1.2.
|
||
|
||
### AI inline — erreur 400 DeepSeek (espace → AI)
|
||
- `fdAgent.generate` passait par l'engine à outils (`/conversations/{id}/run`) qui
|
||
envoie un schéma `tools`/`tool_choice` que DeepSeek rejette en **400 Bad Request**.
|
||
- Bascule de `generate` sur l'endpoint **`/api/agent/generate`** (sans outils, prompt
|
||
propre + contexte) : le composer AI, « Ask AI », la note de réunion et la synthèse
|
||
renvoient désormais un simple texte Markdown. Le bloc « 400 » disparaît.
|
||
|
||
### Bloc vide (placeholder)
|
||
- Le hint « Press 'space' for AI or '/' for commands » était rendu trop discret
|
||
(`--text-dim`). Il passe à `--text-secondary` avec opacité, plus lisible.
|
||
(Le texte n'apparaît que sur un paragraphe réellement vide — comportement Notion.)
|
||
|
||
### Slash menu — flèches ↑/↓
|
||
- La sélection n'était **pas visible** au démarrage (rien de surligné) → les flèches
|
||
semblaient inactives. `_rs()` est désormais appelé à l'ouverture et après filtrage,
|
||
et l'item sélectionné reçoit un fond accentué + liseré gauche clair. Testé en
|
||
jsdom : ↓/↑ déplacent la sélection, Entrée applique l'élément.
|
||
|
||
### Contour du bloc sélectionné
|
||
- Suppression des anneaux de focus (`outline`/`box-shadow`) sur tous les blocs
|
||
éditables (`[data-bid]`, `.block-content`, titre de page) quand sélectionné.
|
||
|
||
### Poignée de déplacement (⠿)
|
||
- La poignée 6 points restait invisible (seule `.block-column-wrapper:hover` la
|
||
montrait). Ajout de `.block-wrapper:hover`/`:focus-within .block-handle { opacity:1 }`
|
||
pour les blocs normaux.
|
||
|
||
### Tests / validation
|
||
- 271 tests pytest verts ; rendu édition 200 (hint/AIC/trigger présents) ;
|
||
route `/api/agent/generate` enregistrée ; JS validé (`node --check` + jsdom).
|
||
|
||
> Adapte le visuel des blocs à l'expérience Notion AI : hint sur bloc vide, menu
|
||
> `/` compact avec raccourcis + aperçu au survol, et composer AI inline
|
||
> (« Edit with AI » → « Brewing… » → résultat encadré → « Insert below »).
|
||
|
||
### Bloc vide & commandes (Images 1 & 3)
|
||
- **Hint de bloc vide** : sur un paragraphe vide, le placeholder devient
|
||
« Press 'space' for AI or '/' for commands ».
|
||
- **Menu slash refait** : compact (icône + nom + raccourci `#`/`##`/`###`… à droite),
|
||
groupes renommés (« Basic blocks », « Media », « Data »), footer **« Close menu (esc) »**,
|
||
champ **« Type to search »** en bas, et **aperçu au survol** d'un item (carte
|
||
« Our Values » / exemple de rendu de bloc), positionnée à côté du menu.
|
||
|
||
### Composer AI inline (Images 2, 4, 5, 6)
|
||
- **Espace sur un bloc vide** ouvre le composer AI inline (pill « **Edit with AI** »).
|
||
- **Entrée** → l'agent génère : indicateur « **Brewing…** » animé (bouton ■ pour
|
||
stopper), puis **résultat encadré** (markdown rendu : titres, listes, gras…)
|
||
avec barre d'actions (👍/👎) et bouton **« Insert below »**.
|
||
- **Insert below** insère le contenu comme vrais blocs (via `applyAIBlocks` → `md2b`).
|
||
|
||
### Tests / validation
|
||
- 271 tests pytest verts (aucune régression) ; éditeur rendu 200 (hint, menu, AIC présents) ;
|
||
JS validé (`node --check`).
|
||
|
||
> L'agent FlowDeck et l'insertion dans les pages affichent désormais le markdown
|
||
> rendu (titre, gras, tableaux, code…) au lieu des caractères bruts `##`, `**`…
|
||
|
||
### Éditeur de page (blocs)
|
||
- **Insertion agent → vrais blocs** : `applyAIBlocks()` (et le mode *replace* de
|
||
`fdApplyDocument`) convertit la réponse markdown en blocs réels via `md2b()` :
|
||
`## titre` → bloc `heading_2`, tableaux GFM → bloc `table`, ` ``` ` → bloc `code`,
|
||
listes `-`/`1.`, to-do, citations, séparateurs. Plus de `## ` affiché littéralement.
|
||
- **Rendu inline** : les blocs de texte affichent désormais le markdown inline
|
||
(gras `**`, italique `*`, code `` ` ``, lien `[t](url)`, barré `~~`) sans les
|
||
caractères, via `mdEsc()` dans `renderBlock` (paragraphes, titres, listes, to-do,
|
||
toggle, citation, callout, code).
|
||
- Les raccourcis clavier markdown existants (`# ` + espace → H1…) restent inchangés.
|
||
|
||
### Panneau FlowDeck Agent
|
||
- **`renderMarkdown()`** (agent_panel.html) : les réponses de l'agent s'affichent en
|
||
HTML rendu — titres H1–H4, tableaux (GFM), blocs de code, listes à puces/numérotées
|
||
(+ cases), citations, séparateurs, gras/italique/code/lien/barré. HTML échappé
|
||
avant rendu (sécurité face au contenu LLM). CSS dédié (`.fd-md-*`, `pre`, tableaux).
|
||
- Le bouton « Insérer dans la page ouverte » continue d'utiliser `fdApplyDocument`
|
||
(maintenant → blocs réels).
|
||
|
||
### Tests / validation
|
||
- 271 tests pytest verts (aucune régression). Rendu markdown vérifié fonctionnellement
|
||
en Node (titres, tableau, code, listes) ; éditeur et panneau rendus 200 avec les
|
||
nouveaux composants présents.
|
||
|
||
> Livre la **v5.3.0** du roadmap : databases inline dans les pages (slash `/database`),
|
||
> galerie de **templates prédéfinis** (CRM, Project tracker, …) et **validation des
|
||
> propriétés** (required / unique / min / max) côté serveur + UI.
|
||
|
||
### Added — Inline databases (slash `/database`)
|
||
- Commande slash **`/database`** (groupe DATA) dans l'éditeur : ouvre un **sélecteur de
|
||
templates**, crée une database inline liée à la page et insère un bloc
|
||
`embed_type:'collection'` rendu par `FlowDeckDB.renderInto`.
|
||
- Le bouton « Database » de la barre *Get Started* ouvre désormais le même sélecteur
|
||
(au lieu de créer une database vide directement).
|
||
|
||
### Added — Templates de database prédéfinis
|
||
- **6 templates intégrés** seedés (migration v4) : Project tracker, CRM / Contacts,
|
||
Task list, Content calendar, Meeting notes, Reading list — avec icône, description et
|
||
schéma de propriétés.
|
||
- `app/services/db_templates.py` : `materialize_properties()` (schéma → `collection_properties`)
|
||
et `create_from_template()` (collection + propriétés + vue par défaut).
|
||
- `POST /db/api` et `POST /db/inline/api` acceptent `"template": "<nom>"` et matérialisent
|
||
les propriétés (titre exclu — c'est le titre de ligne). Colonne `icon` ajoutée à
|
||
`database_templates` (migration v4).
|
||
- `apply_db_template` (`/workspace/templates/database/{id}/apply`) matérialise les propriétés.
|
||
|
||
### Added — Validation des propriétés (required / unique / min / max)
|
||
- Nouvelle colonne `collection_properties.validation_json` (migration v4) ; le type/format
|
||
et les règles de validation sont portés par `validate_property_rule()` dans
|
||
`property_types.py` (required, unique, min, max, min_length, max_length).
|
||
- **Serveur** : `create_page_api` et `update_page_api` (`/db/{c}/pages/api`,
|
||
`/db/pages/{id}/api`) valident les valeurs avant écriture → 400 + message clair. La règle
|
||
`unique` exclut la page en cours d'édition. Les valeurs sont résolues par id **ou** par nom.
|
||
- **API propriétés** : `POST/PUT /db/{c}/properties/api` acceptent `validation` (persisté).
|
||
- **UI** (`_database_table_scripts.html`) : la modale « Add property » expose Required,
|
||
Unique, Min, Max ; en cas de 400 à l'édition d'une cellule, la cellule est marquée en rouge
|
||
et un toast affiche le message (valeur restaurée).
|
||
|
||
### Tests
|
||
- **271 tests** (+9 dans `tests/test_db_advanced.py`) : templates seedés, création de
|
||
collection/inline depuis template (propriétés matérialisées), apply template, validation
|
||
required/unique/min-max (create + update, exclusion de la ligne courante), persistance de
|
||
la config de validation, payload collection.
|
||
|
||
> Livre deux chantiers du roadmap v5 : la **migration au système de schéma versionné**
|
||
> (v5.2.0, priorité n°1 — débloque tout le reste) et la **Command Palette + recherche
|
||
> full-text** (v5.0.0).
|
||
|
||
### Added — Command palette (v5.0.0)
|
||
- **Palette de commandes `Ctrl+K` / `Ctrl+P`** dans `base.html` : modale centrée
|
||
autofocusée, navigation clavier (↑/↓/Enter/Esc), surlignage fuzzy des correspondances,
|
||
recherche universelle et **quick actions** (Nouvelle page, Library, My Tasks, Trash,
|
||
Settings, Help). Le bouton Search de la sidebar et `openQuickFind()` ouvrent désormais
|
||
la palette (fini le toast « Quick Find — Ctrl+K »).
|
||
- **Endpoint `GET /api/search?q=`** (`app/routers/search.py`) + service
|
||
`app/services/search.py` : recherche unifiée des **pages** (titre + contenu) et des
|
||
**databases** (nom + description), scopée aux workspaces accessibles de l'utilisateur.
|
||
- **Index full-text FTS5** (`pages_fts`, migration v3) avec triggers de synchronisation
|
||
(insert/update/delete) sur la table `pages` ; repli automatique sur `LIKE` si FTS5
|
||
absent. Exclusions automatiques des pages supprimées (corbeille).
|
||
|
||
### Added — Migrations versionnées (v5.2.0)
|
||
- **Runner léger `app/migrations.py`** (alternative à Alembic pour ce projet SQLite) :
|
||
table `schema_version` enregistrant la version appliquée ; le schéma de base existant
|
||
(`init_db`) devient la **baseline v1**, et chaque changement futur est une étape
|
||
versionnée appliquée une seule fois, dans l'ordre.
|
||
- **Migration v2 — indexes manquants** : `users(email)`, `user_oauth_tokens(user_id,
|
||
provider)`, `collections(workspace_id)`, `pages(workspace_id)`, `pages(deleted_at)`.
|
||
- **Migration v3 — FTS5** : `pages_fts` + triggers `pages_fts_ai/ad/au` + backfill.
|
||
- `init_db()` termine désormais par `apply_migrations(conn)` : nouveau schéma et bases
|
||
existantes convergent sans rejouer les migrations déjà appliquées.
|
||
|
||
### Tests
|
||
- **267 tests** (8 nouveaux dans `tests/test_search_migrations.py`) : suivi de la version
|
||
`schema_version`, indexes créés, table/triggers FTS5, synchronisation de l'index,
|
||
recherche pages+collections, exclusions de la corbeille, requête vide et sans résultat.
|
||
|
||
### Corrections
|
||
- **Test de connexion multi-provider** : le modèle configuré globalement (ex. `deepseek-v4-flash`)
|
||
n'est plus envoyé lorsqu'on teste un **autre** provider (ex. nvidia) — c'était la cause d'un
|
||
HTTP 404 « model not found ». Chaque provider utilise désormais son modèle par défaut tant que
|
||
le modèle global ne le concerne pas.
|
||
- **Presets NVIDIA actualisés** : les anciens modèles (`nvidia/llama-3.1-70b-instruct`,
|
||
`nvidia/nemotron-4-340b-instruct`) étaient retirés de la plateforme ou soumis à abonnement ;
|
||
liste remplacée par des modèles disponibles (`nvidia/nemotron-3-super-120b-a12b`,
|
||
`meta/llama-3.1-70b-instruct`, `deepseek-ai/deepseek-v4-pro`, `z-ai/glm-5.2`, …).
|
||
|
||
> Note : un 404 persistant après cette correction vient de NVIDIA lui-même (code
|
||
> « Function … : Not found for account ») — cause fréquente : le droit « Public API Endpoints »
|
||
> pas activé sur le compte/clé pour le modèle choisi.
|
||
|
||
## v4.15.4 (2026-09-06) — Correctifs listes agent (@ et /)
|
||
|
||
### Corrections
|
||
- **Menus « @ » vides** : le template Alpine du menu de mentions avait deux racines
|
||
(Alpine n'en accepte qu'une) — seuls les titres de sections s'affichaient, sans éléments.
|
||
Les éléments sont maintenant bien rendus sous chaque section.
|
||
- **Sélection clavier visible dans le menu « / »** : le changement de focus (↑/↓) n'était pas
|
||
visible faute de style `.focus` ; ajout d'un surlignage (fond + barre d'accent à gauche).
|
||
|
||
## v4.15.3 (2026-09-06) — Agent : mentions @ par espace courant + skills composés en arrière-plan
|
||
|
||
> La commande « @ » propose en priorité les **fichiers de l'espace de travail ouvert**, puis les
|
||
> autres contextes (collections, pages de collections, autres documents). La navigation clavier
|
||
> (↑/↓) positionne maintenant visuellement la sélection dans la fenêtre (défilement), et les
|
||
> fichiers identiques ne sont plus affichés plusieurs fois (déduplication par titre).
|
||
|
||
### Améliorations
|
||
- **Mentions @ contextuelles** : nouvelle API `/api/agent/mentions` par **sections** :
|
||
*Fichiers de l'espace courant* (« `workspace_id` transmis par le frontend »), *Collections / bases*,
|
||
*Pages de collections*, *Autres documents*.
|
||
- **Navigation clavier corrigée** : ↑/↓ font défiler la liste ouverte pour garder la sélection
|
||
visible à l'écran (aussi appliqué au menu `/`). Entrée/Tab sélectionne toujours l'élément focalisé.
|
||
- **Déduplication** : deux fichiers du même type et même titre (ex. « Sans titre », « Aussi Vide »)
|
||
n'apparaissent qu'une seule fois dans les propositions.
|
||
- **Skills composés en arrière-plan** : sélectionner un skill n'injecte plus son texte dans la boîte
|
||
d'édition. À l'envoi la requête est assemblée : `contexte1 + contexte2 + skill-1 + skill-2 + texte`.
|
||
Les skills intégrés (templace) et enregistrés (skill_ids) sont tous deux pris en compte ; les
|
||
contextes @ continuent d'être résolus en contenu côté serveur.
|
||
|
||
## v4.15.2 (2026-09-06) — Agent : skills multi-épinglés en bulles + chips de contexte cliquables
|
||
|
||
> Les skills s'épinglent désormais au contexte **comme les mentions @** (bulles « / » purples dans
|
||
> l'éditeur du chat), on peut en associer **plusieurs** à un même post, et chaque bulle de contexte
|
||
> référencée s'ouvre d'un clic comme document courant.
|
||
|
||
### Améliorations
|
||
- **Skills en bulles** : sélectionner un skill (menu `/`, intégré *ou* enregistré) ajoute une vraie
|
||
bulle dans le composer, comportement identique aux mentions `@`. La bulle d'un skill intégré greffe
|
||
son modèle de demande dans le texte ; plusieurs skills peuvent coexister.
|
||
- **Plusieurs skills par post** : l'API `/run` accepte `skill_ids` (tableau) et l'engine injecte tous
|
||
leurs prompts dans les instructions système (`skill_id` reste supporté pour la rétro-compatibilité).
|
||
- **Chips de contexte cliquables** : cliquer sur une bulle de contexte (`@ document / page / base`)
|
||
ouvre l'élément comme document courant — `document:<id>`/`page:<id>` → `/pages/<id>`,
|
||
`collection:<id>` → `/db/<id>`. Le bouton « ✕ » retire toujours la bulle (sans ouvrir).
|
||
- Envoi possible avec uniquement des skills (sans texte) : « Applique le(s) skill(s) sélectionné(s)… ».
|
||
|
||
### Correctifs
|
||
- L'écriture du chat (`send`) renvoie désormais tous les `skill_id` épinglés via `skill_ids`.
|
||
|
||
## v4.15.1 (2026-09-06) — Agent : réparation des conversations réelles (protocole tool_calls)
|
||
|
||
> Le chatbot ne « répondait » plus : avec un fournisseur réel, dès qu'un **outil** était appelé la passe
|
||
> suivante était refusée par l'API (message assistant sans ses `tool_calls`) et l'agent retombait
|
||
> silencieusement sur le **mock hors-ligne**, qui se contentait de répéter la question.
|
||
|
||
### Correctifs
|
||
- **Protocole tool-calling conforme** (`agent_engine.py`) : le message assistant qui annonce un appel
|
||
d'outil transporte désormais ses `tool_calls` (avec `id`), et chaque résultat d'outil répond avec le
|
||
`tool_call_id` correspondant (y compris en cas de refus/permission). Le run se poursuit alors
|
||
correctement et se termine par une vraie réponse du modèle.
|
||
- **Plus de repli silencieux** (`llm_client.py`) : quand un fournisseur **réel** configuré échoue,
|
||
l'erreur remonte (événement SSE `error`) au lieu de produire silencieusement un écho hors-ligne.
|
||
Le mock n'est utilisé que si le fournisseur est réellement non configuré (`offline` / pas de clé).
|
||
- Les `tool_calls` analysés conservent leur `id` et les arguments bruts (`arguments_raw`).
|
||
- Test de régression `test_engine_tool_protocol_messages`.
|
||
|
||
## v4.15.0 (2026-09-06) — Agent : actions sous chaque réponse, mentions @ & skills /
|
||
|
||
> Le panneau FlowDeck Agent gagne l'expérience « type Notion AI » : chaque réponse de l'agent expose
|
||
> des actions rapides (copier, insérer dans la page, feedback 👍/👎), on peut **épingler au contexte**
|
||
> plusieurs documents / pages / bases via la commande **@** (ou le bouton **+**), et la commande **/**
|
||
> propose des **skills** de travail réutilisables en plus des commandes admin existantes.
|
||
|
||
### Sous chaque réponse de l'agent
|
||
- **⧉ / ➕ / 👍 / 👎** : boutons **à icônes seules** (infobulle au survol) — copier la réponse,
|
||
insérer dans la page ouverte, feedback 👍/👎 enregistré en base.
|
||
- Le bandeau statique « Contexte : … » du haut du panneau est supprimé.
|
||
|
||
### Contexte épinglé — commande « @ » / bouton « + » (style Notion AI)
|
||
- Le composeur est maintenant un éditeur **contenteditable** : les références sélectionnées
|
||
s'insèrent **inline** comme des pastilles cliquables (icône + nom) directement dans le message.
|
||
- Au-dessus du composeur, une **barre de contexte groupée** rassemble les fichiers épinglés
|
||
(rangée documents, icônes 📄) et les **skills** (rangée séparée, icône ✨), chacun retirable.
|
||
- **@** ouvre le sélecteur (icône + titre + chemin) listant documents, pages et bases ; l'élément
|
||
choisi devient un jeton inline **et** une puce de contexte. **+** épingle un élément au contexte
|
||
sans l'insérer inline. Les puces persistent jusqu'au retrait (et sont vidées en nouveau chat).
|
||
- **Contexte d'ouverture** : ouvrir l'agent sur un document (`/pages/:id`) ou une base (`/db/:id`)
|
||
ajoute automatiquement ce contexte comme **mention** (puce épinglée) — plus de bandeau statique.
|
||
- À l'envoi, chaque référence est transmise comme **mention** ; le backend résout son **contenu réel**.
|
||
|
||
### Skills — commande « / »
|
||
- Le menu **/** liste les **skills intégrés** (research, resume, brainstorm, plan, meeting-note,
|
||
livrable…), les **skills enregistrés** (table `agent_skills`) et les commandes admin classiques.
|
||
- Un skill intégré pré-remplit le composeur d'un modèle à compléter ; un skill en base s'épingle en
|
||
chip et est appliqué via `skill_id` à chaque envoi.
|
||
- Navigation clavier (↑/↓/Entrée/Échap) dans les menus ; aucun envoi parasite pour « / » seul.
|
||
|
||
### Infra & API
|
||
- Nouvelle table `agent_feedback` (rating, conversation/message, extrait, commentaire).
|
||
- `GET /api/agent/mentions?q=` : recherche des éléments mentionnables du workspace.
|
||
- `POST /api/agent/feedback` : enregistre un retour 👍/👎.
|
||
- `VERSION` → 4.15.0 ; `app/main.py` version → 4.15.0.
|
||
|
||
## v4.14.0 (2026-09-06) — Agent : création de documents dans un espace + nouveaux outils & titre auto
|
||
|
||
> L'agent sait enfin **créer, lire, modifier et supprimer des documents** (pages éditeur) **dans un
|
||
> espace de travail** — la demande « crée un document « Projet » dans le workspace « TEST WS 1 » »
|
||
> ne se termine plus par un simple `search_workspace` vide. Il liste aussi les espaces, et **renomme
|
||
> automatiquement les conversations** avec un titre descriptif au lieu de « Nouvelle conversation ».
|
||
|
||
### Outils (nouveaux / corrigés)
|
||
- **`create_document`** : crée une page éditeur (`pages`) dans un espace (par `workspace_id` ou
|
||
`workspace_name`), avec contenu Markdown optionnel — snapshot d'undo.
|
||
- **`read_document`** : lit un document (titre + contenu + métadonnées).
|
||
- **`read_workspaces`** : liste les espaces accessibles (rôle, nb de documents/collections).
|
||
- **`delete_document`** : suppression → corbeille (`deleted_at`), annulable (`softdelete`).
|
||
- **`write_blocks`** étendu : renommage de titre et/ou écriture des blocs ; undo restaure titre + contenu.
|
||
- **`search_workspace`** étendu : recherche désormais aussi les **documents** et les **espaces de travail**.
|
||
|
||
### Contexte & planification
|
||
- Contexte de l'agent enrichi : sections « Documents » et « Espaces de travail » (avec ids) pour que
|
||
le LLM cible le bon espace. Guide mis à jour. Mock hors-ligne : détection
|
||
« créer un document dans un espace » / « lister les espaces ».
|
||
|
||
### Conversations
|
||
- Auto-titre : après la première exécution d'une conversation encore nommée « Nouvelle conversation »,
|
||
l'agent génère un titre descriptif (déterministe, hors-ligne sûr) ; l'historique devient navigable.
|
||
|
||
### UX
|
||
- Indication claire dans le composeur du panneau : **Entrée ↵ = envoyer · Maj+Entrée = nouvelle ligne**
|
||
(le comportement existait déjà, il est maintenant visible + placeholder mis à jour).
|
||
|
||
### Infra
|
||
- `VERSION` → 4.14.0 ; `app/main.py` version → 4.14.0.
|
||
|
||
## v4.13.2 (2026-09-06) — Saisie libre « écrire un texte » → proposition à appliquer au document
|
||
|
||
> Quand un **document est ouvert** et qu'on tape directement une demande de **rédaction/contenu**
|
||
> dans le chat (« Écrire un texte sur… », « Rédige… », « Résume… », « Traduis… »), la requête est
|
||
> maintenant routée vers la **génération de texte** (sans outils) et affichée comme **proposition
|
||
> à appliquer ou rejeter** — au lieu d'un `search_workspace` puis d'un simple « Objectif traité ».
|
||
> « Traduire » remplace le contenu de la page ; les autres demandes l'insèrent en fin de page.
|
||
|
||
### Fix
|
||
- Envoi libre avec contexte document : détection d'intention de contenu (écrire/rédiger/générer/
|
||
résumer/traduire/améliorer/note de réunion/texte sur/série…) → `POST /api/agent/generate` +
|
||
aperçu « ✓ Appliquer au document / ✕ Rejeter ».
|
||
- Mock : « écrire » (infinitif) reconnu comme verbe de rédaction.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.13.2 ; `app/main.py` version → 4.13.2.
|
||
|
||
## v4.13.1 (2026-09-06) — Corrections actions contenu Agent & aperçu à approuver
|
||
|
||
> Corrige les actions de contenu sur document (« Résumer », « Traduire cette page », « Proposer
|
||
> des améliorations », « Créer une note de réunion ») qui partaient en recherche (`search_workspace`)
|
||
> sans retour utile, et ajoute un flux **aperçu des changements → Appliquer / Rejeter**.
|
||
|
||
### Fix
|
||
- **Le contenu du document n'est plus interprété comme des intentions d'outil** : le planificateur
|
||
(mock) n'analyse que l'objectif de l'utilisateur, jamais le texte injecté dans `# Contexte`. Un
|
||
document contenant « recherche », « collection », etc. ne déclenche plus `search_workspace` /
|
||
`create_collection` intempestifs.
|
||
- **Ask AI** n'emprunte plus le canal outil : il utilise la génération de texte directe.
|
||
|
||
### Added
|
||
- **Endpoint `POST /api/agent/generate`** : génération de texte **sans outils** (l'IA répond à
|
||
partir du contexte du document fourni, sans chercher/créer dans le workspace).
|
||
- **Aperçu & approbation** : les actions de contenu sur document affichent la **proposition** de
|
||
l'agent dans le panneau avec boutons **« ✓ Appliquer au document » / « ✕ Rejeter »**. Appliquer
|
||
insère la proposition (mode `insert`) ou **remplace** le contenu de la page (mode `replace` pour
|
||
« Traduire cette page ») via `window.fdApplyDocument()` ; rejeter la conserve sans modification.
|
||
En mode hors-ligne, l'action explique que la modification nécessite un modèle connecté (pas
|
||
d'application accidentelle).
|
||
|
||
### Infra
|
||
- `VERSION` → 4.13.1 ; `app/main.py` version → 4.13.1.
|
||
|
||
## v4.13.0 (2026-09-06) — Agent : panneau « Notion AI », ouverture fiable (Ctrl+J) & contexte universel
|
||
|
||
> Refonte complète du panneau Agent (style Notion AI) : ouverture/fermeture **fiable** (API
|
||
> `window.fdAgent.open/close/toggle`, bouton flottant + sidebar « Agents » + raccourci `Ctrl/Cmd+J`),
|
||
> **contexte détecté et épinglé à l'ouverture** (document en cours, bibliothèque, My Tasks,
|
||
> espaces, base de données, board, corbeille…) et sélecteur **fournisseur + modèle** intégré à la
|
||
> zone de saisie.
|
||
|
||
### Added
|
||
- **Ouverture robuste** : `window.fdAgent.open()/close()/toggle()` appliquent l'état directement
|
||
(classe `.open` + attribut) ; FAB, entrée « Agents » du sidebar, icône Chat et `fdAgent.ask`
|
||
ouvrent explicitement le panneau (fini le toggle silencieux). Raccourci **`Ctrl/Cmd+J`** global.
|
||
- **Contexte universel `window.FlowDeckCtx`** : détection par priorité —
|
||
(1) marqueur d'écran (`window.fdViewContext` / `data-fd-context`), (2) **document ouvert**
|
||
(éditeur : titre + contenu des blocs), (3) **route** de l'application (Accueil, Bibliothèque,
|
||
Corbeille, My Tasks, Espaces, Espace local/Gitea, Base `/db`, Board, Notes, Paramètres…).
|
||
Le contexte est **épinglé à l'ouverture** du panneau, affiché en chip (« Contexte : … ») et
|
||
**rafraîchi** automatiquement si l'utilisateur ouvre un autre document avant d'envoyer.
|
||
- **Fournisseur & modèle affichés** : sous-titre du panneau `Provider · Modèle` (ex. `OpenAI ·
|
||
gpt-4o`) dès qu'un modèle réel fonctionnel est sélectionné ; « hors-ligne (mock) » n'apparaît
|
||
que lorsqu'aucun fournisseur actif n'existe. Sélecteurs provider → modèle regroupés dans une
|
||
pastille « Auto · <model> » au-dessus de la zone de saisie.
|
||
- **Actions rapides contextuelles** (état vide) en français selon la section : Résumer / Traduire /
|
||
Améliorer un document, Créer une note de réunion, Analyser une base, Mes tâches / planifier,
|
||
ou guides « Comment utiliser FlowDeck ? » et création de collection/board.
|
||
|
||
### Changed
|
||
- **Panneau redessiné façon Notion AI** : largeur 470 px, en-tête « How can I help you today? »,
|
||
icône avatar, ligne de contexte, historique, grande `textarea` arrondie auto-redimensionnable
|
||
(jusqu'à ~220 px) avec toolbar (`+`-équivalent, contexte, modèle, flèche d'envoi) et sélection
|
||
de modèle en popover.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.13.0 ; `app/main.py` version → 4.13.0.
|
||
|
||
## v4.12.1 (2026-09-05) — Corrections configuration Agent & IA + AI Meeting Note
|
||
|
||
> Correctifs UI sur la section **Settings → Agent & IA** et remplacement de l'ancien bouton
|
||
> « AI meeting note » (qui insérait du texte) par une vraie **boîte de réunion interactive**.
|
||
|
||
### Fix
|
||
- **Champs « Clé API »** : le `x-text` embarquait le SVG de l'icône œil dans l'attribut HTML, ce qui
|
||
cassait l'attribut et laissait du texte résiduel (`sk-…': '👁'" style="top:50%;">`). Les boutons
|
||
d'affichage/masquage utilisent désormais deux `<span x-show>` avec l'icône en contenu.
|
||
- **Champs « URL API »** : pré-remplis avec l'URL réellement utilisée par l'application (base par
|
||
défaut du fournisseur, ex. `https://api.openai.com/v1`) quand aucune URL personnalisée n'est
|
||
stockée — exposée via `base_url` dans `/api/agent/providers` (fournisseur par défaut + au
|
||
changement de provider).
|
||
|
||
### Changed
|
||
- **AI Meeting Note** : le bouton « Get started » ne génère plus du texte via l'agent. Il crée un
|
||
bloc **`meeting`** interactif dans la page (page renommée « Meeting @date » si vide) :
|
||
- Étape 1 — consentement (moi-même / message audio auto) + « Start transcribing » ;
|
||
- Étape 2 — transcription **live** via Web Speech API (fr-FR, horodatée), boutons Pause / Stop,
|
||
fallback « sans audio » (transcript à saisir/coller) si le navigateur ne supporte pas ;
|
||
- Étape 3 — onglets Notes / Transcript / Summary, bouton « Générer le résumé » (helper headless
|
||
`window.fdAgent.generate` sur le FlowDeck Agent) produisant Résumé / Décisions / Action items,
|
||
copie presse-papiers et « Nouvelle transcription ».
|
||
- Persistance du bloc et de son état dans la page (champ `meeting` ajouté aux blocs sauvegardés).
|
||
|
||
### Infra
|
||
- `VERSION` → 4.12.1 ; `app/main.py` version → 4.12.1.
|
||
|
||
## v4.12.0 (2026-09-05) — Agent & IA : refonte des réglages, fournisseurs activés/fonctionnels dans le panneau, contexte document & guide
|
||
|
||
> Refonte de la section **Settings → Agent & IA** pour la rendre plus claire, et aligne le panneau
|
||
> Agent sur la réalité de la configuration : **seuls les fournisseurs configurés ET dont le test de
|
||
> connexion a réussi** sont proposés (avant, tous les fournisseurs connus apparaissaient, même non
|
||
> configurés). Le panneau sait aussi sur quel **document** l'utilisateur travaille et peut guider
|
||
> sur l'utilisation de l'application quand aucun document n'est ouvert.
|
||
|
||
### Added
|
||
- **Vérification des fournisseurs** : colonnes `verified` / `verified_at` / `last_error` sur
|
||
`user_llm_keys` et `llm_config` (migration automatique au démarrage). Un fournisseur est
|
||
« fonctionnel » après un test de connexion réussi ou un chargement de modèles réussi.
|
||
- **Endpoint non-admin** `POST /api/agent/keys/{provider}/test` : teste la clé enregistrée de
|
||
l'utilisateur (sans mock) et met à jour l'état `verified` ; l'endpoint admin `/providers/test`
|
||
marque le fournisseur par défaut de l'espace.
|
||
- **`/api/agent/providers` enrichi** : chaque provider expose `configured`, `verified`,
|
||
`functional`, `last_error` et `source` (user/global/open) ; `default_provider` + `default_verified`
|
||
en tête de réponse.
|
||
- **Panneau Agent** : les listes fournisseur/modèle ne contiennent plus que les providers
|
||
**activés et fonctionnels** (+ mode hors-ligne). Bandeau « Aucun fournisseur activé » avec lien
|
||
vers les réglages et rappel des clés « à tester » le cas échéant.
|
||
- **Contexte document** : le panneau joint automatiquement la page ouverte (titre + contenu des
|
||
blocs) à chaque requête — l'agent peut interagir avec le document courant.
|
||
- **Guide d'utilisation** : le prompt système intègre un guide FlowDeck concis pour répondre aux
|
||
questions « comment faire… ? » (pages, collections/vues, Gitea, corbeille, réglages…) lorsqu'aucun
|
||
document n'est fourni.
|
||
|
||
### Changed
|
||
- **Settings → Agent & IA refondu** : carte de statut (prêt / N fournisseurs actifs / mode
|
||
hors-ligne), cartes de fournisseurs avec badges d'état, erreur du dernier test, boutons
|
||
« Enregistrer & tester », « Tester », « Modèles », « Supprimer » et encart d'aide.
|
||
- Le bouton **Save configuration** admin gagne un rappel « Tester la connexion » pour activer le
|
||
fournisseur pour tous ; changer de clé réinitialise la vérification.
|
||
|
||
### Fix
|
||
- Saisie clé : une clé vide conserve désormais l'existante aussi pour la config globale (au lieu de
|
||
l'effacer), et un changement de clé invalide l'ancien statut `verified`.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.12.0 ; `app/main.py` version → 4.12.0.
|
||
|
||
## v4.11.1 (2026-09-05) — Correction & intégration des points d'entrée IA
|
||
|
||
> Aligne les entrées AI de l'interface sur le FlowDeck Agent (alignement Guide Notion AI) et corrige
|
||
> les trois boutons qui ne fonctionnaient pas : **Ask AI**, **AI meeting note** (toolbar « Get
|
||
> started » de l'éditeur de page) et le **bouton rond flottant FlowDeck Agent** (coin bas-droit).
|
||
|
||
### Fix
|
||
- **Bouton flottant Agent 🤖 (coin bas-droit)** : le chat restait inerte tant qu'aucune conversation
|
||
n'existait (`send()` retournait silencieusement sans `currentConv`). Ajout d'une création de
|
||
conversation **lazy** (`ensureConversation()`) effectuée avant chaque envoi — la 1ère saisie crée
|
||
la conversation puis l'exécute (mode offline ou LLM réel).
|
||
- **Mode hors-ligne plus utile** : le mock LLM (`_draft_reply`) génère désormais un **vrai contenu
|
||
structuré** pour les demandes de rédaction de page (« Ask AI », titre détecté via « intitulée »)
|
||
et les **AI meeting notes** (squelette Résumé / Décisions / Action items), au lieu de se contenter
|
||
de renvoyer le prompt tel quel — les boutons produisent donc du contenu même sans clé API.
|
||
- **« Ask AI » & « AI meeting note »** : ne faisaient qu'insérer du texte placeholder
|
||
(« Ask AI to write... » / « AI Meeting Note ») sans aucun appel IA. Ils déclenchent désormais une
|
||
**génération inline réelle** via le FlowDeck Agent :
|
||
- méthodes `askAI()` / `meetingNote()` / `aiRun()` / `applyAIBlocks()` sur l'éditeur de page ;
|
||
- un prompt contextuel est envoyé au panneau agent (exposition d'`window.fdAgent.ask`) ;
|
||
- la réponse (`event final` du flux SSE) est **insérée automatiquement dans la page** sous forme
|
||
de blocs édités (hook `window.__fdInsertAI`).
|
||
- Un état `aiBusy` évite les doubles envois pendant la génération.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.11.1.
|
||
|
||
## v4.11.0 (2026-09-05) — Agent IA : clés API par utilisateur & chargement dynamique des modèles
|
||
|
||
> Suite de v4.10.1 : migre la config LLM d'un **global admin** vers des **credentials par
|
||
> utilisateur**. Chacun peut enregistrer plusieurs provider + clé API dans son profil, les modèles
|
||
> disponibles sont **chargés automatiquement depuis le fournisseur**, et le provider/modèle actif
|
||
> d'une conversation se pilote **directement dans le chat** via des commandes slash.
|
||
|
||
### Added
|
||
- **Clés par utilisateur** : table `user_llm_keys` (UNIQUE(user_id, provider)) + service
|
||
`app/services/llm_config.py` : `get_user_llm_key`, `list_user_llm_keys` (masqué),
|
||
`upsert_user_llm_key`, `delete_user_llm_key`. Une clé vide conserve l'existante.
|
||
- **Chargement dynamique des modèles** : `fetch_provider_models()` (httpx) — GET `{base}/models`
|
||
pour OpenAI-compatible (Bearer), headers `x-api-key`/`anthropic-version` (Anthropic),
|
||
`x-goog-api-key` (Gemini) ; stockés dans `models_json` et fusionnés dans `GET /providers`.
|
||
- **Endpoints API** (tout utilisateur authentifié) :
|
||
- `GET /api/agent/keys`, `PUT/DELETE /api/agent/keys/{provider}`,
|
||
- `POST /api/agent/keys/{provider}/models` (fallback sur la clé stockée, pas de fuite de clé),
|
||
- `PATCH /api/agent/conversations/{id}` (title / provider / model — commande slash).
|
||
- **`POST /run`** : utilise la clé API de l'utilisateur pour le provider de la conversation
|
||
(sinon config globale / `.env` / offline mock).
|
||
- **UI Settings → Agent & IA** (visible pour tous, plus seulement admin) : carte par provider
|
||
(clé API masquée, URL API, « Charger les modèles » en live, modèle par défaut,
|
||
Enregistrer / Supprimer). La config globale reste admin sous « Configuration globale ».
|
||
- **Commandes slash dans le panel agent** : `/help`, `/providers`, `/keys`,
|
||
`/provider <nom>`, `/model <nom>` — menu de suggestions live, appliquées via `PATCH`
|
||
et sans appel LLM.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.11.0 ; migration idempotente (création table `user_llm_keys` + index).
|
||
|
||
### Tests
|
||
- **239 tests verts** (+7 : CRUD clés + masquage des secrets, isolation par utilisateur,
|
||
provider invalide, fetch modèles (clé stockée / body gagnant / erreur), PATCH conversation,
|
||
`/run` utilise la clé de l'utilisateur).
|
||
|
||
|
||
|
||
> Comble le trou UX de v4.10.0 : l'utilisateur pouvait discuter avec l'agent mais **ne pouvait
|
||
> configurer ni provider ni modèle**. Ajout d'un sélecteur provider/modèle dans le panneau, d'une
|
||
> config runtime persistée en base (table `llm_config`) et d'un écran admin avec test de connexion.
|
||
|
||
### Added
|
||
- **Sélecteur provider/modèle dans le panneau agent** (`agent_panel.html`) : deux `<select>`
|
||
(provider + modèles connus par provider) dans l'en-tête du chat, branchés sur `POST /run` et
|
||
**persistés par conversation** (colonnes `provider`/`model` sur `agent_conversations`,
|
||
migrées automatiquement). Bouton ⚙️ pointant vers la config admin.
|
||
- **Endpoints API** :
|
||
- `GET /api/agent/providers` enrichi : liste complète des providers avec modèles connus,
|
||
`requires_key`, `api_base` et `has_api_key`.
|
||
- `PATCH /api/agent/providers` (admin) : persiste provider/modèle/clé/base en runtime.
|
||
- `POST /api/agent/providers/test` (admin) : test de connexion **sans fallback mock**
|
||
(méthode `LLMClient.ping()`) ; le provider `offline` répond sans réseau.
|
||
- **Table `llm_config`** (1 ligne, id=1) + service `app/services/llm_config.py` :
|
||
précédence **base (runtime) > `.env`**, créée seulement au premier enregistrement.
|
||
- **Écran admin** : nouvelle section *Agent & IA* dans Settings (onglet Admin) : provider,
|
||
modèle, clé API (masquée / réaffichable), URL API optionnelle, boutons
|
||
**Save configuration** et **Test connection** avec retour d'état inline.
|
||
|
||
### Fixed
|
||
- `LLMClient.__init__` : le modèle par défaut suit maintenant la config runtime
|
||
(DB > `.env`) au lieu d'être toujours `gpt-4o` ; `PROVIDER_MODELS` documente les modèles
|
||
connus par provider.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.10.1 ; migration idempotente des colonnes `agent_conversations.provider/model`.
|
||
|
||
### Tests
|
||
- **232 tests verts** (+6 : providers enrichi, PATCH config (admin + 403 non-admin),
|
||
test connexion offline, test connexion clé manquante, persistance provider/modèle par conversation).
|
||
|
||
## v4.10.0 (2026-09-05) — FlowDeck Agent : agent IA natif conversationnel
|
||
|
||
> Implémentation complète du pilier « Agent IA » : un agent ReAct intégré qui **agit** sur le
|
||
> workspace (pas seulement répond) — panneau de chat, boucle raisonnement→action, streaming SSE,
|
||
> 18 outils avec snapshots rollback, permissions par rôle, contexte automatique, custom agents,
|
||
> déclencheurs planifiés, skills réutilisables et multi-LLM (offline mock + 8 providers).
|
||
|
||
### Added
|
||
- **Interface conversationnelle** : panneau agent dans le sidebar (🤖 Agents) piloté par Alpine.js +
|
||
streaming SSE ; FAB ouvrant le chat ; historique de conversations.
|
||
- **Boucle ReAct** (`app/services/agent_engine.py`) : raisonnement → action → observation, max 12
|
||
itérations, budget de tokens, timeout, journal d'audit + fonction `undo_action()` (rollback).
|
||
- **Streaming SSE** : endpoint `/api/agent/conversations/{id}/run` qui émet `reasoning`, `action`
|
||
et `final` en temps réel.
|
||
- **18 outils** (`app/services/tool_registry.py`) : `search_workspace`, `read_collection`,
|
||
`read_page`, `create_collection`, `create_view`, `add_property`, `create_page`, `update_page`,
|
||
`write_blocks`, `add_relation`, `create_sub_item`, `add_dependency`, `apply_template`,
|
||
`delete_page`, `delete_collection`, `read_gitea_issues`, `sync_gitea`, `create_gitea_issue` —
|
||
chacun avec `ToolResult` + snapshot d'undo réversible.
|
||
- **Permissions** (`app/services/permission_manager.py`) : même ACL que l'utilisateur par rôle
|
||
workspace (viewer/editor/admin/owner) + gating des outils.
|
||
- **Contexte automatique** (`app/services/context_builder.py`) : snapshot du workspace actif,
|
||
collections, pages, mentions `@`, fichiers uploadés injectés dans le prompt système.
|
||
- **Multi-LLM** (`app/services/llm_client.py`) : OpenAI, Anthropic, Google, Ollama, DeepSeek,
|
||
QwenCloud, NVIDIA, OpenRouter + **offline mock** (planner déterministe, aucune clé requise).
|
||
- **Custom agents, skills, déclencheurs** : CRUD agents + conversations, `agent_*` tables
|
||
(`agents`, `agent_conversations`, `agent_messages`, `agent_actions`, `agent_skills`,
|
||
`agent_triggers`), skills (`/api/agent/skills`, apply), scheduler de fond pour triggers planifiés.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.10.0 ; config LLM (`LLM_PROVIDER/MODEL/API_KEY/API_BASE`, `AGENT_MAX_*`) ;
|
||
exclusions CSRF `/api/agent` ; routeur agent + tâche scheduler dans le lifespan de `app/main.py`.
|
||
|
||
### Tests
|
||
- **226 tests verts** (+18 v4.10.0 : schéma DB, mock LLM, permissions, outils+undo, engine ReAct +
|
||
audit + rollback, CRUD agents/conversations, streaming SSE, skills, tools, providers).
|
||
|
||
## v4.9.0 (2026-09-04) — Collaboration : commentaires inline, mentions @, notifications
|
||
|
||
> Implémentation complète du pilier « Collaboration » — basée sur la revue des docs Notion
|
||
> (`docs/Guide_Complet_Notion_sharing_collaborartion.md`) : commentaires avec ancres inline,
|
||
> mentions `@` avec autocomplétion, centre de notifications in-app et emails SMTP.
|
||
|
||
### Added
|
||
- **Commentaires inline sur les pages** : sélection d'un texte dans l'éditeur → bouton flottant
|
||
« 💬 Comment » → commentaire ancré (`block_id` + offsets). Table `comments` étendue
|
||
(`target_type`, `target_id`, `anchor_block_id`, `anchor_start`, `anchor_end`) et **migrée**
|
||
de son FK `collection_pages` vers un schéma générique (aucune perte de données, idempotente).
|
||
- **Panneau de commentaires** dans l'éditeur : liste, résolution (✔/↪), suppression, compteur
|
||
dans la topbar.
|
||
- **Mentions `@`** : autocomplétion en temps réel (recherche d'utilisateurs), insertion de
|
||
`@login` dans un bloc ou un commentaire → notification ciblée à l'utilisateur mentionné.
|
||
- **Centre de notifications in-app** : cloche dans la topbar (badge non-lus, polling 30s),
|
||
panneau déroulant, « mark as read » / « mark all read ». Table `notifications`.
|
||
- **Emails de notification** : service SMTP (`app/services/mailer.py`) + templates HTML ;
|
||
préférences email par utilisateur (comments/mentions) réglables dans Settings → Notifications.
|
||
Sans SMTP configuré, seules les notifications in-app sont émises (aucune erreur).
|
||
- **API** : `GET/POST /api/notifications`, `/api/notifications/read|read-all|prefs`,
|
||
`/api/notifications/users/search`, `GET/POST /api/pages/{id}/comments`,
|
||
`PUT/DELETE /api/comments/{id}`, `POST /api/pages/{id}/mentions`.
|
||
- **Config SMTP** (`SMTP_HOST/PORT/USER/PASSWORD/FROM/USE_TLS`, `APP_BASE_URL`) dans `.env.example`.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.9.0 ; `app/main.py` (log + `version=`) ; router `notifications` + `collaboration`
|
||
enregistrés ; exclusions CSRF (`/api/notifications`, `/api/comments`) ; le `RateLimitMiddleware`
|
||
respecte désormais `settings.rate_limit_enabled`.
|
||
|
||
### Tests
|
||
- **208 tests verts** (+9 v4.9.0 : table notifications, commentaires inline + mentions, centre de
|
||
notifications, préférences, recherche utilisateurs, résolution/suppression, mentions de page).
|
||
|
||
### Fixed
|
||
- **Bouton « + Row » (bas du tableau) inopérant** — le code passait la nouvelle ligne en argument `deleteCount` de `Array.splice` au lieu d'utiliser `splice(index, 0, ligne)` : aucune ligne n'était jamais insérée. Corrigé ; les insertions de lignes (menu contextuel aussi) fonctionnent.
|
||
- Coloration/handles alignés sur l'en-tête.
|
||
|
||
### Added
|
||
- **Bouton « + » à droite du tableau** (sur toute la hauteur) → ajoute une colonne à la droite (équivalent d'un « Add column » comme « Add Row »).
|
||
- **Redimensionnement des colonnes à la souris** : au survol d'une bordure de colonne (en-tête) le curseur devient `col-resize` ; glisser ajuste la largeur. Les largeurs sont persistées dans le bloc (`colsW`) → conservées au rechargement, à l'ajout/suppression et dans l'auto-save.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.8.1 ; cache CSS `app.css?v=4.8.1`.
|
||
|
||
## v4.8.0 (2026-09-03) — Bloc Tableau simple (mode édition Notion)
|
||
|
||
> Insertion via `/table`, cellules éditables au clic, ajout/suppression/modification de lignes et colonnes comme dans Notion.
|
||
|
||
### Added
|
||
- **Bloc `table` dans l'éditeur de page.** Nouvelles commandes slash `/table` (menu « BASIC ») → tableau 3×3 avec en-tête ; première ligne stylée en en-tête, cellules `contenteditable` éditées au clic, auto-sauvegarde (format de bloc conservé : `{type:'table', rows:[...]}`).
|
||
- **Gestion des colonnes** : bouton de poignée (⋮⋮) au-dessus de chaque colonne → menu contextuel Insert left / Insert right / Duplicate / Clear contents / Delete column / Delete table.
|
||
- **Gestion des lignes** : bouton « + Row » sous le tableau (clic = ajouter une ligne) + menu contextuel (clic droit sur une cellule de corps) → Insert above / Insert below / Duplicate / Clear row / Delete row / Delete table.
|
||
- **Import Markdown (GFM)** : `md2b` détecte les tableaux pipe (`| a | b |` + ligne de séparateurs) et produit un bloc `table` éditable au lieu de paragraphes aplatis (pages `.md`/markdown).
|
||
- **Roundtrip Markdown** : `blocksToMarkdown` (JS) et l'export Python reconstruisent un tableau pipe valide depuis les blocs.
|
||
- **Export** : les pages contenant un bloc `table` sortent correctement en Markdown / HTML / PDF (géré côté serveur).
|
||
- UX : cliquer sous un tableau en fin de page ajoute un paragraphe pour continuer la rédaction.
|
||
|
||
### Infra
|
||
- `VERSION` → 4.8.0 ; `app/main.py` (log + `version=`) ; cache CSS `app.css?v=4.8.0` ; ROADMAP renuméroté (v4.8.0 Bloc Tableau, Collaboration → v4.9.0, Agent → v4.10.0).
|
||
|
||
### Tests
|
||
- Suite pytest verte (199) ; vérification syntaxe JS du template via `node --check`.
|
||
|
||
## v4.7.3 (2026-09-03) — Fix export PDF/HTML : tableaux + émojis
|
||
|
||
### Fixed
|
||
- **Tableaux Markdown rendus comme du texte brut** dans les exports HTML/PDF (les lignes `| a | b |` ressortaient en paragraphes). Ajout d'un parseur GFM de tableaux (`_md_to_blocks` → bloc `table`) et d'un rendu en vrai `<table>` (en-tête `<th>`, corps `<td>`, alignement gauche/centre/droite, bordures fines via `.ftable`). Le format Markdown reconstruit un tableau pipe valide.
|
||
- **Émojis en « carrés noirs » dans le PDF.** Cause : le moteur `xhtml2pdf` (reportlab) n'embarque que des polices de base sans glyphes Unicode/émojis. Le moteur PDF passe à **WeasyPrint** (vraies tables CSS + émojis couleur via Pango + `fonts-noto-color-emoji`, installés dans l'image Docker). Repli automatique sur `xhtml2pdf` si WeasyPrint n'a pas ses libs natives (ex. poste de dev Windows) — le contenu s'exporte quand même.
|
||
|
||
### Infra
|
||
- `Dockerfile` : ajout des libs WeasyPrint (pango/harfbuzz/gdk-pixbuf/shared-mime-info) + polices `fonts-dejavu-core` et `fonts-noto-color-emoji`.
|
||
- `requirements.txt` : + `weasyprint==69.0` (xhtml2pdf conservé en repli).
|
||
|
||
### Tests
|
||
- **199 tests** (4 nouveaux) : parse table → `<table>` HTML réel + alignements ; standalone HTML embarque `.ftable` ; roundtrip table → markdown pipe ; PDF d'une page avec table = PDF valide.
|
||
- Vérifié en réel sur `README.md` : PDF avec tableau structuré + émojis en couleur (pixels colorés confirmés), HTML avec tableau bordé.
|
||
|
||
## v4.7.2 (2026-09-03) — Fix export : le contenu des documents était absent
|
||
|
||
### Fixed
|
||
- **Export Markdown / HTML / PDF : seuls le nom de page et le titre étaient inclus, jamais le contenu.** Cause : le service d'export ne lisait que les pages en `content_format='blocks'`. Or les documents peuvent être stockés autrement :
|
||
- `content_format='file'` (fichiers `.md`/code uploadés) : le `content` de la page n'est que des métadonnées JSON — le vrai texte est un fichier sur disque (`/data/uploads/workspace_*`). L'export n'allait jamais le lire → uniquement le titre.
|
||
- `content_format='markdown'` : le HTML/PDF se contentait d'envelopper chaque ligne en `<p>` (titres et listes aplatis).
|
||
- Le service résout désormais la vraie source pour les 3 formats : lit le fichier sur disque pour les pages `file`, rend les pages `markdown`/fichiers `.md` en blocs (headings, listes, code…), et traite les fichiers texte non-markdown comme bloc de code. Les fichiers binaires (PDF/images) sont laissés de côté (rien d'exploitable).
|
||
- Chemin des uploads rendu configurable pour les tests via `FLOWDECK_DATA_DIR` (défaut `/data`, identique à `dashboard.py`).
|
||
|
||
### Tests
|
||
- **195 tests au total** (5 nouveaux v4.7.2) : page markdown → corps exporté ; page markdown → HTML avec headings/listes ; fichier `.md` uploadé → contenu réel dans Markdown/HTML/PDF ; fichier code → exporté ; fichier binaire → non déversé.
|
||
- Vérifié en réel sur `/data` (page `README.md` et arborescence « Base de Connaissances » → fichiers `.md`) : le corps complet est maintenant présent dans les 3 formats.
|
||
|
||
## v4.7.1 (2026-09-03) — Fix UI éditeur : menus Share / More invisibles
|
||
|
||
### Fixed
|
||
- **Boutons « Share » et « ⋯ » (More) sans réaction visible dans l'éditeur de page** — les popovers (share-dialog, more-menu, activity-popover, move-dialog), enfants de `.page-editor-wrapper` (conteneur à `overflow-y:auto`), étaient ancrés en `absolute; top:100%` → rendus SOUS le viewport, donc invisibles. Repositionnés en `position:fixed` sous la topbar (scoped `.page-editor-wrapper > …` pour ne pas casser les `.more-menu` des pages library/local_workspace) + variantes mobile dans le media query < 768px.
|
||
- **Sous-menu Export se refermait** — l'itém « ↗ Export » faisait `moreOpen=false` avant d'ouvrir `exportOpen` : les 4 formats restaient inaccessibles au clic. Devient un toggle (`exportOpen = !exportOpen`), le menu More reste ouvert.
|
||
- **Cache CSS** — version du query string `app.css` bumpée (`v=3.0.0` → `v=4.7.1`) pour invalider le cache navigateur.
|
||
|
||
### Vérifié
|
||
- Playwright headless (desktop 1280×800) : Share, More, sous-menu Export, popover Activity et menu More après scroll = tous visibles dans le viewport ; téléchargement Markdown + toast « Exported as Markdown » OK ; zéro pageerror.
|
||
- 190/190 tests pytest passent.
|
||
|
||
## v4.7.0 (2026-09-03) — Export
|
||
|
||
### Added
|
||
- **Export Markdown** — conversion serveur complète de tous les blocs (headings, listes, to-do, quote, code, math, toggle, colonnes, callout, image), images incluses
|
||
- **Export Markdown récursif** — les sous-pages sont incluses automatiquement
|
||
- **Export PDF** — via `xhtml2pdf` (pur Python, aucun lib système), rendu print-friendly
|
||
- **Export HTML** — document autonome self-contained (styles inline)
|
||
- **Export Site** — site statique multi-pages en `.zip` (index.html + une page HTML par sous-page)
|
||
- **4 endpoints serveur** : `/api/export/markdown|html|pdf|site/{page_id}`
|
||
- **UI** : menu « More › Export » dans l'éditeur (Markdown, HTML, PDF, Site .zip)
|
||
|
||
### Améliorations
|
||
- Nouveau service `app/services/export.py` (conversion blocs → Markdown / HTML / PDF réutilisable)
|
||
- Nouveau routeur `app/routers/export.py`
|
||
- Téléchargements via `Content-Disposition` (avec nom de fichier encodé UTF-8)
|
||
|
||
### Tests
|
||
- **190 tests au total** (6 nouveaux tests v4.7.0) : Markdown, sous-pages, HTML, PDF, Site zip, 404
|
||
|
||
## v4.6.0 (2026-09-02) — Content Blocks Enrichis
|
||
|
||
### Added
|
||
- **Callout blocks** — boîtes colorées avec sélecteur d'emoji/icône cliquable
|
||
- **Table of contents** — bloc auto-généré depuis les headings, avec ancres de navigation
|
||
- **Math equations** — bloc LaTeX/KaTeX avec aperçu en temps réel (édition inline)
|
||
- **`table_of_contents` et `math`** — nouveaux types dans le slash menu (éditeur)
|
||
- **Multi-colonnes** — bloc `columns` avec layout flexible, bouton "+ Add column" / retrait
|
||
- **Toggle lists enrichis** — contenu expandable/collapsible avec blocs enfants imbriqués
|
||
|
||
### Améliorations
|
||
- Intégration **KaTeX 0.16.11 self-hosté** (`/static/js/katex.min.js`, `/static/css/katex.min.css`, `/static/fonts/`) — aucun CDN externe
|
||
- Rendu des nouveaux blocs dans les **pages publiques** (`/p/<slug>`): TOC ancré, KaTeX, colonnes flex, toggle `<details>`
|
||
- Export Markdown étendu aux nouveaux types de blocs
|
||
- Correction d'un bug pré-existant : `public_page.html` n'importait pas la macro `fd_icon` → erreur de rendu des pages publiées
|
||
|
||
### Tests
|
||
- **183 tests au total** (5 nouveaux tests v4.6.0) couvrant le rendu public TOC/math/colonnes/toggle et la persistance `children`
|
||
|
||
## v2.7.1 (2026-07-13) — Private Pages Gitea
|
||
|
||
### Added
|
||
- **Table `gitea_private_pages`**: pages FlowDeck locales liées à un projet Gitea (user_id, owner, repo)
|
||
- **API CRUD**: GET/POST/PUT/DELETE `/api/gitea/projects/{owner}/{repo}/private-pages`
|
||
- **UI**: liste des pages privées + éditeur titre/contenu dans le workspace Gitea
|
||
- **Sidebar**: lien "Private Pages" dans l'arborescence du workspace
|
||
- **Persistance**: les pages survivent au changement de workspace / session
|
||
|
||
### Fixed
|
||
- `get_user_repos(limit=100)` — `bruno/flowdeck` était tronqué à la page 2 (30 repos par défaut)
|
||
|
||
## v2.7.0 (2026-07-13) — Intégration Gitea Phase 1
|
||
|
||
### Added
|
||
- **OAuth2 Provider**: Gitea comme fournisseur OAuth2 pour FlowDeck (Client ID/Secret dans `.env`)
|
||
- **Redirect URI dynamique**: basé sur le header `Host` — fonctionne depuis `localhost` et `192.168.x.x`
|
||
- **Workspace listing**: 2 sections — My Workspaces + Gitea Projects (groupés par organisation)
|
||
- **Tabs par org**: All | user | org1 | org2 avec compteurs de projets
|
||
- **Barre de recherche**: filtrage live par nom/description dans Gitea Projects
|
||
- **Lazy tree**: arborescence chargée un dossier à la fois (API Gitea `GET /repos/{o}/{r}/contents`)
|
||
- **File viewer**: lecture fichiers Gitea avec décodage base64
|
||
- **Éditeur + commit**: modifier un fichier et commiter dans Gitea avec message personnalisé
|
||
- **Delete fichier**: suppression avec confirmation + commit
|
||
- **Arborescence dans le sidebar**: comme un workspace local, avec lazy-load des sous-dossiers
|
||
- **Settings › Integrations**: connecter/déconnecter Gitea (`GET /api/gitea/status`, `DELETE /api/gitea/disconnect`)
|
||
- **Fallback token admin**: opérations GET utilisent le token serveur si pas de token OAuth utilisateur
|
||
- **Write ops protégées**: commit et delete exigent un token OAuth utilisateur
|
||
- **Routes API**: `/api/gitea/orgs`, `/api/gitea/projects`, `/api/gitea/projects/{o}/{r}/tree`, `/api/gitea/projects/{o}/{r}/file`, `/api/gitea/projects/{o}/{r}/labels`
|
||
|
||
### Fixed
|
||
- Bouton "Register with Gitea" ne crée plus de session admin fantôme (fallback retiré)
|
||
- `_sidebar_data` gère le cookie `gitea:owner:repo` pour l'arborescence
|
||
|
||
## v2.6.0 (2026-07-13) — My Account & Auth Locale
|
||
|
||
### Added
|
||
- **Section My Account**: modifier username, full name, email, mot de passe
|
||
- **Toggle mot de passe**: bouton voir/cacher dans les formulaires login/register
|
||
- **Label "Email or username"**: login accepte les deux formats
|
||
- **Refresh cookie session**: après modification du compte, le cookie est mis à jour
|
||
- **Register avec Gitea**: boutons dynamiques "Login/Register with Gitea" selon l'onglet actif
|
||
|
||
### Fixed
|
||
- Session cookie non rafraîchi après `PUT /api/settings/account` (création nouvelle session)
|
||
- Input login `type="email"` → `type="text"` (accepte usernames non-email)
|
||
|
||
## v2.5.0 (2026-07-13) — Administration Avancée
|
||
|
||
### Added
|
||
- **Premier compte = admin automatique**: `is_admin=1` pour le premier utilisateur créé
|
||
- **Panneau Admin dans Settings**: visible uniquement pour les admins
|
||
- **Users & Roles**: créer/modifier/supprimer utilisateurs, changer rôles
|
||
- **Table `login_history`**: audit des connexions (IP, user_agent, timestamp)
|
||
- **Statistiques par utilisateur**: workspaces, pages, dernière connexion
|
||
- **API `/api/admin/*`**: users, stats, audit — protégées par `admin_required`
|
||
- **CSRF fix**: token CSRF dans toutes les requêtes admin (`adminFetch`)
|
||
|
||
## v2.4.0 (2026-07-13) — Tags Personnalisés par Utilisateur
|
||
|
||
### Added
|
||
- **Tags isolés par `user_id`**: chaque utilisateur voit uniquement ses propres tags
|
||
- **`UNIQUE(name, user_id)`**: remplace `UNIQUE(name)` dans la table `tags`
|
||
- **Migration**: tags existants assignés au premier utilisateur
|
||
- **Filtrage transparent**: l'API retourne les tags filtrés par `user_id` (session cookie)
|
||
|
||
### Fixed
|
||
- Affichage des tags dans le menu contextuel workspace (sync Alpine.js)
|
||
- Création de tag avec couleur persistée
|
||
- Fermeture auto du menu contextuel après ajout de tag
|
||
- Filtrage par tags: `toggleTagFilter()` appelle `doFilter()`
|
||
- Pastilles tags: fond = couleur du tag, sans rond séparé
|
||
|
||
## v2.3.0 (2026-07-12) — Multi-Vues Workspace & Preview Panel
|
||
|
||
### Added
|
||
- **Sélecteur de vue**: Tree, List, Details, Title, Content — 5 modes d'affichage
|
||
- **Vue List**: liste compacte avec Name, Type, Size, Modified
|
||
- **Vue Details**: table complète avec Path, Author, Tags
|
||
- **Vue Title**: grille de cartes avec icônes et métadonnées
|
||
- **Vue Content**: liste avec aperçu inline (taille ou nombre d'items)
|
||
- **Preview Panel**: panneau latéral droit avec contenu du fichier sélectionné
|
||
- Header avec icône + titre + bouton fermer
|
||
- Métadonnées (type, taille, date)
|
||
- Tags affichés en chips
|
||
- Corps avec rendu du contenu (blocks JSON → HTML, texte brut)
|
||
- Actions: Open, Rename, Delete
|
||
- Animation slide-in/out
|
||
- **Tri par date**: options Oldest/Newest dans le selecteur de tri
|
||
|
||
## v2.2.0 (2026-07-12) — Tags & Recherche Avancée
|
||
|
||
### Added
|
||
- **Tags**: système complet de tags avec tables `tags` + `page_tags`, CRUD API
|
||
- **API Tags**: `GET/POST/DELETE /api/local-workspace/items/{id}/tags`, `GET /api/local-workspace/tags`
|
||
- **Vue Table enrichie**: colonnes Name, Path, Size, Modified, Type, Author, Tags
|
||
- **Filtrage par tags**: barre de chips avec compteurs, clic pour filtrer
|
||
- **Ajout/retrait tags inline**: bouton + dans la table, chips avec × pour retirer
|
||
- **Tree endpoint enrichi**: chaque nœud retourne `size`, `size_display`, `created_at`, `updated_at`, `author`, `tags`
|
||
- **Filtre "Dossiers"**: nouveau chip Folders dans la barre de filtres
|
||
- **Auto-switch table**: quand recherche/filtre actif, passe en vue table automatiquement
|
||
|
||
## v2.1.1 (2026-07-12) — Fix Arborescence Workspace
|
||
|
||
### Fixed
|
||
- **Arborescence**: création fichier/dossier dans un sous-dossier met à jour l'arbre sans rechargement
|
||
- **parentFolder**: corrigé le bug où `parentFolder` était nullifié avant l'insertion dans l'arbre JS
|
||
- **displayTree**: nouvelle référence array après chaque mutation pour forcer le re-render Alpine.js
|
||
- **doSort/doFilter**: gèrent maintenant le cas où un filtre/tri est actif après création
|
||
|
||
## v2.1.0 (2026-07-10) — Intégrations Avancées
|
||
|
||
### Added
|
||
- **API publique**: `/api/v1` (collections, pages, my-tasks) avec token auth
|
||
- **Token generation**: `POST /api/v1/token`
|
||
- **Webhooks sortants**: `GET/POST/DELETE /workspace/webhooks` + dispatcher
|
||
- **PWA**: `GET /manifest.json`
|
||
- **CSRF**: `/api/v1` exempté
|
||
|
||
### Tests
|
||
- 73/73 passent (+6 tests v2.1)
|
||
|
||
## v2.0.0 (2026-07-10) — Multi-User Workspaces & Editor Complete
|
||
|
||
### Added
|
||
- **Workspaces**: tables `workspaces` + `workspace_members`, CRUD API, roles admin/editor/commenter/viewer
|
||
- **Comments**: table `comments` avec thread (parent_id), resolved
|
||
- **Page History**: table `page_history`, snapshot JSON
|
||
- **Favorites**: table `favorites`, add/remove/list
|
||
- **Templates**: `database_templates` + `page_templates`, apply API
|
||
- **CSV Import/Export**: `POST .../import/csv`, `GET .../export/csv`
|
||
- **Public Sharing**: `GET /workspace/public/{id}` (read-only, no auth)
|
||
|
||
### Tests
|
||
- 67/67 passent (+7 tests v2.0)
|
||
|
||
## v1.9.0 (2026-07-10) — My Tasks Dashboard
|
||
|
||
### Added
|
||
- **My Tasks**: `GET /my-tasks` (HTML), `GET /my-tasks/api` (JSON)
|
||
- Agrégation cross-collection, vues All/Today/Overdue/Next 7 days
|
||
|
||
## v1.8.0 (2026-07-10) — Sub-items & Dependencies
|
||
|
||
### Added
|
||
- **Sub-items**: parent_id auto-référence, GET/POST sub-items
|
||
- **Status aggregate**: `GET .../status-aggregate`
|
||
- **Dependencies**: `POST .../dependencies`, `POST .../check-deps`
|
||
|
||
## v1.7.0 (2026-07-10) — Vues Améliorées
|
||
|
||
### Added
|
||
- **View config**: `PUT /db/views/{id}/config` (group_by, card_size, cover, visible)
|
||
- **Save view as**: `POST /db/{id}/views/save-as`
|
||
- **List views**: `GET /db/{id}/views/api`
|
||
|
||
## v1.6.0 (2026-07-10) — Vues Manquantes
|
||
|
||
### Added
|
||
- **Calendar**: grid mensuel, navigation mois
|
||
- **Gallery**: cartes visuelles, card_size configurable
|
||
- **List**: vue compacte avec preview
|
||
- **Timeline**: barres horizontales Gantt
|
||
- **Table**: rendu SSR avec colonnes properties
|
||
- **View tabs**: navigation Table/Board/Calendar/Gallery/List/Timeline
|
||
|
||
## v1.5.0 (2026-07-10) — Relations & Rollups
|
||
|
||
### Added
|
||
- **FormulaEngine**: 19 fonctions (prop, if, concat, round, now, today, dateAdd, replace, ...)
|
||
- **RollupEngine**: 12 agrégations (count, sum, avg, min, max, range, unique, percent_checked)
|
||
- **Relation**: lien bidirectionnel, API create/link
|
||
|
||
## v1.4.0 (2026-07-10) — Propriétés Avancées
|
||
|
||
### Added
|
||
- **Table `collection_properties`**: 21 types Notion
|
||
- **Service `property_types.py`**: validation, formatage, auto-values
|
||
|
||
## v1.3.0 (2026-07-10) — Database Concept
|
||
|
||
### Added
|
||
- **Tables**: `collections`, `collection_pages`, `collection_views`
|
||
- **Router `/db`**: CRUD collections + pages (16 endpoints)
|
||
- **GiteaBoardCompat**: adaptateur boards legacy → Collections
|
||
|
||
## v1.0.0 (2026-07-08) — Production
|
||
|
||
### Added
|
||
- FastAPI lifespan, 28 tests, CI/CD Gitea Actions, CSS 31KB, 10 templates
|
||
- 5 vues: Kanban, Table, Status Overview, Team Load, Detailed Board
|
||
- Filtres cumulables AND, tri multi-critères
|
||
- Card detail modal, checklists, commentaires Gitea
|
||
- Propriétés custom, AI keywords, sync API
|