00Boobs00's picture
import gradio as gr
afe04f9 verified
class CustomToolsPanel extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
flex-direction: column;
height: 100%;
background: rgba(15, 23, 42, 0.5);
}
.tabs {
display: flex;
border-bottom: 1px solid #334155;
}
.tab-btn {
flex: 1;
background: transparent;
border: none;
color: #94a3b8;
padding: 1rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: all 0.2s;
}
.tab-btn:hover {
background: rgba(255, 255, 255, 0.05);
color: #e2e8f0;
}
.tab-btn.active {
color: #6366f1;
border-bottom: 2px solid #6366f1;
background: rgba(99, 102, 241, 0.05);
}
.panel-content {
flex: 1;
overflow: hidden;
display: none;
flex-direction: column;
}
.panel-content.active {
display: flex;
}
.upload-area {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
border: 2px dashed #334155;
margin: 1rem;
border-radius: 0.5rem;
text-align: center;
color: #94a3b8;
cursor: pointer;
transition: border-color 0.2s;
}
.upload-area:hover {
border-color: #6366f1;
color: #818cf8;
}
#monaco-container {
width: 100%;
height: 100%;
padding: 1rem;
box-sizing: border-box;
}
.file-list {
padding: 0 1rem;
overflow-y: auto;
}
.file-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
background: #1e293b;
margin-bottom: 0.5rem;
border-radius: 0.25rem;
font-size: 0.875rem;
}
</style>
<div class="tabs">
<button class="tab-btn active" data-tab="0">
<i data-feather="file-text"></i> Docs
</button>
<button class="tab-btn" data-tab="1">
<i data-feather="code"></i> Code
</button>
</div>
<div id="panel-0" class="panel-content active">
<div class="upload-area" id="drop-zone">
<i data-feather="upload-cloud" style="width: 48px; height: 48px; margin-bottom: 1rem;"></i>
<p>Drag & Drop PDF/Docx/Txt</p>
<p style="font-size: 0.75rem; margin-top: 0.5rem;">or click to browse</p>
<input type="file" id="file-input" multiple style="display: none;">
</div>
<div class="file-list" id="file-list">
<!-- Uploaded files appear here -->
</div>
</div>
<div id="panel-1" class="panel-content">
<div id="monaco-container"></div>
</div>
`;
// Tab Logic
const tabs = this.shadowRoot.querySelectorAll('.tab-btn');
const panels = this.shadowRoot.querySelectorAll('.panel-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
panels.forEach(p => p.classList.remove('active'));
tab.classList.add('active');
this.shadowRoot.getElementById(`panel-${tab.dataset.tab}`).classList.add('active');
// Resize Monaco if code tab is active
if(tab.dataset.tab === "1" && window.monacoEditor) {
window.monacoEditor.layout();
}
});
});
// File Upload Logic (Mock)
const dropZone = this.shadowRoot.getElementById('drop-zone');
const fileInput = this.shadowRoot.getElementById('file-input');
const fileList = this.shadowRoot.getElementById('file-list');
const handleFiles = (files) => {
Array.from(files).forEach(file => {
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<i data-feather="file"></i>
<span style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${file.name}</span>
<i data-feather="check-circle" style="color: #34d399; width: 16px;"></i>
`;
fileList.prepend(item);
feather.replace();
});
};
dropZone.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => handleFiles(e.target.files));
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.style.borderColor = '#6366f1';
});
dropZone.addEventListener('dragleave', () => {
dropZone.style.borderColor = '#334155';
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.style.borderColor = '#334155';
handleFiles(e.dataTransfer.files);
});
// Monaco Editor Loader
this.loadMonaco();
// Listen for Switch Tab Event (from Script.js when code is generated)
window.appEvents.addEventListener('switch-tab', (e) => {
const index = e.detail.index;
tabs[index].click();
});
// Listen for Code Update
window.appEvents.addEventListener('update-code', (e) => {
if(window.monacoEditor) {
window.monacoEditor.setValue(e.detail.code);
}
});
feather.replace();
}
loadMonaco() {
if (document.getElementById('monaco-script')) return; // Already loaded
const script = document.createElement('script');
script.id = 'monaco-script';
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js';
script.onload = () => {
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' } });
require(['vs/editor/editor.main'], function() {
// Delay slightly to ensure container is rendered and has dimensions
setTimeout(() => {
if(document.getElementById('monaco-container')) {
// Access shadow root container
const container = this.shadowRoot.getElementById('monaco-container');
// Actually, the require callback scope 'this' might be lost, but standard monaco usage usually targets global or ID
// We need to be careful with Shadow DOM and Monaco.
// Monaco appends to the body by default for overlays, but we want it inside our component.
// Simplest way for this demo: Target by ID inside ShadowDOM
// Because Monaco is a bit complex with ShadowDOM encapsulation (styles/events),
// we might need to use global or trick it.
// For this demo, let's attach to the specific ID if possible, or use window.monacoEditor for control.
// Note: Monaco doesn't fully support ShadowDOM out of the box easily without configuration tweaks.
// We will try to render it. If it fails, it might be due to the shadow root encapsulation blocking some global styles/events needed by Monaco's widget logic.
// To make it robust, we often put the monaco container *outside* or pass specific options.
// Let's try standard initialization on the element found in the shadowRoot of the custom element.
const host = document.querySelector('custom-tools-panel').shadowRoot.getElementById('monaco-container');
window.monacoEditor = monaco.editor.create(host, {
value: '# AI-generated code will appear here\n# Start coding your vibe...',
language: 'python',
theme: 'vs-dark',
automaticLayout: true,
minimap: { enabled: false }
});
}
}, 500);
}.bind(this));
};
document.head.appendChild(script);
}
}
customElements.define('custom-tools-panel', CustomToolsPanel);