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
This commit is contained in:
+24
-1
@@ -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 `<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.
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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;})})})
|
||||
|
||||
+7
-6
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user