| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Clinical Note Scribe</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> |
| <style> |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| |
| :root { |
| --bg: #0f1117; |
| --surface: #1a1d27; |
| --surface-2: #242836; |
| --border: #2e3345; |
| --text: #e4e6ef; |
| --text-muted: #8b8fa3; |
| --accent: #4f8cff; |
| --accent-glow: rgba(79, 140, 255, 0.15); |
| --green: #34d399; |
| --red: #f87171; |
| --yellow: #fbbf24; |
| --radius: 10px; |
| --font: 'Inter', -apple-system, sans-serif; |
| } |
| |
| body { |
| font-family: var(--font); |
| background: var(--bg); |
| color: var(--text); |
| min-height: 100vh; |
| line-height: 1.5; |
| } |
| |
| |
| .header { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 16px 28px; |
| border-bottom: 1px solid var(--border); |
| background: var(--surface); |
| } |
| .header h1 { |
| font-size: 18px; |
| font-weight: 600; |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| .header h1 span { font-size: 22px; } |
| .header-right { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
| .status-badge { |
| font-size: 12px; |
| padding: 4px 10px; |
| border-radius: 20px; |
| font-weight: 500; |
| } |
| .status-badge.idle { background: var(--surface-2); color: var(--text-muted); } |
| .status-badge.active { background: rgba(52,211,153,0.15); color: var(--green); } |
| .status-badge.done { background: rgba(79,140,255,0.15); color: var(--accent); } |
| |
| |
| .layout { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 0; |
| height: calc(100vh - 57px); |
| } |
| |
| .panel { |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| } |
| .panel-left { border-right: 1px solid var(--border); } |
| |
| .panel-section { |
| padding: 16px 20px; |
| border-bottom: 1px solid var(--border); |
| } |
| .panel-section.resizable { |
| resize: vertical; |
| overflow-y: auto; |
| overflow-x: hidden; |
| min-height: 80px; |
| max-height: 250px; |
| } |
| .panel-section-title { |
| font-size: 11px; |
| font-weight: 600; |
| text-transform: uppercase; |
| letter-spacing: 0.8px; |
| color: var(--text-muted); |
| margin-bottom: 10px; |
| } |
| |
| .scrollable { |
| flex: 1; |
| overflow-y: auto; |
| padding: 16px 20px; |
| } |
| .scrollable::-webkit-scrollbar { width: 6px; } |
| .scrollable::-webkit-scrollbar-track { background: transparent; } |
| .scrollable::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } |
| |
| |
| .controls { |
| display: flex; |
| gap: 8px; |
| flex-wrap: wrap; |
| align-items: center; |
| } |
| select, input, textarea { |
| font-family: var(--font); |
| font-size: 13px; |
| background: var(--surface-2); |
| border: 1px solid var(--border); |
| color: var(--text); |
| border-radius: 6px; |
| padding: 7px 10px; |
| outline: none; |
| transition: border-color 0.15s; |
| } |
| select:focus, input:focus, textarea:focus { |
| border-color: var(--accent); |
| } |
| textarea { |
| width: 100%; |
| resize: vertical; |
| min-height: 80px; |
| } |
| |
| .btn { |
| font-family: var(--font); |
| font-size: 13px; |
| font-weight: 500; |
| padding: 7px 16px; |
| border: none; |
| border-radius: 6px; |
| cursor: pointer; |
| transition: all 0.15s; |
| } |
| .btn:disabled { opacity: 0.4; cursor: not-allowed; } |
| .btn-primary { background: var(--accent); color: #fff; } |
| .btn-primary:hover:not(:disabled) { background: #3d7ae8; } |
| .btn-secondary { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); } |
| .btn-secondary:hover:not(:disabled) { background: var(--border); } |
| .btn-green { background: var(--green); color: #0f1117; } |
| .btn-green:hover:not(:disabled) { background: #2bc48d; } |
| |
| |
| .transcript-box { |
| font-size: 13px; |
| white-space: pre-wrap; |
| color: var(--text); |
| line-height: 1.7; |
| } |
| .transcript-box .speaker-doctor { color: var(--accent); font-weight: 500; } |
| .transcript-box .speaker-patient { color: var(--green); font-weight: 500; } |
| |
| |
| .context-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); |
| gap: 8px; |
| } |
| .context-card { |
| background: var(--surface-2); |
| border-radius: 8px; |
| padding: 10px 12px; |
| } |
| .context-card .label { |
| font-size: 10px; |
| font-weight: 600; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| color: var(--text-muted); |
| margin-bottom: 3px; |
| } |
| .context-card .value { |
| font-size: 13px; |
| color: var(--text); |
| } |
| |
| |
| .soap-grid { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 10px; |
| } |
| .soap-card { |
| background: var(--surface-2); |
| border-radius: 8px; |
| padding: 12px 14px; |
| border-left: 3px solid var(--border); |
| } |
| .soap-card.s { border-left-color: #60a5fa; } |
| .soap-card.o { border-left-color: #34d399; } |
| .soap-card.a { border-left-color: #fbbf24; } |
| .soap-card.p { border-left-color: #c084fc; } |
| .soap-card .soap-label { |
| font-size: 11px; |
| font-weight: 600; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| margin-bottom: 6px; |
| } |
| .soap-card.s .soap-label { color: #60a5fa; } |
| .soap-card.o .soap-label { color: #34d399; } |
| .soap-card.a .soap-label { color: #fbbf24; } |
| .soap-card.p .soap-label { color: #c084fc; } |
| .soap-card .soap-text { |
| font-size: 13px; |
| color: var(--text-muted); |
| line-height: 1.6; |
| } |
| |
| |
| .reward-bar { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| padding: 10px 0; |
| } |
| .reward-bar .score-value { |
| font-size: 28px; |
| font-weight: 700; |
| font-variant-numeric: tabular-nums; |
| } |
| .reward-meter { |
| flex: 1; |
| height: 8px; |
| background: var(--surface-2); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| .reward-meter-fill { |
| height: 100%; |
| border-radius: 4px; |
| background: linear-gradient(90deg, var(--accent), var(--green)); |
| transition: width 0.4s ease; |
| } |
| |
| |
| .action-form { |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| } |
| .action-row { |
| display: flex; |
| gap: 8px; |
| align-items: flex-start; |
| } |
| .action-row select { min-width: 160px; } |
| |
| |
| .log-container { |
| font-size: 12px; |
| font-family: 'SF Mono', 'Fira Code', monospace; |
| color: var(--text-muted); |
| height: 120px; |
| min-height: 60px; |
| overflow-y: auto; |
| resize: vertical; |
| padding: 8px 0; |
| } |
| .log-entry { |
| padding: 2px 0; |
| border-bottom: 1px solid rgba(46, 51, 69, 0.4); |
| } |
| .log-entry .log-time { color: var(--border); margin-right: 8px; } |
| .log-entry.error { color: var(--red); } |
| .log-entry.success { color: var(--green); } |
| |
| |
| .empty-state { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| height: 100%; |
| color: var(--text-muted); |
| text-align: center; |
| gap: 12px; |
| } |
| .empty-state .icon { font-size: 36px; opacity: 0.5; } |
| .empty-state p { font-size: 14px; max-width: 280px; } |
| |
| |
| @media (max-width: 768px) { |
| .layout { |
| display: flex; |
| flex-direction: column; |
| height: auto; |
| } |
| .panel-left { |
| border-right: none; |
| border-bottom: 2px solid var(--border); |
| min-height: 60vh; |
| } |
| .panel { |
| overflow: visible; |
| } |
| .scrollable { |
| overflow-y: visible; |
| } |
| #soapInputs > div { |
| grid-template-columns: 1fr !important; |
| } |
| .soap-grid { |
| grid-template-columns: 1fr; |
| } |
| .header h1 span { font-size: 18px; } |
| .header h1 { font-size: 15px; } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <header class="header"> |
| <h1><span>π₯</span> Clinical Note Scribe</h1> |
| <div class="header-right"> |
| <span class="status-badge idle" id="statusBadge">Idle</span> |
| </div> |
| </header> |
|
|
| <div class="layout"> |
|
|
| |
| <div class="panel panel-left"> |
| <div class="panel-section"> |
| <div class="controls"> |
| <select id="taskSelect"> |
| <option value="easy_routine_checkup">π’ Easy β Routine Check-Up</option> |
| <option value="medium_chronic_disease_followup">π‘ Medium β Chronic Follow-Up</option> |
| <option value="hard_complex_er_visit">π΄ Hard β Complex ER Visit</option> |
| </select> |
| <button class="btn btn-primary" id="resetBtn">Reset</button> |
| </div> |
| </div> |
|
|
| <div class="panel-section resizable" id="contextSection" style="display:none;"> |
| <div class="panel-section-title">Patient Context</div> |
| <div class="context-grid" id="contextGrid"></div> |
| </div> |
|
|
| <div class="scrollable" id="transcriptArea"> |
| <div class="empty-state"> |
| <div class="icon">π</div> |
| <p>Select a task and click <strong>Reset</strong> to load the transcript.</p> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="panel"> |
|
|
| <div class="panel-section resizable" id="actionSection" style="display:none;"> |
| <div class="panel-section-title">Action</div> |
| <div class="action-form"> |
| <div class="action-row"> |
| <select id="actionType"> |
| <option value="submit_note">Submit Note</option> |
| <option value="revise_section">Revise Section</option> |
| <option value="request_clarify">Request Clarify</option> |
| </select> |
| <select id="sectionSelect" style="display:none;"> |
| <option value="S">Subjective</option> |
| <option value="O">Objective</option> |
| <option value="A">Assessment</option> |
| <option value="P">Plan</option> |
| </select> |
| <button class="btn btn-green" id="stepBtn">Send</button> |
| </div> |
| <div id="soapInputs"> |
| <div style="display:grid; grid-template-columns:1fr 1fr; gap:8px;"> |
| <textarea id="inputS" placeholder="Subjective..." rows="3"></textarea> |
| <textarea id="inputO" placeholder="Objective..." rows="3"></textarea> |
| <textarea id="inputA" placeholder="Assessment..." rows="3"></textarea> |
| <textarea id="inputP" placeholder="Plan..." rows="3"></textarea> |
| </div> |
| </div> |
| <div id="reviseInput" style="display:none;"> |
| <textarea id="inputRevision" placeholder="Revision text..." rows="3"></textarea> |
| </div> |
| <div id="clarifyInput" style="display:none;"> |
| <input id="inputClarify" type="text" placeholder="Your question..." style="width:100%;"> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="panel-section resizable" id="rewardSection" style="display:none;"> |
| <div class="panel-section-title">Reward</div> |
| <div class="reward-bar"> |
| <div class="score-value" id="scoreValue">0.00</div> |
| <div class="reward-meter"> |
| <div class="reward-meter-fill" id="rewardFill" style="width:0%"></div> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="scrollable" id="draftArea"> |
| <div class="empty-state" id="draftEmpty"> |
| <div class="icon">π</div> |
| <p>Your SOAP note draft will appear here after you submit or revise.</p> |
| </div> |
| <div id="soapDraft" style="display:none;"> |
| <div class="panel-section-title" style="margin-bottom:12px;">Current Draft</div> |
| <div class="soap-grid" id="soapGrid"></div> |
| </div> |
| </div> |
|
|
| <div class="panel-section" style="border-top:1px solid var(--border); border-bottom:none;"> |
| <div class="panel-section-title">Log</div> |
| <div class="log-container" id="logContainer"></div> |
| </div> |
|
|
| </div> |
| </div> |
|
|
| <script src="/static/app.js"></script> |
| </body> |
| </html> |
|
|