Spaces:
Running
Running
<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> |