jeongsoo's picture
init
6575706
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG ๊ฒ€์ƒ‰ ์ฑ—๋ด‡</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/device-style.css') }}">
</head>
<body>
<div class="container">
<header>
<h1>RAG ๊ฒ€์ƒ‰ ์ฑ—๋ด‡</h1>
<div class="header-actions">
<div class="llm-selector">
<label for="llmSelect">LLM ์„ ํƒ:</label>
<select id="llmSelect">
<!-- ์˜ต์…˜์€ JavaScript์—์„œ ๋™์ ์œผ๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค -->
</select>
</div>
<div class="user-info">
<span>์‚ฌ์šฉ์ž: {% if session.username %}{{ session.username }}{% else %}์†๋‹˜{% endif %}</span>
<a href="{{ url_for('logout') }}" class="logout-button">
<i class="fas fa-sign-out-alt"></i> ๋กœ๊ทธ์•„์›ƒ
</a>
</div>
</div>
<div class="tabs">
<button id="chatTab" class="tab active">๋Œ€ํ™”</button>
<button id="docsTab" class="tab">๋ฌธ์„œ๊ด€๋ฆฌ</button>
<button id="deviceTab" class="tab">์žฅ์น˜์ œ์–ด</button>
</div>
</header>
<main>
<!-- ๋Œ€ํ™” ํƒญ -->
<section id="chatSection" class="tab-content active">
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message system">
<div class="message-content">
<p>์•ˆ๋…•ํ•˜์„ธ์š”! ์ง€์‹๋ฒ ์ด์Šค์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•œ ์ ์„ ๋ฌผ์–ด๋ณด์„ธ์š”. ์Œ์„ฑ์œผ๋กœ ์งˆ๋ฌธํ•˜์‹œ๋ ค๋ฉด ๋งˆ์ดํฌ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์„ธ์š”.</p>
</div>
</div>
</div>
<div class="chat-input-container">
<textarea id="userInput" placeholder="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”..." rows="1"></textarea>
<button id="micButton" class="mic-button">
<i class="fas fa-microphone"></i>
</button>
<button id="sendButton" class="send-button">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div id="recordingStatus" class="recording-status hidden">
<div class="recording-indicator">
<div class="recording-pulse"></div>
</div>
<span>๋…น์Œ ์ค‘...</span>
<button id="stopRecordingButton" class="stop-recording-button">
<i class="fas fa-stop"></i>
</button>
</div>
</div>
</section>
<!-- ๋ฌธ์„œ๊ด€๋ฆฌ ํƒญ -->
<section id="docsSection" class="tab-content">
<div class="docs-container">
<div class="upload-section">
<h2>๋ฌธ์„œ ์—…๋กœ๋“œ</h2>
<p>์ง€์‹๋ฒ ์ด์Šค์— ์ถ”๊ฐ€ํ•  ๋ฌธ์„œ๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”. (์ง€์› ํ˜•์‹: .txt, .md, .csv)</p>
<form id="uploadForm" enctype="multipart/form-data">
<div class="file-upload">
<input type="file" id="documentFile" name="document" accept=".txt,.md,.csv">
<label for="documentFile">ํŒŒ์ผ ์„ ํƒ</label>
<span id="fileName">์„ ํƒ๋œ ํŒŒ์ผ ์—†์Œ</span>
</div>
<button type="submit" id="uploadButton" class="upload-button">
<i class="fas fa-upload"></i> ์—…๋กœ๋“œ
</button>
</form>
<div id="uploadStatus" class="upload-status hidden"></div>
</div>
<div class="docs-list-section">
<h2>๋ฌธ์„œ ๋ชฉ๋ก</h2>
<button id="refreshDocsButton" class="refresh-button">
<i class="fas fa-sync-alt"></i> ์ƒˆ๋กœ๊ณ ์นจ
</button>
<div class="docs-list-container">
<table id="docsList" class="docs-list">
<thead>
<tr>
<th>ํŒŒ์ผ๋ช…</th>
<th>์ฒญํฌ ์ˆ˜</th>
<th>์œ ํ˜•</th>
</tr>
</thead>
<tbody>
<!-- ๋ฌธ์„œ ๋ชฉ๋ก์ด ์—ฌ๊ธฐ์— ๋™์ ์œผ๋กœ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค -->
</tbody>
</table>
<div id="docsLoading" class="loading-indicator">
<div class="spinner"></div>
<p>๋ฌธ์„œ ๋กœ๋”ฉ ์ค‘...</p>
</div>
<div id="noDocsMessage" class="no-docs-message hidden">
<p>์ง€์‹๋ฒ ์ด์Šค์— ๋“ฑ๋ก๋œ ๋ฌธ์„œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๋ฅผ ์—…๋กœ๋“œํ•ด ์ฃผ์„ธ์š”.</p>
</div>
</div>
</div>
</div>
</section>
<!-- ์žฅ์น˜ ์ œ์–ด ํƒญ -->
<section id="deviceSection" class="tab-content">
<div class="device-connection">
<h3>1. ์žฅ์น˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ</h3>
<div class="device-connection-form">
<input type="text" id="deviceServerUrlInput" placeholder="LocalPCAgent Ngrok URL ์ž…๋ ฅ (https://xxxx-xx-xxx-xxx.ngrok-free.app ํ˜•์‹)">
<button id="connectDeviceServerBtn">์—ฐ๊ฒฐ</button>
</div>
<div id="deviceConnectionStatus" class="connection-status disconnected">์—ฐ๊ฒฐ ์ƒํƒœ: ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Œ</div>
</div>
<div id="deviceBasicFunctions" class="device-functions">
<h3>2. ๊ธฐ๋ณธ ๊ธฐ๋Šฅ</h3>
<div class="function-buttons">
<button id="checkDeviceStatusBtn">์žฅ์น˜ ์ƒํƒœ ํ™•์ธ</button>
</div>
<textarea id="deviceStatusResult" class="device-status-result" readonly></textarea>
</div>
<div id="deviceProgramControl" class="program-control">
<h3>3. ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</h3>
<div class="function-buttons">
<button id="getProgramsBtn">ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ</button>
</div>
<div id="programsList" class="program-list-container">
<div class="no-programs-message">ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก์ด ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
<div class="program-select-container">
<select id="programSelectDropdown">
<option value="">-- ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์„ ํƒ --</option>
</select>
</div>
<button id="executeProgramBtn" class="execute-btn" disabled>์„ ํƒํ•œ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</button>
<div id="executeResult" class="execute-result"></div>
</div>
<!-- ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์„น์…˜ ์ถ”๊ฐ€ -->
<div id="deviceCustomControl" class="program-control">
<h3>4. ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</h3>
<div class="custom-command-container">
<input type="text" id="customCommandInput" placeholder="์‹คํ–‰ํ•  ๋ช…๋ น์–ด ๋˜๋Š” ํ”„๋กœ๊ทธ๋žจ ๊ฒฝ๋กœ ์ž…๋ ฅ">
<button id="executeCustomBtn">์‹คํ–‰</button>
</div>
<div id="customExecuteResult" class="execute-result"></div>
</div>
</section>
</main>
<footer>
<p>ยฉ 2025 RAG ๊ฒ€์ƒ‰ ์ฑ—๋ด‡ | OpenAI/DeepSeek LLM & VITO STT ํ™œ์šฉ</p>
<div class="current-llm">
<span>Current LLM: </span>
<span id="currentLLMInfo">-</span>
</div>
</footer>
</div>
<script src="{{ url_for('static', filename='js/app-core.js') }}"></script>
<script src="{{ url_for('static', filename='js/app-device.js') }}"></script>
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>