pediatric-ai-scribe-v3/public/components/assistant.html
Daniel 629dea808e
Some checks are pending
Forgejo Android APK / Build signed APK (push) Waiting to run
Fix assistant cancel button visibility
2026-05-12 16:20:10 +02:00

207 lines
18 KiB
HTML

<div class="module-header assistant-header">
<div>
<h2><i class="fas fa-brain" style="color:var(--purple);"></i> AI Clinical Assistant</h2>
<p>Ask focused clinical questions over indexed Nextcloud books/resources, or ask for a teaching image directly in the chat box.</p>
</div>
<div class="assistant-status" id="assistant-status">
<span class="assistant-dot"></span>
<span id="assistant-status-text">Ready</span>
</div>
</div>
<div class="assistant-layout">
<section class="assistant-main card">
<div class="assistant-toolbar">
<div>
<strong>Clinical Question</strong>
<span id="assistant-model-label" class="model-tag">Admin model</span>
</div>
<div class="assistant-toolbar-actions">
<button id="btn-assistant-clear" class="btn-sm btn-ghost" type="button"><i class="fas fa-rotate-left"></i> Clear</button>
<button id="btn-assistant-copy" class="btn-sm btn-ghost" type="button"><i class="fas fa-copy"></i> Copy answer</button>
<button id="btn-assistant-save" class="btn-sm btn-ghost" type="button"><i class="fas fa-bookmark"></i> Save chat</button>
<button id="btn-assistant-export-pdf" class="btn-sm btn-ghost" type="button"><i class="fas fa-file-pdf"></i> Export PDF</button>
</div>
</div>
<div id="assistant-messages" class="assistant-messages" aria-live="polite">
<div class="assistant-empty">
<i class="fas fa-book-medical"></i>
<h3>Evidence-first pediatric assistant</h3>
<p>Ask a real clinical question. Citations stay linked to the source cards on the right.</p>
<div class="assistant-examples">
<button type="button" data-assistant-example="In a 4-year-old with acute wheeze, when should magnesium sulfate be considered and what dose is recommended?">Status asthma escalation</button>
<button type="button" data-assistant-example="What are the red flags for bilious vomiting in neonates, and what immediate workup is recommended?">Bilious vomiting</button>
<button type="button" data-assistant-example="Compare bronchiolitis and asthma management in infants, citing clinical references.">Bronchiolitis vs asthma</button>
</div>
</div>
</div>
<form id="assistant-form" class="assistant-composer">
<textarea id="assistant-input" rows="3" placeholder="Ask a focused clinical question..." autocomplete="off"></textarea>
<div class="assistant-composer-footer">
<label class="assistant-check"><input type="checkbox" id="assistant-include-context" checked> retrieve broader context</label>
<button id="btn-assistant-cancel" class="btn-sm btn-ghost" type="button" hidden><i class="fas fa-stop"></i> Cancel search</button>
<button id="btn-assistant-send" class="btn-generate" type="submit"><i class="fas fa-paper-plane"></i> Ask</button>
</div>
</form>
</section>
<aside class="assistant-side">
<div class="card">
<div class="card-header"><h3><i class="fas fa-wand-magic-sparkles"></i> Image / Graph</h3></div>
<div class="assistant-side-body">
<textarea id="assistant-image-prompt" rows="4" placeholder="Generate a teaching image, pathway, or infographic from the current answer..."></textarea>
<div class="assistant-image-buttons">
<button id="btn-assistant-image" class="btn-sm btn-primary" type="button"><i class="fas fa-image"></i> Generate image</button>
<button id="btn-assistant-image-clear" class="btn-sm btn-ghost" type="button"><i class="fas fa-rotate-left"></i> Clear image</button>
</div>
<div id="assistant-visual-output" class="assistant-visual-output"></div>
</div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-bookmark"></i> Saved Chats</h3></div>
<div id="assistant-saved-chats" class="assistant-saved-chats">
<p class="assistant-muted">Saved chats appear here after you click Save chat.</p>
</div>
<div id="assistant-save-panel" class="assistant-save-panel" hidden>
<label for="assistant-save-title">Save chat as</label>
<input id="assistant-save-title" type="text" maxlength="160" autocomplete="off">
<div class="assistant-save-actions">
<button id="btn-assistant-save-confirm" class="btn-sm btn-primary" type="button">Save</button>
<button id="btn-assistant-save-cancel" class="btn-sm btn-ghost" type="button">Cancel</button>
</div>
</div>
</div>
<div class="card">
<div class="card-header"><h3><i class="fas fa-quote-right"></i> Sources</h3></div>
<div id="assistant-sources" class="assistant-sources">
<p class="assistant-muted">Citations appear here after an answer.</p>
</div>
</div>
</aside>
</div>
<style>
.assistant-header { display:flex; justify-content:space-between; gap:12px; align-items:flex-start; }
.assistant-status { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--g500); background:white; border:1px solid var(--g200); border-radius:999px; padding:5px 10px; box-shadow:var(--shadow); }
.assistant-dot { width:8px; height:8px; border-radius:50%; background:var(--green); display:inline-block; }
.assistant-status.busy .assistant-dot { background:var(--amber); animation:pulse 1.5s infinite; }
.assistant-status.error .assistant-dot { background:var(--red); }
.assistant-layout { display:grid; grid-template-columns:minmax(0,1fr) 330px; gap:14px; align-items:start; }
.assistant-layout > * { min-width:0; }
.assistant-main { display:grid; grid-template-rows:auto minmax(420px,1fr) auto; min-height:calc(100vh - 190px); min-width:0; }
.assistant-toolbar { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid var(--g200); background:var(--g50); }
.assistant-toolbar-actions { display:flex; gap:6px; flex-wrap:wrap; }
.assistant-messages { padding:16px; overflow-y:auto; overflow-x:hidden; background:linear-gradient(180deg,#fff,var(--g50)); min-width:0; }
.assistant-empty { max-width:680px; margin:50px auto; text-align:center; color:var(--g500); }
.assistant-empty i { font-size:34px; color:var(--purple); margin-bottom:10px; }
.assistant-empty h3 { color:var(--g800); font-size:18px; margin-bottom:6px; }
.assistant-examples { display:flex; gap:8px; flex-wrap:wrap; justify-content:center; margin-top:16px; }
.assistant-examples button { border:1px solid var(--g200); background:white; color:var(--blue); border-radius:999px; padding:7px 10px; font-size:12px; cursor:pointer; }
.assistant-suggestion-buttons { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }
.assistant-suggestion-buttons button { border:1px solid var(--purple-light); background:#faf5ff; color:var(--purple); border-radius:999px; padding:7px 10px; font-size:12px; cursor:pointer; text-align:left; }
.assistant-suggestion-buttons button:hover { border-color:var(--purple); background:var(--purple-light); }
.assistant-msg { max-width:900px; min-width:0; margin:0 0 14px; display:grid; gap:6px; }
.assistant-msg.user { margin-left:auto; max-width:760px; }
.assistant-msg-label { font-size:11px; font-weight:700; color:var(--g400); text-transform:uppercase; letter-spacing:.04em; }
.assistant-bubble { border:1px solid var(--g200); border-radius:14px; padding:12px 14px; background:white; box-shadow:var(--shadow); font-size:13px; line-height:1.75; overflow-wrap:anywhere; min-width:0; max-width:100%; }
.assistant-msg.user .assistant-bubble { background:var(--blue); color:white; border-color:var(--blue); }
.assistant-bubble h1, .assistant-bubble h2, .assistant-bubble h3 { margin:16px 0 8px; line-height:1.25; color:var(--g900); }
.assistant-bubble h1:first-child, .assistant-bubble h2:first-child, .assistant-bubble h3:first-child { margin-top:0; }
.assistant-bubble h1 { font-size:20px; }
.assistant-bubble h2 { font-size:17px; border-bottom:1px solid var(--g200); padding-bottom:4px; }
.assistant-bubble h3 { font-size:15px; }
.assistant-bubble p { margin:0 0 10px; }
.assistant-bubble p:last-child { margin-bottom:0; }
.assistant-bubble ul, .assistant-bubble ol { padding-left:20px; margin:8px 0; }
.assistant-bubble li { margin:4px 0; }
.assistant-bubble blockquote { margin:10px 0; padding:8px 12px; border-left:3px solid var(--blue); background:var(--blue-light); color:var(--g700); border-radius:8px; }
.assistant-table-scroll { max-width:100%; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; margin:12px 0; border:1px solid var(--g200); border-radius:12px; background:white; box-shadow:inset 0 -1px 0 rgba(0,0,0,.03); }
.assistant-table-scroll table { width:max-content; min-width:100%; max-width:none; border-collapse:separate; border-spacing:0; margin:0; border:0; border-radius:0; font-size:12px; }
.assistant-table-scroll::after { content:'Swipe table'; display:none; position:sticky; left:0; bottom:0; padding:3px 9px; font-size:10px; font-weight:700; color:var(--g500); background:linear-gradient(90deg,rgba(255,255,255,.95),rgba(255,255,255,0)); pointer-events:none; }
.assistant-bubble th, .assistant-bubble td { padding:8px 10px; border-bottom:1px solid var(--g200); vertical-align:top; text-align:left; }
.assistant-bubble th, .assistant-bubble td { overflow-wrap:normal; word-break:normal; min-width:120px; }
.assistant-bubble th { background:var(--g50); font-weight:700; color:var(--g800); }
.assistant-bubble tr:last-child td { border-bottom:0; }
.assistant-bubble code { background:var(--g100); border-radius:4px; padding:1px 4px; }
.assistant-bubble pre { background:var(--g900); color:white; border-radius:8px; padding:10px; overflow:auto; margin:10px 0; }
.assistant-bubble .katex-display { overflow-x:auto; overflow-y:hidden; padding:4px 0; }
.assistant-thinking { background:linear-gradient(90deg,#fff,#f8fafc,#fff); background-size:220% 100%; animation:assistantShimmer 1.8s ease-in-out infinite; }
.assistant-thinking-line { display:flex; align-items:center; gap:6px; color:var(--g800); }
.assistant-thinking-detail { color:var(--g500); font-size:12px; margin-top:3px; }
.assistant-thinking-dot { width:7px; height:7px; border-radius:50%; background:var(--purple); display:inline-block; animation:assistantBounce 1.2s infinite ease-in-out; }
.assistant-thinking-dot:nth-child(2) { animation-delay:.15s; }
.assistant-thinking-dot:nth-child(3) { animation-delay:.3s; margin-right:3px; }
@keyframes assistantBounce { 0%,80%,100% { transform:scale(.65); opacity:.45; } 40% { transform:scale(1); opacity:1; } }
@keyframes assistantShimmer { 0% { background-position:100% 0; } 100% { background-position:-100% 0; } }
.assistant-cite { display:inline-flex; align-items:center; justify-content:center; min-width:18px; height:18px; padding:0 6px; margin:0 1px; border-radius:999px; background:var(--purple-light); color:var(--purple); font-size:10px; font-weight:800; text-decoration:none; vertical-align:baseline; border:1px solid rgba(124,58,237,.18); text-transform:uppercase; letter-spacing:.03em; }
.assistant-cite:hover { background:var(--purple); color:white; text-decoration:none; }
.assistant-composer { border-top:1px solid var(--g200); padding:12px; background:white; display:grid; gap:8px; }
.assistant-composer textarea, .assistant-side textarea { width:100%; border:1.5px solid var(--g300); border-radius:10px; padding:10px 12px; resize:vertical; font-family:inherit; font-size:13px; outline:none; }
.assistant-composer textarea:focus, .assistant-side textarea:focus { border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-light); }
.assistant-composer-footer { display:flex; justify-content:space-between; align-items:center; gap:10px; }
.assistant-composer-footer .btn-generate { width:auto; margin:0; padding:9px 18px; }
.assistant-composer-footer #btn-assistant-cancel[hidden] { display:none !important; }
.assistant-composer-footer #btn-assistant-cancel:not([hidden]) { display:inline-flex; }
.assistant-check { font-size:12px; color:var(--g500); display:flex; align-items:center; gap:6px; }
.assistant-side { display:grid; gap:12px; }
.assistant-side-body { padding:12px; display:grid; gap:10px; font-size:13px; }
.assistant-visual-output { display:grid; gap:8px; }
.assistant-image-buttons { display:flex; gap:8px; flex-wrap:wrap; }
.assistant-visual-output img { width:100%; border-radius:10px; border:1px solid var(--g200); background:white; }
.assistant-generated-image { display:grid; gap:8px; }
.assistant-generated-image img { width:100%; border-radius:10px; border:1px solid var(--g200); background:white; }
.assistant-image-actions { display:flex; gap:8px; flex-wrap:wrap; }
.assistant-image-preview-open { overflow:hidden; }
.assistant-image-modal { position:fixed; inset:0; z-index:9999; background:rgba(15,23,42,.82); display:flex; align-items:center; justify-content:center; padding:24px; }
.assistant-image-modal-card { position:relative; display:grid; gap:10px; max-width:min(96vw,1200px); max-height:92vh; }
.assistant-image-modal-card img { max-width:100%; max-height:92vh; border-radius:14px; background:white; box-shadow:0 24px 80px rgba(0,0,0,.35); }
.assistant-image-modal-close { position:absolute; top:8px; right:8px; z-index:1; width:38px; height:38px; border:0; border-radius:999px; background:white; color:var(--g800); font-size:24px; line-height:1; cursor:pointer; box-shadow:var(--shadow); }
.assistant-image-modal-cancel { justify-self:center; border:0; border-radius:999px; background:white; color:var(--g800); font-weight:700; padding:9px 14px; box-shadow:var(--shadow); cursor:pointer; }
.assistant-sources { padding:10px 12px; display:grid; gap:8px; max-height:520px; overflow-y:auto; }
.assistant-saved-chats { padding:10px 12px; display:grid; gap:8px; max-height:220px; overflow-y:auto; }
.assistant-saved-chat { border:1px solid var(--g200); border-radius:10px; padding:8px; background:white; display:grid; gap:5px; }
.assistant-saved-chat-title { font-size:12px; font-weight:700; color:var(--g800); line-height:1.35; }
.assistant-saved-chat-meta { font-size:11px; color:var(--g500); }
.assistant-saved-chat-actions { display:flex; gap:6px; flex-wrap:wrap; }
.assistant-save-panel { border-top:1px solid var(--g200); padding:10px 12px; display:grid; gap:7px; }
.assistant-save-panel[hidden] { display:none; }
.assistant-save-panel label { font-size:11px; font-weight:700; color:var(--g500); text-transform:uppercase; letter-spacing:.04em; }
.assistant-save-panel input { width:100%; border:1.5px solid var(--g300); border-radius:9px; padding:8px 10px; font-size:12px; outline:none; }
.assistant-save-panel input:focus { border-color:var(--blue); box-shadow:0 0 0 3px var(--blue-light); }
.assistant-save-actions { display:flex; gap:6px; flex-wrap:wrap; }
.assistant-source { border:1px solid var(--g200); border-radius:10px; padding:9px; background:white; font-size:12px; line-height:1.5; }
.assistant-source strong { color:var(--g800); }
.assistant-source-badges { display:flex; gap:5px; flex-wrap:wrap; margin-top:6px; }
.assistant-source-badges span { border:1px solid var(--g200); border-radius:999px; background:var(--g50); color:var(--g600); padding:2px 7px; font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.03em; }
.assistant-source-meta { color:var(--g500); font-size:11px; margin-top:3px; }
.assistant-source-preview { margin-top:8px; }
.assistant-source-preview button { border:0; padding:0; background:transparent; cursor:pointer; width:100%; display:block; }
.assistant-source-preview img { width:100%; max-height:220px; object-fit:contain; border:1px solid var(--g200); border-radius:10px; background:white; display:block; }
.assistant-source-excerpt { margin-top:7px; color:var(--g600); max-height:170px; overflow:auto; }
.assistant-source-excerpt p { margin:0 0 6px; }
.assistant-source-excerpt ul, .assistant-source-excerpt ol { padding-left:16px; margin:4px 0; }
.assistant-source-excerpt strong { color:var(--g700); }
.assistant-muted { color:var(--g500); font-size:12px; line-height:1.6; }
.assistant-mermaid { background:white; border:1px solid var(--g200); border-radius:10px; padding:10px; margin:10px 0; overflow:auto; }
@media (max-width: 960px) { .assistant-layout { grid-template-columns:1fr; } .assistant-main { min-height:auto; grid-template-rows:auto minmax(320px,1fr) auto; } }
@media (max-width: 640px) {
.assistant-header { flex-direction:column; align-items:stretch; }
.assistant-status { align-self:flex-start; }
.assistant-toolbar { flex-direction:column; align-items:stretch; }
.assistant-toolbar-actions { display:grid; grid-template-columns:1fr 1fr; }
.assistant-toolbar-actions .btn-sm { width:100%; justify-content:center; }
.assistant-messages { padding:10px; }
.assistant-msg, .assistant-msg.user { max-width:100%; }
.assistant-bubble { font-size:13px; padding:11px 12px; }
.assistant-table-scroll::after { display:block; }
.assistant-composer { position:sticky; bottom:0; z-index:3; }
.assistant-composer-footer { flex-direction:column; align-items:stretch; }
.assistant-composer-footer .btn-generate { width:100%; }
.assistant-composer-footer #btn-assistant-cancel:not([hidden]) { width:100%; justify-content:center; }
.assistant-side { gap:10px; }
}
</style>