- board.py: pass embed_mode=True to template context when ?embed=1
- base.html: <body class="embed-mode"> when embed_mode is true
- CSS: body.embed-mode hides .sidebar, .topbar, .unified-header,
.header-actions; removes app-layout margin/padding; adjusts editor
padding and max-width for peek panel
- Sidebar and header now properly hidden in iframe via CSS
Architecture for zero code duplication:
- Extract page editor HTML into _page_editor_content.html (shared include)
- page_editor.html includes it (same behavior as before)
- page_editor_embed.html includes it with empty topbar (no header in peek)
- board.py: ?embed=1 renders page_editor_embed.html
Peek panels now load /pages/{id}?embed=1 in iframe:
- Editor renders without sidebar or header breadcrumb/actions
- Full editing capability (blocks, markdown, slash commands, save)
- Same code — one change to _page_editor_content.html updates everywhere
Reverted the iframe-based peek panels (d13f137, 99fee56) which caused:
- Sidebar/header still visible inside iframe despite embed CSS
- Resize handle broken by iframe layout
Restored:
- Vanilla JS content loading via API (_loadPreviewContentVanilla)
- Peek-body div instead of iframe
- Resize handles on both library and local-workspace panels
(click=close, drag=resize, width persisted in localStorage)
- Hide .unified-header and .header-actions in embed-mode (not just .topbar)
- Hide .topbar-right specifically (Edited, Share, Copy link, Favorite, ...)
- Set .page-editor-wrapper padding-top to 12px (no header gap)
- Only the document editor/content is visible in the iframe now
The .peek-header class from library.html CSS was not available on the
local-workspace page, causing header buttons to stack vertically.
Added inline display:flex;align-items:center;gap:4px on the header div.
- Same 560px width, same header layout (close | icon+title | open | fav | copy | more)
- Header uses peek-header class from library CSS for identical styling
- Meta info (type, size, date) moved inline into body as subtle header
- Tags moved inline into body below meta
- More menu with Open, Favorite, Copy link, Close actions
- Copy link button uses navigator.clipboard API with toast feedback
- Bottom action buttons removed (now in header + more menu)
- Empty state matches library (file icon + 'Select a file to preview')
Root cause: blocks format content is a direct array [{id,type,content}],
not wrapped in {blocks:[...]}. Old code checked blocks.blocks which was
always undefined, so internal pages showed nothing.
Fixes:
- Handle blocks as raw array or {blocks:[...]} wrapper (both formats)
- Add heading styling (h3-h6 based on heading_1/2/3), code blocks (pre)
- Add proper markdown format handler (renders as pre-wrapped text)
- Add format=file handler showing file metadata + Open file link
- Add unknown format fallback as pre-wrapped text with char limit
- Clean up escaped quotes — no more double-escaped \" in strings
- Better empty state messages (Empty document, No content)
Alpine 3.14.9 x-if and x-show are fundamentally broken for reactive
visibility toggling of the preview panel. Complete rewrite:
- Replace all Alpine directives in preview panel with static HTML + IDs
- selectForPreview(): vanilla JS to populate fields and show panel
- closePreviewPanel(): vanilla JS to hide panel
- _loadPreviewContentVanilla(): async content loader using innerHTML
- Open button wired via onclick to navigate to /pages/{id}
- No Alpine reactivity dependencies - panel just works
- Keep outer x-show on preview-panel div for transition animation
- Wrap all inner content referencing previewItem in <template x-if=previewItem>
to prevent Alpine from evaluating expressions when previewItem is null
- Clean up debug console.logs from earlier debugging
- This combines x-show (reliable visibility toggle) with x-if (null guard)
Root cause: Alpine 3.14.9 x-if on template element doesn't reliably re-render
when a reactive property changes. previewItem was being set (confirmed by logs)
but x-if never re-evaluated.
Fix: replace <template x-if> wrapper with direct x-show on the preview-panel div.
x-show with x-transition is the reliable pattern for this Alpine version.
- Add console.log in selectForPreview and openSidePeek to trace button clicks
- Add console.log in Alpine OPEN button click handler
- Remove redundant x-show from preview-panel (was conflicting with x-if transition)
- This should fix the preview panel not appearing when Open button is clicked
- Add CSS rule .ws-tree-item:hover .hover-only for tree item hover visibility
- Move OPEN button OUT of .actions div, place it right after title span with
margin-left:auto and .hover-only class (matches Library layout exactly)
- Fix renderChildren() static HTML: button now between name span and .actions
- Remove duplicate button from inside .actions div
- Button calls selectForPreview(node) or openSidePeek(id) for side preview panel
- Add openSidePeek(id) helper method to find node by id and open in side preview
- Update renderChildren() static HTML OPEN button: replace full page nav with selectForPreview
- Add OPEN button in Alpine x-for tree template actions div for file items
- Uses existing preview-panel (right side panel) instead of navigating away
- 'Get started with' Database/Form buttons now POST to /db/inline/api
- Creates real collections linked to the page via parent_page_id
- Inline DBs rendered as embed blocks with links to collection views
- Persisted in page blocks JSON (embed_type='inline_dbs')
- Templates button opens the 'More' dropdown with all view types
- 175 tests pass
- Add is_task flag on collections (toggle-task API)
- New page_dependencies table with auto_shift config (blocks/blocked_by/related)
- API: GET/POST/DELETE page dependencies
- API: POST auto-shift dates based on blocking dependencies (skip_weekends option)
- 5 new tests (171 total)
- Dropdown Filter dans la toolbar (icône filtre, à côté de Sort)
- Menu avec Folders, Pages, PDF, Images, Code, Text + Clear filter
- doFilter() applique maintenant filterType au tree view via _filterTree()
- Filter chips visibles dans toutes les vues comme indicateur
- L'enhanced table prend le relais quand un filtre est actif dans les vues non-tree
143 tests passent
- dashboard.py: quote(filename) dans file_url (espaces/caractères spéciaux)
- page_editor.html: iframe PDF → onerror fallback avec lien Open in new tab
- 143 tests passent
Maintenant au bon endroit: base.html sidebar-section-actions (L229-233)
Pas dans local_workspace.html tree (où il n'était pas visible).
143 tests passent
Alpine ne résolvait pas wsId dans le scope x-for.
Maintenant: href='/local-workspace' (route qui utilise le cookie ws actif).
+ hard refresh nécessaire (Ctrl+Shift+R) pour vider cache ancien JS.
143 tests passent
_header.html L54: {{ page_icon }} affichait le mot 'home'/'file'/'paperclip'
au lieu de l'icône SVG. Maintenant détecte les noms connus → fd_icon().
Fallback pour les valeurs non-reconnues: affiche tel quel.
143 tests passent
page_editor.html L5: le bouton star utilisait x-text avec fd_icon('star',14)
contenant des guillemets doubles SVG → Expression Error.
→ x-html + getSvgIcon('star',14) (JS, pas de guillemets dans l'expression)
→ share/lock/link: utilisent déjà la concaténation ~ (safe)
143 tests passent
- Fichier VERSION à la racine (4.0.3)
- _get_app_version() avec cache dans dashboard.py
- Lecture depuis VERSION (dev) ou /app/VERSION (Docker)
- Fallback '0.0.0' si fichier absent
- board.py: import _get_app_version depuis dashboard
- Les deux _sidebar_data() remplacent '4.0.3' par _get_app_version()
- Déploiement: changer VERSION → mettre à jour sans toucher le code
- 143 tests passent
- List (compact): rangée '..' avec icône folder avant les données
- Details (enhanced): rangée '..' avec toutes les colonnes
- Cards (title grid): carte '..' dans la grille
- Content (content list): élément '..' avec preview 'Parent folder'
- x-show='currentFolder > 0' pour n'afficher que dans un sous-dossier
- 143 tests passent
Document Notion Agent (2026) concepts and a FlowDeck Agent design
that orchestrates tool calls through existing FastAPI routers, plus
a chat UI screenshot.
- Workspaces: bouton bleu '+' maintenant centré, 48px, min-height 140px (plus coupé)
- Library no-workspace: 'Open a Workspace...' avec lien Go to Workspaces au lieu de Loading
- Library: boutons '+ Add new' et 'New page' cachés quand pas de workspace
- Settings: espacement bouton Upload photo (margin-bottom, margin-top augmentés)
- 143 tests passent
- main.py: lifespan insère toujours le hash de 'FlowDeck2026!' pour admin
- dashboard.py + my_tasks.py: content_html passe via {% block content %} (Jinja)
- Avant: content_html ignoré car base.html utilise des blocs, pas des variables
- 143 tests passent
- Section 'Notion apps' retirée
- Liens statiques en bas du sidebar (toujours visibles): Library, My Tasks, Trash, Help
- Nouvelle route /help avec page d'aide (raccourcis, guide rapide)
- Trash visible pour tous (plus de condition sur auth_method)
- 143 tests passent
- Meetings '+' button → gardé avec has_active_workspace
- Sidebar footer 'New page' → gardé avec has_active_workspace
- Context menu New File/New Folder → déjà gardé car l'arbre est caché sans workspace
- Workspace section déjà gardée (commit précédent)
- 143 tests passent
- Ajout flag has_active_workspace dans _sidebar_data (dashboard.py + board.py)
- Template base.html: quand has_active_workspace=False:
- Titre section → '📁 No workspace open'
- Boutons New File/New Folder cachés
- Message 'Open a workspace to see your files'
- Guard JS newPageInWorkspace()/newFolderInWorkspace(): toast + redirect si pas de workspace
- 143 tests passent
- ROADMAP.md: réorganisé avec priorités claires (v4.0.0=complété, v4.0.1=onboarding PRIO MAX)
- Ajout BRANCHING.md: convention main/develop/feat/*, workflow complet
- Branche develop créée et poussée sur Gitea
- Sections Parité Notion réparties en v4.1–v4.10 ordonnées par priorité
- Résumé des phases mis à jour
- createPage() now detects active workspace context (library, local-workspace page, sidebar)
- In a local/Gitea workspace: creates workspace item via /api/local-workspace/items with prompt
- No workspace: creates general Notion page via /board/api/pages (no prompt)
- Added hidden #fd-local-ws-id in local_workspace.html to expose workspace_id
- _confirmCreateFolder() passes workspace_id for Gitea workspaces
- Added _getContext() helper that checks multiple context sources in priority order
- Added _toast() helper for cross-component notifications
- Add global window.FlowDeck namespace with createPage() and showCreateFolderModal()
- createPage() creates a Notion-style page via /board/api/pages (same as sidebar bottom-right button)
- showCreateFolderModal() opens a global themed modal (dark/light) instead of browser prompt()
- All New Page buttons now call window.FlowDeck.createPage()
- All New Folder buttons now call window.FlowDeck.showCreateFolderModal()
- Global New Folder modal uses existing .flowdeck-modal CSS classes for theme consistency
- Updated Ctrl+N keyboard shortcut to use unified createPage()
Le x-show sur #lib-table dépendait de flatItems.length,
qu'Alpine 3.14.9 ne détecte pas. Maintenant _renderTable()
contrôle l'affichage/masquage directement via style.display.
Abandon complet d'Alpine pour le rendu du tableau :
- _renderTable() fait innerHTML directement sur #lib-table-body
- Plus de x-for, plus de x-html, plus de getters
- SortableJS supprimé (causait removeEventListener sur null)
- Fix startRename/commitRenameById pour utiliser les IDs DOM
Le x-for d'Alpine 3.14.9 ne détecte pas les changements d'array.
Remplacement complet par x-html avec génération HTML manuelle.
Ajout de window._libData pour les onclick handlers globaux.
Fonctions _byId pour toggle/expand/rename/open depuis le HTML.