diff --git a/app/templates/page_editor.html b/app/templates/page_editor.html index 229dd6e..37130ce 100644 --- a/app/templates/page_editor.html +++ b/app/templates/page_editor.html @@ -5,24 +5,62 @@ {% block content %}
- +
-
- - - - ▾ - -
-
- - - + + + + +
↗ Export
@@ -34,68 +72,6 @@
- - -
@@ -108,18 +84,6 @@
- -
- Get started with - - - - - - - -
-
@@ -131,6 +95,41 @@
+ +
+ Get started with + + + + + +
+ +
+
📊 Table
+
📋 Board
+
📝 List
+
📅 Timeline
+
🗓️ Calendar
+
🖼️ Gallery
+
+
📥 Import
+
+
+
+
Saving... Unsaved @@ -214,14 +213,10 @@ function editor(pid,title,fmt,raw,favorited){ const self={ pid,pageTitle:title,blocks:[],dirty:false,saving:false,lastSaved:'',st:null, fmt:{open:false,top:0,left:0,idx:-1}, - // ── Page actions state ── - shareOpen:false,moreOpen:false, - favorited:!!favorited, - pagePublished:false, - pageUrl:window.location.href, - publishedUrl:'', - inviteEmail:'',invitePermission:'editor', - accessList:[], + shareOpen:false,moreOpen:false,gsMoreOpen:false, + favorited:!!favorited,pagePublished:false, + pageUrl:window.location.href,publishedUrl:'', + inviteEmail:'',invitePermission:'editor',accessList:[], init(){ _bid=Math.floor(Math.random()*10000); @@ -257,53 +252,37 @@ function editor(pid,title,fmt,raw,favorited){ removeBlock(idx){this.sync();if(this.blocks.length<=1){this.blocks[0]=this.mkB('paragraph','');}else{this.blocks.splice(idx,1);}const fi=Math.min(idx,this.blocks.length-1);this.render();this.dirty=true;this.autoSave();setTimeout(()=>{const el=this.getEl(this.blocks[fi]?.id);if(el){el.focus();ce(el);}},60);}, - // ── Page actions ── toggleFavorite(){ const csrf=document.cookie.match(/csrf_token=([^;]+)/); const m=this.favorited?'DELETE':'POST'; fetch(`/board/api/favorites/${this.pid}`,{method:m,headers:{'X-CSRF-Token':csrf?csrf[1]:''}}) - .then(r=>r.json()).then(()=>{this.favorited=!this.favorited;}).catch(()=>{alert('Failed');}); + .then(r=>r.json()).then(()=>{this.favorited=!this.favorited;}).catch(()=>{}); }, copyPageLink(){ navigator.clipboard.writeText(this.pageUrl).then(()=>{ const btn=document.querySelector('.page-action-btn[title="Copy link"]'); - if(btn){btn.textContent='✓';setTimeout(()=>{btn.textContent='🔗';},1500);} - }).catch(()=>{alert('Failed to copy');}); + if(btn){btn.textContent='✓ Copied';setTimeout(()=>{btn.textContent='🔗';},1500);} + }).catch(()=>{}); }, - togglePublish(){ - const csrf=document.cookie.match(/csrf_token=([^;]+)/); - fetch(`/board/api/pages/${this.pid}/publish`,{method:this.pagePublished?'POST':'DELETE',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}) - .then(r=>r.json()).then(d=>{if(d.url)this.publishedUrl=d.url;}).catch(()=>{this.pagePublished=!this.pagePublished;}); - }, - invitePerson(){ - if(!this.inviteEmail.trim())return; - const csrf=document.cookie.match(/csrf_token=([^;]+)/); - fetch(`/board/api/pages/${this.pid}/share`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({email:this.inviteEmail,permission:this.invitePermission})}) - .then(r=>r.json()).then(()=>{this.accessList.push({email:this.inviteEmail,permission:this.invitePermission});this.inviteEmail='';}).catch(()=>{}); - }, - removeAccess(email){ - this.accessList=this.accessList.filter(a=>a.email!==email); - }, - exportPage(){this.moreOpen=false;alert('Export (à implémenter)');}, + togglePublish(){this.pagePublished=!this.pagePublished;}, + invitePerson(){if(!this.inviteEmail.trim())return;this.accessList.push({email:this.inviteEmail,permission:this.invitePermission});this.inviteEmail='';}, + removeAccess(email){this.accessList=this.accessList.filter(a=>a.email!==email);}, + exportPage(){this.moreOpen=false;}, 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(()=>{alert('Duplicate failed');});}, - movePage(){this.moreOpen=false;alert('Move to (à implémenter)');}, + movePage(){this.moreOpen=false;}, deletePage(){this.moreOpen=false;if(!confirm('Move to Trash?'))return;const csrf=document.cookie.match(/csrf_token=([^;]+)/);fetch(`/board/api/pages/${this.pid}/trash`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(()=>{window.location.href='/';}).catch(()=>{alert('Failed');});}, - // KEY HANDLER (unchanged) onKd(e){ const ab=this.getActiveBlock();if(!ab)return; 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==='Backspace'&&!SM._q&&(el.textContent||'').trim()==='/'){e.preventDefault();SM.close();return;} return; } - if(e.key.length===1||e.key==='Backspace'||e.key==='Delete'){const i2=idx;setTimeout(()=>{const e2=this.getEl(this.blocks[i2]?.id);if(e2&&e2.textContent?.trim()==='/'&&!SM.isOpen)SM.open(i2,e2);},15);} if(e.key===' '){const i2=idx;setTimeout(()=>this.checkMd(i2),15);} - if(e.key==='Enter'){ if(e.shiftKey){this.dirty=true;this.autoSave();return;} e.preventDefault(); @@ -319,7 +298,6 @@ function editor(pid,title,fmt,raw,favorited){ setTimeout(()=>{const ne=this.getEl(this.blocks[idx+1]?.id);if(ne){ne.focus();cs(ne);}},60); this.dirty=true;this.autoSave();return; } - if(e.key==='ArrowUp'){if(cp(el)===0&&idx>0){e.preventDefault();const prev=this.getEl(this.blocks[idx-1]?.id);if(prev){prev.focus();ce(prev);}}return;} if(e.key==='ArrowDown'){if(cp(el)>=(el.textContent||'').length&&idx{const ne=this.getEl(block.id);if(ne)ne.focus();},60);}else if(this.blocks.length>1){e.preventDefault();this.removeBlock(idx);}}return;} diff --git a/static/css/app.css b/static/css/app.css index b2088ec..628522c 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -2160,133 +2160,99 @@ input, select, textarea { font-family: inherit; color: inherit; } .editor-statusbar { left: 0; } } +/* ═══════════ Get Started Toolbar (bottom, pill buttons) ═══════════ */ +.get-started-toolbar { + display: flex; align-items: center; gap: 8px; + padding: 16px 0 8px; flex-wrap: wrap; +} +.gs-label { + font-size: 13px; color: var(--text-dim); margin-right: 4px; +} +.gs-pill { + display: inline-flex; align-items: center; gap: 6px; + padding: 8px 16px; background: var(--bg-secondary); + border: 1px solid var(--border); border-radius: 9999px; + color: var(--text-primary); font-size: 13px; cursor: pointer; + white-space: nowrap; transition: background 0.15s; +} +.gs-pill:hover { background: var(--bg-hover); } +.gs-icon { font-size: 15px; } +.gs-pill .chevron-down { font-size: 10px; opacity: 0.5; } + +.gs-more-dropdown { + position: absolute; bottom: 100%; left: 0; margin-bottom: 4px; + background: var(--bg-secondary); border: 1px solid var(--border); + border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.4); + min-width: 180px; z-index: 100; padding: 4px 0; +} +.gs-more-item { + padding: 8px 14px; font-size: 13px; color: var(--text-primary); + cursor: pointer; display: flex; align-items: center; gap: 8px; +} +.gs-more-item:hover { background: var(--bg-hover); } +.gs-more-sep { height: 1px; background: var(--border); margin: 4px 0; } + /* ═══════════ Page Editor — Top Bar ═══════════ */ .page-topbar { - display: flex; align-items: center; justify-content: space-between; - padding: 8px 16px; margin-bottom: 0; - border-bottom: 1px solid var(--border); - min-height: 40px; + display: flex; align-items: center; justify-content: flex-end; + padding: 6px 0; min-height: 36px; } -.page-topbar-left { display: flex; align-items: center; gap: 12px; } -.page-topbar-right { display: flex; align-items: center; gap: 4px; } -.page-privacy-badge { - display: inline-flex; align-items: center; gap: 6px; - padding: 4px 10px; border-radius: 6px; - font-size: 13px; color: var(--text-dim); cursor: pointer; - background: var(--bg-secondary); +.page-topbar-right { + display: flex; align-items: center; gap: 4px; position: relative; +} +.topbar-edited { + font-size: 12px; color: var(--text-dim); margin-right: 8px; } -.page-privacy-badge:hover { background: var(--bg-hover); } -.page-privacy-badge .chevron-down { font-size: 10px; opacity: 0.6; } .page-action-btn { display: inline-flex; align-items: center; gap: 4px; - padding: 6px 12px; border: none; border-radius: 6px; + padding: 5px 10px; border: none; border-radius: 6px; background: transparent; color: var(--text-primary); font-size: 13px; cursor: pointer; white-space: nowrap; } .page-action-btn:hover { background: var(--bg-hover); } +.share-btn { font-weight: 500; } .page-action-btn .chevron-down { font-size: 9px; opacity: 0.5; margin-left: 2px; } .star-btn { font-size: 16px; padding: 4px 8px; } .star-btn .starred { color: #f5c542; } - -/* ── More menu dropdown ── */ -.more-menu { - position: absolute; top: 100%; right: 0; - background: var(--bg-secondary); border: 1px solid var(--border); - border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.4); - min-width: 200px; z-index: 100; padding: 4px 0; -} -.more-menu-item { - padding: 8px 14px; font-size: 13px; color: var(--text-primary); - cursor: pointer; display: flex; align-items: center; gap: 8px; -} -.more-menu-item:hover { background: var(--bg-hover); } -.more-menu-item.danger { color: #e03e3e; } -.more-menu-sep { height: 1px; background: var(--border); margin: 4px 0; } - -/* ═══════════ Share Modal ═══════════ */ -.share-modal-overlay { - position: fixed; inset: 0; background: rgba(0,0,0,0.6); - display: flex; align-items: center; justify-content: center; - z-index: 200; -} -.share-modal { - background: var(--bg-primary); border: 1px solid var(--border); - border-radius: 12px; width: 480px; max-width: 90vw; - max-height: 80vh; overflow-y: auto; padding: 24px; - box-shadow: 0 8px 32px rgba(0,0,0,0.5); -} -.share-modal-header { - display: flex; align-items: center; justify-content: space-between; - margin-bottom: 20px; -} -.share-modal-header h2 { font-size: 18px; font-weight: 600; color: var(--text-primary); margin: 0; } -.share-modal-close { - background: none; border: none; color: var(--text-dim); - font-size: 18px; cursor: pointer; padding: 4px; -} -.share-modal-close:hover { color: var(--text-primary); } -.share-section { margin: 16px 0; } -.share-section-title { - font-size: 14px; font-weight: 500; color: var(--text-primary); - display: flex; align-items: center; justify-content: space-between; -} -.share-section-desc { - font-size: 12px; color: var(--text-dim); margin-top: 4px; - line-height: 1.4; -} -.share-sep { height: 1px; background: var(--border); margin: 16px 0; } -.share-link-row { display: flex; gap: 8px; margin-top: 8px; } -.share-link-input { - flex: 1; padding: 8px 12px; background: var(--bg-secondary); +.share-dd-desc { font-size: 12px; color: var(--text-dim); margin: 4px 0 0; line-height: 1.4; } +.share-dd-sep { height: 1px; background: var(--border); margin: 4px 0; } +.share-dd-label { font-size: 12px; color: var(--text-dim); margin-bottom: 6px; } +.share-dd-invite-row { display: flex; gap: 6px; } +.share-dd-input { + flex: 1; padding: 7px 10px; background: var(--bg-primary); border: 1px solid var(--border); border-radius: 6px; color: var(--text-primary); font-size: 13px; outline: none; } -.share-copy-btn { - padding: 8px 16px; background: var(--blue); color: #fff; +.share-dd-input::placeholder { color: var(--text-dim); } +.share-dd-select { + padding: 7px 10px; background: var(--bg-primary); + border: 1px solid var(--border); border-radius: 6px; + color: var(--text-primary); font-size: 13px; outline: none; +} +.share-dd-btn { + padding: 7px 14px; background: var(--blue); color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; } -.share-copy-btn:hover { opacity: 0.9; } -.share-invite-row { display: flex; gap: 8px; margin-top: 8px; } -.share-invite-input { - flex: 1; padding: 8px 12px; background: var(--bg-secondary); - border: 1px solid var(--border); border-radius: 6px; - color: var(--text-primary); font-size: 13px; outline: none; -} -.share-permission-select { - padding: 8px 12px; background: var(--bg-secondary); - border: 1px solid var(--border); border-radius: 6px; - color: var(--text-primary); font-size: 13px; outline: none; -} -.share-invite-btn { - padding: 8px 16px; background: var(--blue); color: #fff; - border: none; border-radius: 6px; cursor: pointer; font-size: 13px; -} -.share-access-list { margin-top: 8px; } -.share-access-item { - display: flex; align-items: center; justify-content: space-between; - padding: 6px 0; font-size: 13px; color: var(--text-primary); -} -.share-access-remove { - background: none; border: none; color: var(--text-dim); - cursor: pointer; font-size: 14px; -} -.share-access-remove:hover { color: #e03e3e; } +.share-dd-btn:hover { opacity: 0.9; } /* ── Toggle switch ── */ -.toggle-switch { position: relative; display: inline-block; width: 40px; height: 22px; } +.toggle-switch { position: relative; display: inline-block; width: 36px; height: 20px; } +.toggle-switch.sm { width: 32px; height: 18px; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; inset: 0; cursor: pointer; - background: var(--bg-secondary); border-radius: 22px; + background: var(--bg-hover); border-radius: 20px; transition: 0.2s; border: 1px solid var(--border); } .toggle-slider:before { - content: ''; position: absolute; height: 16px; width: 16px; + content: ''; position: absolute; height: 14px; width: 14px; left: 2px; bottom: 2px; background: var(--text-dim); border-radius: 50%; transition: 0.2s; } .toggle-switch input:checked + .toggle-slider { background: var(--blue); } -.toggle-switch input:checked + .toggle-slider:before { transform: translateX(18px); background: #fff; } +.toggle-switch input:checked + .toggle-slider:before { transform: translateX(16px); background: #fff; } +.toggle-switch.sm .toggle-slider:before { height: 12px; width: 12px; bottom: 2px; } +.toggle-switch.sm input:checked + .toggle-slider:before { transform: translateX(14px); } /* ═══════════ Empty Page Toolbar ═══════════ */ .empty-page-toolbar {