feat(diarization-ui): replace browser prompt/confirm with in-app JS modal dialogs on document and library pages
This commit is contained in:
90
app.py
90
app.py
@@ -128,12 +128,46 @@ pre{{white-space:pre-wrap;background:#020617;color:#86efac;padding:12px;border-r
|
||||
small{{color:var(--muted)}}
|
||||
.hint{{color:var(--muted);font-size:13px}}
|
||||
@media (max-width:900px){{nav{{width:86px;padding:10px}} nav a span{{display:none}} .brand{{font-size:12px}} main{{padding:12px}}}}
|
||||
.modal-backdrop{{position:fixed;inset:0;background:rgba(0,0,0,.55);display:none;align-items:center;justify-content:center;z-index:2000}}
|
||||
.modal{{width:min(92vw,520px);background:#0b1222;border:1px solid #334155;border-radius:16px;padding:14px;box-shadow:0 24px 60px rgba(0,0,0,.5)}}
|
||||
.modal h4{{margin:0 0 8px 0}}
|
||||
.modal .actions{{display:flex;gap:8px;justify-content:flex-end;margin-top:10px}}
|
||||
.modal input,.modal select{{width:100%}}
|
||||
.iconbtn{{text-decoration:none;font-size:18px;padding:2px 6px;border-radius:8px;border:1px solid #334155;background:#0b1222}}
|
||||
</style>
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {{ window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js').catch(()=>{{}})); }}
|
||||
|
||||
window.showModal = function({{title='Eingabe', html='', ok='OK', cancel='Abbrechen'}}) {{
|
||||
return new Promise((resolve) => {{
|
||||
const bd=document.getElementById('modal-backdrop');
|
||||
const box=document.getElementById('modal-box');
|
||||
box.innerHTML = `<h4>${{title}}</h4><div>${{html}}</div><div class='actions'><button id='m-cancel' type='button' style='background:#334155;color:#fff'>${{cancel}}</button><button id='m-ok' type='button'>${{ok}}</button></div>`;
|
||||
bd.style.display='flex';
|
||||
const close=(v)=>{{bd.style.display='none'; resolve(v);}};
|
||||
box.querySelector('#m-cancel').onclick=()=>close(null);
|
||||
box.querySelector('#m-ok').onclick=()=>{{
|
||||
const inp=box.querySelector('[data-modal-input]');
|
||||
if(inp) close(inp.value); else close(true);
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
window.uiPrompt = async function(title, value='') {{
|
||||
const v = await window.showModal({{title, html:`<input data-modal-input value="${{String(value).replace(/"/g,'"')}}">`}});
|
||||
return v;
|
||||
}};
|
||||
window.uiConfirm = async function(title) {{
|
||||
const v = await window.showModal({{title, html:`<p class='hint'>Bitte bestätigen.</p>`, ok:'Ja', cancel:'Nein'}});
|
||||
return v !== null;
|
||||
}};
|
||||
window.uiSelect = async function(title, options, placeholder='') {{
|
||||
const opts = options.map(o=>`<option value="${{o.value}}">${{o.label}}</option>`).join('');
|
||||
return await window.showModal({{title, html:`<select data-modal-input><option value="">${{placeholder}}</option>${{opts}}</select>`}});
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id='modal-backdrop' class='modal-backdrop'><div id='modal-box' class='modal'></div></div>
|
||||
<nav>
|
||||
<div class='brand'>🎙️ Audio Copilot</div>
|
||||
<a href='/'><span>⤴️</span><span>Upload</span></a>
|
||||
@@ -310,23 +344,20 @@ def library(project_id: Optional[int] = None):
|
||||
p_opts = "<option value=''>Alle</option>" + "".join(
|
||||
[f"<option value='{p['id']}' {'selected' if project_id==p['id'] else ''}>{p['name']}</option>" for p in projects]
|
||||
)
|
||||
proj_opts = "".join([f"<option value='{p['id']}'>{p['name']}</option>" for p in projects])
|
||||
items = "".join(
|
||||
[
|
||||
f"<div class='card'><b>#{d['id']}</b> [{d['kind']}] {d['title']}<br><small>{d['project']} · {d['created_at']}</small><br>"
|
||||
f"<div class='row' style='margin-top:8px'>"
|
||||
f"<a href='/document/{d['id']}' style='text-decoration:none'><button type='button'>👁️ Ansehen</button></a>"
|
||||
f"<a href='/document/{d['id']}/download.md' style='text-decoration:none'><button type='button'>⬇️ .md</button></a>"
|
||||
f"</div>"
|
||||
f"<form method='post' action='/document/{d['id']}/rename' class='row' style='margin-top:8px'>"
|
||||
f"<input name='title' value='{d['title']}'><button>✏️ Umbenennen</button></form>"
|
||||
f"<form method='post' action='/document/{d['id']}/move' class='row' style='margin-top:6px'>"
|
||||
f"<select name='project_id'>{proj_opts}</select><button>📁 Verschieben</button></form>"
|
||||
f"<form method='post' action='/document/{d['id']}/delete' onsubmit=\"return confirm('Dokument löschen?')\" style='margin-top:6px'>"
|
||||
f"<button>🗑️ Löschen</button></form></div>"
|
||||
f"<a href='/document/{d['id']}/download.md' class='iconbtn' title='Download'>⬇️</a>"
|
||||
f"<a href='#' class='iconbtn' title='Umbenennen' onclick=\"libRename({d['id']}, {json.dumps(d['title'])});return false;\">✏️</a>"
|
||||
f"<a href='#' class='iconbtn' title='Verschieben' onclick=\"libMove({d['id']});return false;\">📁</a>"
|
||||
f"<a href='#' class='iconbtn' title='Löschen' onclick=\"libDelete({d['id']});return false;\">🗑️</a>"
|
||||
f"</div></div>"
|
||||
for d in docs
|
||||
]
|
||||
)
|
||||
project_js = json.dumps([{"value": p["id"], "label": p["name"]} for p in projects], ensure_ascii=False)
|
||||
body = f"""
|
||||
<h2>Datenbank / Dokumente</h2>
|
||||
<form method='get' class='row card'>
|
||||
@@ -335,6 +366,29 @@ def library(project_id: Optional[int] = None):
|
||||
<button type='submit'>Filtern</button>
|
||||
</form>
|
||||
{items or '<p>Keine Einträge.</p>'}
|
||||
<script>
|
||||
async function libPost(url, data) {{
|
||||
const r = await fetch(url, {{method:'POST', headers:{{'Content-Type':'application/x-www-form-urlencoded'}}, body:new URLSearchParams(data)}});
|
||||
if(!r.ok) {{ alert('Fehler '+r.status); return; }}
|
||||
location.reload();
|
||||
}}
|
||||
window.libRename = async function(id, current) {{
|
||||
const v = await window.uiPrompt('Dokument umbenennen', current || '');
|
||||
if(v===null) return;
|
||||
await libPost(`/document/${{id}}/rename`, {{title:v}});
|
||||
}};
|
||||
window.libMove = async function(id) {{
|
||||
const options = {project_js};
|
||||
const v = await window.uiSelect('In Projekt verschieben', options, 'Projekt wählen');
|
||||
if(v===null || v==='') return;
|
||||
await libPost(`/document/${{id}}/move`, {{project_id:v}});
|
||||
}};
|
||||
window.libDelete = async function(id) {{
|
||||
const ok = await window.uiConfirm('Dokument löschen?');
|
||||
if(!ok) return;
|
||||
await libPost(`/document/${{id}}/delete`, {{}});
|
||||
}};
|
||||
</script>
|
||||
"""
|
||||
return layout("Library", body)
|
||||
|
||||
@@ -357,7 +411,6 @@ def view_document(doc_id: int):
|
||||
|
||||
rendered = md.markdown(d["content_md"] or "", extensions=["fenced_code", "tables", "nl2br"])
|
||||
projects = get_projects()
|
||||
project_list = "\\n".join([f"{p['id']}: {p['name']}" for p in projects])
|
||||
|
||||
body = f"""
|
||||
<h2>Dokument #{d['id']} – {d['title']}</h2>
|
||||
@@ -378,19 +431,20 @@ window.postForm = async function(url, data) {{
|
||||
if (!r.ok) {{ alert('Fehler: '+r.status); return; }}
|
||||
location.href = '/library';
|
||||
}};
|
||||
window.renameDoc = function() {{
|
||||
const v = prompt('Neuer Dokumentname:', {json.dumps(d['title'])});
|
||||
window.renameDoc = async function() {{
|
||||
const v = await window.uiPrompt('Neuer Dokumentname', {json.dumps(d['title'])});
|
||||
if (v===null) return;
|
||||
window.postForm('/document/{doc_id}/rename', {{title:v}});
|
||||
}};
|
||||
window.moveDoc = function() {{
|
||||
const info = {json.dumps(project_list)};
|
||||
const v = prompt('Ziel-Projekt-ID eingeben\\n'+info, '');
|
||||
if (v===null) return;
|
||||
window.moveDoc = async function() {{
|
||||
const options = {json.dumps([{"value": p['id'], "label": p['name']} for p in projects], ensure_ascii=False)};
|
||||
const v = await window.uiSelect('In Projekt verschieben', options, 'Projekt wählen');
|
||||
if (v===null || v==='') return;
|
||||
window.postForm('/document/{doc_id}/move', {{project_id:v}});
|
||||
}};
|
||||
window.deleteDoc = function() {{
|
||||
if (!confirm('Dokument wirklich löschen?')) return;
|
||||
window.deleteDoc = async function() {{
|
||||
const ok = await window.uiConfirm('Dokument wirklich löschen?');
|
||||
if (!ok) return;
|
||||
window.postForm('/document/{doc_id}/delete', {{}});
|
||||
}};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user