From 3956f1ffa5190c21eed054510973e94b8f0ace47 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Mon, 7 Sep 2026 12:25:29 -0400 Subject: [PATCH] fix(editor): placeholder titre "New Page" + aides de bloc au focus seul - Titre : data-placeholder="New Page" + bascule .empty a l'input (mecanisme des blocs). Le placeholder s'affiche en gris pale quand le titre est vide, y compris apres effacement. save() stocke un titre vide (plus de "New page" force), la sidebar garde son fallback, duplicatePage retombe sur "New Page". - Blocs : les placeholders ("Press 'space' for AI...", "Heading 1", "List"...) passent sous :focus-within -> ne s'affichent que quand le bloc est focalise. Idem pour "Type code..." du bloc code. v5.1.6 --- CHANGELOG.md | 25 ++++++++++++++++++++++++- VERSION | 2 +- app/main.py | 2 +- app/templates/_page_editor_content.html | 1 + app/templates/_page_editor_scripts.html | 6 ++++-- static/css/app.css | 13 +++++++------ 6 files changed, 38 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e526418..af4bc2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,29 @@ # Changelog — FlowDeck -## v5.1.5 (2026-09-07) — Placeholder des blocs : disparaît dès la frappe +## v5.1.6 (2026-09-07) — Placeholder titre "New Page" + placeholder blocs au focus seul + +> 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 `
` 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. diff --git a/VERSION b/VERSION index ed95ef7..7a3e417 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.5 \ No newline at end of file +5.1.6 \ No newline at end of file diff --git a/app/main.py b/app/main.py index 0d7a31e..a4c22ae 100644 --- a/app/main.py +++ b/app/main.py @@ -60,7 +60,7 @@ async def lifespan(_app: FastAPI): app = FastAPI( title="FlowDeck", - version="5.1.5", + version="5.1.6", docs_url="/docs" if settings.log_level == "DEBUG" else None, redoc_url=None, lifespan=lifespan, diff --git a/app/templates/_page_editor_content.html b/app/templates/_page_editor_content.html index 669bcb8..30a9fbe 100644 --- a/app/templates/_page_editor_content.html +++ b/app/templates/_page_editor_content.html @@ -396,6 +396,7 @@ class="page-title-input" contenteditable="true" id="_titleEl" + data-placeholder="New Page" @input="dirty=true;save()" @keydown.enter.prevent="focusBlock()" @paste.prevent="pastePlain($event)" diff --git a/app/templates/_page_editor_scripts.html b/app/templates/_page_editor_scripts.html index 3b3bf4b..54592ce 100644 --- a/app/templates/_page_editor_scripts.html +++ b/app/templates/_page_editor_scripts.html @@ -700,6 +700,8 @@ window.E=this; this.insertTable=function(){this.sync();var nb={id:genId(),type:'table',content:'',rows:_tblNew(3,3)};this.blocks.push(nb);this.dirty=true;this.autoSave();this.render();setTimeout(function(){_tblFocus(nb.id,0,0);},60);}; SM.init();_tblInitDelegate();this.render(); + const tEl=document.getElementById('_titleEl'); + if(tEl){const syncT=()=>{tEl.classList.toggle('empty',!(tEl.textContent||'').trim());};syncT();tEl.addEventListener('input',syncT);tEl.addEventListener('blur',syncT);} setTimeout(()=>{this.focusBlock();const ct=document.getElementById('_blocksCt');if(!ct)return; ct.addEventListener('keydown',e=>this.onKd(e)); ct.addEventListener('paste',e=>{e.preventDefault();document.execCommand('insertText',false,(e.clipboardData||window.clipboardData).getData('text/plain'));this.dirty=true;this.autoSave();}); @@ -977,7 +979,7 @@ applyAIBlocks(text){ doDownload(); } }, - duplicatePage(){this.moreOpen=false;const csrf=document.cookie.match(/csrf_token=([^;]+)/);fetch(`/board/api/pages?title=${encodeURIComponent(this.pageTitle+' copy')}§ion=Private&project=${encodeURIComponent('{{ workspace_key }}')}`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(r=>r.json()).then(d=>{window.location.href=`/pages/${d.id}`;}).catch(()=>{window.showToast('Duplicate failed','error');});}, + duplicatePage(){this.moreOpen=false;const csrf=document.cookie.match(/csrf_token=([^;]+)/);const t=(this.pageTitle||'').trim()||'New Page';fetch(`/board/api/pages?title=${encodeURIComponent(t+' copy')}§ion=Private&project=${encodeURIComponent('{{ workspace_key }}')}`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(r=>r.json()).then(d=>{window.location.href=`/pages/${d.id}`;}).catch(()=>{window.showToast('Duplicate failed','error');});}, movePage(){ this.moreOpen=false; this.moveOpen = true; @@ -1276,7 +1278,7 @@ applyAIBlocks(text){ if(this.fileData)return; if(this.saving){ if(cb) cb(); return; } this.sync(); - const t=document.getElementById('_titleEl');if(t)this.pageTitle=t.textContent?.trim()||'New page'; + const t=document.getElementById('_titleEl');if(t)this.pageTitle=t.textContent?.trim()||''; this.saving=true; const csrf=document.cookie.match(/csrf_token=([^;]+)/); fetch(`/board/api/pages/${this.pid}/blocks`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({title:this.pageTitle,blocks:this.blocks.map(b=>{const c={id:b.id,type:b.type,content:b.content};['checked','expanded','language','icon','src','alt','style','embed_type','collection_id','dbs','file_name','file_size','file_mime','children','rows','align','has_header','colsW','meeting'].forEach(k=>{if(b[k]!=null)c[k]=b[k];});return c;})})}) diff --git a/static/css/app.css b/static/css/app.css index d02ab50..f813df0 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -2205,9 +2205,11 @@ button.breadcrumb-current { max-width: 260px; } border: none; word-break: break-word; } +.page-title-input.empty::before, .page-title-input:empty::before { - content: 'New page'; - color: var(--text-dim); + content: attr(data-placeholder); + color: var(--text-secondary); + opacity: .75; pointer-events: none; } @@ -2334,8 +2336,7 @@ button.breadcrumb-current { max-width: 260px; } min-height: 28px; word-break: break-word; } -.block-content [data-bid].empty::before, -.block-content.empty::before { +.block-content:focus-within [data-bid].empty::before { content: attr(data-placeholder); color: var(--text-secondary); opacity: .75; @@ -2496,9 +2497,9 @@ button.breadcrumb-current { max-width: 260px; } display: block; min-height: 28px; } -.block-code code:empty::before { +.block-code:focus-within code:empty::before { content: 'Type code...'; - color: var(--text-dim); + color: var(--text-secondary); } /* Divider */