diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b3b97..9f8ad62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ # Changelog — FlowDeck -## v5.1.3 (2026-09-07) — Corrections : AI inline (400), bloc vide, menu `/`, poignée, contour +## v5.1.4 (2026-09-07) — Corrections : placeholder invisible, flèches `/`, item vocabulaire, largeur AI + +> 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. diff --git a/VERSION b/VERSION index 935851a..76e9e61 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.3 \ No newline at end of file +5.1.4 diff --git a/app/main.py b/app/main.py index eb7bf9f..7652367 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.3", + version="5.1.4", docs_url="/docs" if settings.log_level == "DEBUG" else None, redoc_url=None, lifespan=lifespan, diff --git a/app/templates/_page_editor_scripts.html b/app/templates/_page_editor_scripts.html index 65da18a..8e9be54 100644 --- a/app/templates/_page_editor_scripts.html +++ b/app/templates/_page_editor_scripts.html @@ -7,10 +7,10 @@ {id:'paragraph',name:'Text',icon:'¶'},{id:'heading_1',name:'Heading 1',icon:'H1'},{id:'heading_2',name:'Heading 2',icon:'H2'}, {id:'heading_3',name:'Heading 3',icon:'H3'},{id:'heading_4',name:'Heading 4',icon:'H4'},{id:'bulleted_list',name:'Bulleted list',icon:'•'},{id:'numbered_list',name:'Numbered list',icon:'1.'}, {id:'to_do',name:'To-do',icon:'☐'},{id:'toggle',name:'Toggle',icon:'▶'},{id:'quote',name:'Quote',icon:'❝'}, - {id:'callout',name:'Callout',icon:'lightbulb'},{id:'table_of_contents',name:'Table of contents',icon:'☰'},{id:'math',name:'Math equation',icon:'∑'}, + {id:'callout',name:'Callout',icon:'💡'},{id:'table_of_contents',name:'Table of contents',icon:'☰'},{id:'math',name:'Math equation',icon:'∑'}, {id:'columns',name:'Column list',icon:'▮'},{id:'divider',name:'Divider',icon:'—'},{id:'code',name:'Code block',icon:'<>'}, {id:'table',name:'Table',icon:'⊞'}, - ]},{name:'MEDIA',items:[{id:'image',name:'Image',icon:'image'}]}, + ]},{name:'MEDIA',items:[{id:'image',name:'Image',icon:'🖼️'}]}, {name:'DATA',items:[{id:'database',name:'Database / Inline database',icon:'📊'}]}]; let _bid=0;function genId(){return 'b'+(++_bid)+'_'+Date.now().toString(36);} @@ -220,10 +220,23 @@ footer.onclick=()=>SM.close(); const inp=document.createElement('input');inp.type='text';inp.placeholder='Type to search'; inp.style.cssText='width:100%;padding:9px 12px;background:var(--bg-secondary);border:none;border-top:1px solid var(--border);color:var(--text-primary);font-size:14px;outline:none;box-sizing:border-box'; - inp.oninput=()=>SM.flt();inp.onkeydown=ev=>SM._kd(ev); + inp.oninput=()=>SM.flt(); e.appendChild(list);e.appendChild(footer);e.appendChild(inp); SM._e=e;SM._in=inp;SM._li=list;SM._f=footer; document.addEventListener('mousedown',ev=>{if(!e.contains(ev.target))SM.close();}); + if(!SM._doc){ + SM._doc=true; + document.addEventListener('keydown',function(ev){ + if(!SM._o)return; + if(ev.metaKey||ev.ctrlKey||ev.altKey)return; + if(ev.key==='ArrowDown'||ev.key==='ArrowUp'||ev.key==='Enter'||ev.key==='Escape'){ + ev.preventDefault();ev.stopPropagation();SM._docKd(ev); + } + }); + } + SM._docKd=function(ev){ + if(SM._o&&(ev.key==='ArrowDown'||ev.key==='ArrowUp'||ev.key==='Enter'||ev.key==='Escape'))SM._kd(ev); + }; }, _hover(it){ if(!it) return; const id=it.dataset.sid; if(!id) return; if(!SM._pv){ const d=document.createElement('div'); d.className='sm-preview'; d.style.cssText='position:fixed;z-index:1000;background:var(--bg-modal);border:1px solid var(--border-light);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.5);padding:12px;width:220px;pointer-events:none;'; document.body.appendChild(d); SM._pv=d; } @@ -253,7 +266,7 @@ else if(e.key==='ArrowUp'){e.preventDefault();SM._s=Math.max(SM._s-1,0);SM._rs();} else if(e.key==='Enter'){e.preventDefault();const c=SM._it[SM._s];if(c)SM.sel(c.id);} else if(e.key==='Escape'){e.preventDefault();SM.close();}}, - _rs(){const its=SM._li.querySelectorAll('.slash-item');its.forEach((el,i)=>{el.classList.toggle('selected',i===SM._s);});}, + _rs(){const its=SM._li.querySelectorAll('.slash-item');its.forEach((el,i)=>{el.classList.toggle('selected',i===SM._s);});const cur=its[SM._s];if(cur&&cur.scrollIntoView)cur.scrollIntoView({block:'nearest'});}, sel(id){if(window.E)window.E.applySlash(id);SM.close();}, get isOpen(){return SM._o;}}; @@ -266,14 +279,16 @@ const d=document.createElement('div'); d.id='_aiC'; d.style.cssText='position:fixed;z-index:1200;width:420px;max-width:92vw;display:none'; document.body.appendChild(d); this.ROOT=d; return d; }, open(idx){ const self=this; this._idx=idx; this._stage='input'; this.mk(); - const r=this._anchorRect(idx); this.ROOT.style.left=Math.max(8,Math.min(r.left,window.innerWidth-440))+ 'px'; + const r=this._anchorRect(idx); + const w=Math.max(r.width||0,280); this.ROOT.style.width=Math.min(w,1200)+'px'; + this.ROOT.style.left=Math.max(8,Math.min(r.left,window.innerWidth-r.width-16))+ 'px'; this.ROOT.style.top=Math.max(8,(r.bottom+6>window.innerHeight-260)?r.top-180:r.bottom+6)+'px'; this.paint(); this.ROOT.style.display='block'; this._open=true; this._focusIngest(); document.addEventListener('mousedown',function onDoc(ev){ if(!self.ROOT.contains(ev.target)){ self.close(); document.removeEventListener('mousedown',onDoc); } }); }, close(){ if(this.ROOT)this.ROOT.style.display='none'; this._open=false; this._html=''; if(window.E)window.E.dirty=true; this._stage=''; }, - _anchorRect(idx){ const E=window.E; if(!E||!E.blocks)return{x:Math.max(24,window.innerWidth/2-210),bottom:window.innerHeight/2}; - const b=E.blocks[idx]; const el=E.getEl(b?b.id:null); if(el){const r=el.getBoundingClientRect();return r;} return {left:24,top:120,bottom:180}; }, + _anchorRect(idx){ const E=window.E; if(!E||!E.blocks)return{left:Math.max(24,window.innerWidth/2-210),top:window.innerHeight/2,bottom:window.innerHeight/2,width:420}; + const b=E.blocks[idx]; const el=E.getEl(b?b.id:null); if(el){const r=el.getBoundingClientRect();return r;} return {left:24,top:120,bottom:180,width:420}; }, _focusIngest(){ const i=this.ROOT.querySelector('.aici-input'); if(i){i.focus();} }, _escTag(tag,s){return '<'+tag+'>'+s+'';}, _mdToHtml(md){ // compact block render (headings, lists, bold, paragraphs) @@ -1088,7 +1103,7 @@ applyAIBlocks(text){ const{el,bid,idx}=ab;const block=this.blocks[idx];if(!block)return; if(SM.isOpen){ if(e.key==='Escape'){e.preventDefault();SM.close();return;} - if(e.key==='ArrowDown'||e.key==='ArrowUp'||e.key==='Enter'){e.preventDefault();SM._in.dispatchEvent(new KeyboardEvent('keydown',{key:e.key,bubbles:true}));return;} + if(e.key==='ArrowDown'||e.key==='ArrowUp'||e.key==='Enter'){e.preventDefault();return;} if(e.key==='Backspace'&&!SM._q&&(el.textContent||'').trim()==='/'){e.preventDefault();SM.close();return;} return; } diff --git a/static/css/app.css b/static/css/app.css index 9d5f0d7..d02ab50 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -2334,6 +2334,7 @@ button.breadcrumb-current { max-width: 260px; } min-height: 28px; word-break: break-word; } +.block-content [data-bid].empty::before, .block-content.empty::before { content: attr(data-placeholder); color: var(--text-secondary); @@ -4585,7 +4586,7 @@ button.breadcrumb-current { max-width: 260px; } @keyframes ai-pulse { 0%,100%{opacity:.45} 50%{opacity:1} } .slash-menu { flex-direction: column; } .slash-menu .sm-footer:hover { background: var(--bg-hover); } -.slash-item.selected { background: rgba(35,131,226,.16); box-shadow: inset 2px 0 0 var(--accent,#2383E2); } +.slash-item.selected { background: rgba(35,131,226,.16) !important; box-shadow: inset 2px 0 0 var(--accent,#2383E2) !important; } .slash-item.selected .slash-item-name { color: var(--text); } .slash-item-hint { font-size: 12px; color: var(--text-dim); opacity: .7; } .sm-preview { font-family: inherit; }