LiberalMind / agent.html
liberalusa's picture
Update agent.html
129bd44 verified
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Liberalmind AI - Multifunctional Analyzer</title>
<!-- Preconnect Hints -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.tailwindcss.com">
<link rel="preconnect" href="https://esm.sh">
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
<link rel="preconnect" href="https://unpkg.com">
<link rel="preconnect" href="https://api.allorigins.win">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
<!-- Client-side Libraries -->
<script src="https://cdn.jsdelivr.net/pyodide/v0.26.0/full/pyodide.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
if (typeof pdfjsLib !== 'undefined') {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
<script src='https://unpkg.com/tesseract.js@5/dist/tesseract.min.js'></script>
<style>
:root {
--primary: #EC4899; /* Pink */
--secondary: #F59E0B; /* Amber */
--accent: #ef4444; /* Red for Stop */
--research: #10b981; /* Emerald for Research */
--on-primary: #FFFFFF;
--primary-container: #fdf2f8; /* Lighter Pink */
--on-primary-container: #831843;
--bg-body: #f8fafc; /* Very Light Gray */
--bg-container: #ffffff;
--bg-editor: #ffffff;
--bg-output: #fffbeb; /* Light Yellow/Cream */
--border-color: #f3e8ff; /* Lighter Violet */
--text-editor: #1f2937;
--text-heading: #a21caf; /* Fuchsia */
--text-muted: #64748b; /* Slate */
--text-light: #94a3b8;
--sidebar-bg: #f9fafb;
--sidebar-width: 220px;
--sidebar-icon: #9ca3af;
--sidebar-text: #4b5563;
--sidebar-active: #e5e7eb;
}
html, body { height: 100%; overflow: hidden; }
body { font-family: 'Inter', sans-serif; background-color: var(--bg-body); color: var(--text-editor); margin: 0; }
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--primary-container); border-radius: 3px;}
::-webkit-scrollbar-thumb:hover { background: var(--primary); }
textarea::-webkit-scrollbar { width: 8px; }
textarea::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; }
textarea::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 4px;}
textarea::-webkit-scrollbar-thumb:hover { background: var(--primary); }
.input-container { background-color: #FFFFFF; border-radius: 16px; box-shadow: 0 4px 12px rgba(236, 72, 153, 0.1); border: 1px solid var(--primary-container); }
.main-input { background: transparent; outline: none; border: none; flex-grow: 1; font-weight: 500; color: var(--text-editor); }
.main-input::placeholder { color: var(--text-light); font-weight: 400;}
.icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 12px; background-color: transparent; color: var(--text-muted); border: none; transition: all 0.2s ease; cursor: pointer; }
.icon-btn:hover:not(:disabled) { background-color: var(--primary-container); color: var(--primary); }
.icon-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.icon-btn .material-icons-round { font-size: 1.25rem; }
#send-btn { background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%); color: var(--on-primary); box-shadow: 0 2px 5px rgba(236, 72, 153, 0.2); }
#send-btn:hover:not(:disabled) { box-shadow: 0 4px 8px rgba(236, 72, 153, 0.3); transform: translateY(-1px); background: linear-gradient(135deg, #d63c84 0%, #d97706 100%); }
#research-btn, #fetch-url-btn /* Apply similar styling to fetch button */ {
background: linear-gradient(135deg, var(--research) 0%, #059669 100%); color: var(--on-primary); box-shadow: 0 2px 5px rgba(16, 185, 129, 0.2);
}
#research-btn:hover:not(:disabled), #fetch-url-btn:hover:not(:disabled) {
box-shadow: 0 4px 8px rgba(16, 185, 129, 0.3); transform: translateY(-1px); background: linear-gradient(135deg, #0d9d6e 0%, #047857 100%);
}
#stop-btn { color: var(--accent); display: none; }
#stop-btn:hover:not(:disabled) { background-color: #fee2e2; color: #b91c1c; }
.editor-area, .output-area, #video-stream-panel /* Ensure consistent flex behavior */ {
position: relative; display: flex; flex-direction: column; overflow: hidden;
}
.code-editor-wrapper { flex-grow: 1; overflow: auto; }
.code-input { font-family: 'Courier New', Courier, monospace; font-size: 0.85rem; line-height: 1.6; background: transparent; outline: none; resize: none; color: var(--text-editor); border: none; width: 100%; height: 100%; display: block; padding: 12px; box-sizing: border-box; }
#output-frame { border: none; background-color: #ffffff; border-radius: 8px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
#ai-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(255, 255, 255, 0.85); backdrop-filter: blur(4px); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 50; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; border-radius: 8px; color: var(--primary); pointer-events: none; }
#ai-overlay.visible { opacity: 1; visibility: visible; }
.spinner { border: 3px solid rgba(236, 72, 153, 0.2); border-radius: 50%; border-top: 3px solid var(--primary); width: 30px; height: 30px; animation: spin 1s linear infinite; margin-bottom: 10px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
#ai-overlay p { font-size: 0.9rem; font-weight: 500; text-align: center; padding: 0 10px; }
.file-input-hidden { display: none; }
#sdk-error-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); display: flex; align-items: center; justify-content: center; z-index: 100; opacity: 0; visibility: hidden; transition: opacity 0.3s ease; }
#sdk-error-overlay.visible { opacity: 1; visibility: visible; }
#sdk-error-box { background-color: white; border-radius: 12px; padding: 25px; max-width: 500px; width: 90%; box-shadow: 0 10px 25px rgba(0,0,0,0.2); text-align: center; }
#sdk-error-box h3 { color: var(--accent); margin-bottom: 15px; font-size: 1.3rem; font-weight: 700;}
#sdk-error-box p { margin-bottom: 20px; color: var(--text-muted); line-height: 1.6; }
#reload-button { background-color: var(--primary); color: white; border: none; padding: 10px 20px; border-radius: 8px; cursor: pointer; transition: background-color 0.2s; font-weight: 600; }
#reload-button:hover { background-color: #d63c84; }
#file-info, #url-info { font-size: 0.75rem; color: var(--text-muted); margin-left: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 120px; display: none; align-items: center; }
#file-info .material-icons-round { font-size: 1rem; vertical-align: middle; margin-right: 3px; color: var(--secondary);}
#url-info .material-icons-round { font-size: 1rem; vertical-align: middle; margin-right: 3px; color: var(--research); }
#file-name, #url-info-text { display: inline-block; vertical-align: middle; }
.tab-button { padding: 6px 12px; border-radius: 8px; background: transparent; border: none; cursor: pointer; font-weight: 500; color: var(--text-muted); font-size: 0.8rem;}
.tab-button.active { background: var(--primary-container); color: var(--primary); }
.preview-content { display: none; height: calc(100% - 36px); /* Adjusted for smaller tabs */ }
.preview-content.active { display: block; }
.research-progress, #ocr-progress-container { width: 100%; margin-top: 10px; }
.progress-bar, #ocr-progress-bar { height: 6px; background-color: #e5e7eb; border-radius: 3px; overflow: hidden; }
.progress-fill { height: 100%; background-color: var(--research); width: 0%; transition: width 0.3s ease; }
#ocr-progress-bar { background-color: var(--primary); } /* Different color for OCR progress */
.progress-text, #ocr-status { font-size: 0.75rem; color: var(--text-muted); margin-top: 4px; text-align: center; }
#ocr-status { min-height: 1.2em; /* Ensure space for text */ }
#language-selector-container { display: inline-flex; align-items: center; }
#language-selector {
font-size: 0.75rem; padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border-color);
background-color: white; color: var(--text-muted); margin-left: 8px; height: 28px; max-width: 170px;
}
#language-selector:hover { border-color: var(--primary); }
#language-selector:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-container); }
/* Video Stream Panel Styles */
#video-stream-panel { background-color: var(--bg-output); } /* Consistent background */
#video-element { border: 1px solid var(--border-color); }
#start-stream-btn, #stop-stream-btn {
padding: 6px 12px; font-size: 0.8rem;
}
/* Sidebar Styles */
.sidebar {
width: var(--sidebar-width);
background-color: var(--sidebar-bg);
border-right: 1px solid var(--border-color);
height: 100%;
overflow-y: auto;
transition: width 0.3s ease;
flex-shrink: 0;
}
.sidebar-collapsed {
width: 40px;
}
.sidebar-header {
padding: 10px 12px;
font-size: 0.8rem;
font-weight: 600;
color: var(--sidebar-text);
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border-color);
}
.sidebar-toggle {
background: none;
border: none;
color: var(--sidebar-icon);
cursor: pointer;
padding: 2px;
border-radius: 4px;
}
.sidebar-toggle:hover {
background-color: var(--sidebar-active);
}
.file-explorer {
padding: 8px 0;
}
.folder {
padding: 4px 0;
}
.folder-header {
padding: 4px 12px;
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.folder-header:hover {
background-color: var(--sidebar-active);
}
.folder-icon {
margin-right: 6px;
color: var(--sidebar-icon);
font-size: 18px;
}
.folder-name {
font-size: 0.8rem;
color: var(--sidebar-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.folder-contents {
padding-left: 20px;
display: none;
}
.folder.expanded .folder-contents {
display: block;
}
.file {
padding: 4px 12px 4px 28px;
display: flex;
align-items: center;
cursor: pointer;
position: relative;
}
.file:hover {
background-color: var(--sidebar-active);
}
.file.active {
background-color: var(--primary-container);
}
.file-icon {
margin-right: 6px;
color: var(--sidebar-icon);
font-size: 18px;
}
.file-name {
font-size: 0.8rem;
color: var(--sidebar-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-badge {
position: absolute;
right: 8px;
font-size: 0.6rem;
background-color: var(--primary);
color: white;
border-radius: 4px;
padding: 1px 4px;
}
.main-content {
display: flex;
flex-grow: 1;
overflow: hidden;
}
.editor-container {
display: flex;
flex-grow: 1;
overflow: hidden;
}
</style>
</head>
<body class="p-2 sm:p-3 md:p-4">
<div class="flex flex-col h-full bg-[var(--bg-container)] rounded-xl shadow-xl overflow-hidden border border-[var(--primary-container)]">
<!-- Header -->
<div class="flex items-center justify-between p-2 border-b border-[var(--primary-container)] flex-shrink-0">
<div class="flex items-center space-x-2">
<div class="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0" style="background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);">
<span class="material-icons-round text-white text-base">auto_awesome</span>
</div>
<h1 class="text-base font-semibold tracking-tight text-[var(--text-heading)] hidden sm:inline">Gemini Multifunctional Analyzer</h1>
<div id="language-selector-container">
<select id="language-selector"></select>
</div>
<span id="init-status-text" class="text-xs text-gray-400 ml-2 font-medium">Loading...</span>
</div>
<div class="flex-grow"></div>
<div class="flex justify-end items-center space-x-1" style="min-width: 80px;">
<button id="stop-btn" class="icon-btn" title="Stop AI Generation">
<span class="material-icons-round">stop_circle</span>
</button>
</div>
</div>
<!-- Main Content Area with Sidebar -->
<div class="main-content">
<!-- Sidebar -->
<div class="sidebar" id="sidebar">
<div class="sidebar-header">
<span id="sidebar-title">EXPLORER</span>
<button class="sidebar-toggle" id="sidebar-toggle">
<span class="material-icons-round">chevron_left</span>
</button>
</div>
<div class="file-explorer">
<div class="folder expanded">
<div class="folder-header">
<span class="material-icons-round folder-icon">folder</span>
<span class="folder-name">WORKSPACE</span>
</div>
<div class="folder-contents">
<div class="file active">
<span class="material-icons-round file-icon">description</span>
<span class="file-name">current_file.txt</span>
<span class="file-badge">Active</span>
</div>
<div class="file">
<span class="material-icons-round file-icon">description</span>
<span class="file-name">document.pdf</span>
</div>
<div class="file">
<span class="material-icons-round file-icon">description</span>
<span class="file-name">data.csv</span>
</div>
</div>
</div>
<div class="folder">
<div class="folder-header">
<span class="material-icons-round folder-icon">folder</span>
<span class="folder-name">RESOURCES</span>
</div>
<div class="folder-contents">
<div class="file">
<span class="material-icons-round file-icon">image</span>
<span class="file-name">screenshot.png</span>
</div>
<div class="file">
<span class="material-icons-round file-icon">link</span>
<span class="file-name">reference.html</span>
</div>
</div>
</div>
<div class="folder">
<div class="folder-header">
<span class="material-icons-round folder-icon">folder</span>
<span class="folder-name">OUTPUTS</span>
</div>
<div class="folder-contents">
<div class="file">
<span class="material-icons-round file-icon">code</span>
<span class="file-name">analysis.json</span>
</div>
<div class="file">
<span class="material-icons-round file-icon">insert_chart</span>
<span class="file-name">results.csv</span>
</div>
</div>
</div>
</div>
</div>
<!-- Editor & Output Panels -->
<div class="editor-container">
<!-- Editor Panel -->
<div class="editor-area w-1/2 h-full border-r border-[var(--primary-container)]">
<div class="code-editor-wrapper w-full h-full">
<textarea id="code-input-area" class="code-input" spellcheck="false" placeholder="Initializing AI..." disabled></textarea>
</div>
<div id="ai-overlay">
<div class="spinner"></div>
<p id="ai-status-text">Thinking...</p>
<div id="research-progress" class="research-progress hidden">
<div class="progress-bar"><div class="progress-fill"></div></div>
<div class="progress-text" id="progress-text">Researching: 0/0</div>
</div>
</div>
</div>
<!-- Output Panel Area -->
<div class="output-area w-1/2 h-full flex flex-col p-2">
<div class="preview-tabs flex space-x-1 mb-1 flex-shrink-0">
<button class="tab-button active" data-tab="output">Output</button>
<button class="tab-button" data-tab="code">Full Code</button>
<button class="tab-button" data-tab="video">Video Analysis</button>
</div>
<div id="output-tabs" class="flex-grow overflow-hidden">
<div id="output-tab" class="preview-content active h-full">
<iframe id="output-frame" class="w-full h-full flex-grow shadow-inner rounded-lg" sandbox="allow-scripts allow-same-origin allow-popups allow-forms"></iframe>
</div>
<div id="code-tab" class="preview-content hidden h-full overflow-auto p-2 bg-white rounded-lg">
<pre id="full-code-content" class="whitespace-pre-wrap font-mono text-sm"></pre>
</div>
<div id="video-stream-panel" class="preview-content hidden h-full p-2 flex flex-col items-center bg-gray-50">
<h3 class="text-md font-semibold text-[var(--text-heading)] mb-1">Live Camera Analysis</h3>
<div class="relative w-full max-w-sm aspect-[4/3] bg-black rounded-md overflow-hidden shadow mb-1">
<video id="video-element" autoplay playsinline class="w-full h-full object-contain"></video>
<canvas id="video-canvas" class="hidden"></canvas>
</div>
<div class="flex space-x-2 mb-1">
<button id="start-stream-btn" class="px-3 py-1 text-xs font-medium rounded-md bg-[var(--primary)] text-white hover:bg-opacity-80">Start Camera</button>
<button id="stop-stream-btn" class="px-3 py-1 text-xs font-medium rounded-md bg-[var(--accent)] text-white hover:bg-opacity-80 hidden">Stop Camera</button>
</div>
<p id="ocr-status" class="text-xs text-gray-500 h-4"></p>
<div id="ocr-progress-container" class="w-full max-w-sm h-1.5 bg-gray-300 rounded hidden mt-0.5">
<div id="ocr-progress-bar" class="h-full bg-[var(--primary)] rounded" style="width: 0%;"></div>
</div>
<p class="text-xs text-gray-600 mt-1 text-center">Point camera at text, type prompt below, then click "Send".</p>
</div>
</div>
</div>
</div>
</div>
<!-- Footer/Input Row -->
<div class="p-2 border-t border-[var(--primary-container)] flex-shrink-0">
<div class="input-container p-1 flex items-center space-x-1">
<input type="file" id="file-input" class="file-input-hidden" accept=".txt,.md,.pdf,.csv,.doc,.docx">
<label for="file-input" id="upload-btn-label" class="icon-btn flex-shrink-0" title="Upload File">
<span class="material-icons-round">upload_file</span>
</label>
<span id="file-info">
<span class="material-icons-round">description</span>
<span id="file-name"></span>
</span>
<span id="url-info">
<span class="material-icons-round">link</span>
<span id="url-info-text"></span>
</span>
<input type="text" id="main-input" placeholder="Enter task, paste URL, or use with video..." class="main-input px-2 py-1 text-sm">
<button id="fetch-url-btn" class="icon-btn flex-shrink-0" title="Fetch URL Content">
<span class="material-icons-round">http</span>
</button>
<button id="research-btn" class="icon-btn flex-shrink-0" title="Research Online (uses input text)">
<span class="material-icons-round">search</span>
</button>
<button id="send-btn" class="icon-btn flex-shrink-0" title="Send Prompt / Analyze Frame">
<span class="material-icons-round">send</span>
</button>
</div>
</div>
</div>
<!-- Fatal Error Overlay -->
<div id="sdk-error-overlay">
<div id="sdk-error-box">
<h3>Initialization Failed</h3>
<p id="init-error-message">Could not connect to the AI service.</p>
<p class="text-xs text-gray-400">Check console (F12) for details.</p>
<button id="reload-button">Reload</button>
</div>
</div>
<script type="module" src="agent.js"></script>
</body>
</html>