Spaces:
Configuration error
Configuration error
import { Modal, Icon, Message } from 'semantic-ui-react' | |
import { ReactComponent as Gradio } from '../../images/gradio.svg' | |
import { ReactComponent as Streamlit } from '../../images/streamlit.svg' | |
import { ReactComponent as Exit } from '../../images/exit.svg' | |
import { ReactComponent as Proxmox } from '../../images/proxmox.svg' | |
import { useState } from 'react' | |
import { BsSearch } from 'react-icons/bs'; | |
export default function Import(props) { | |
const [subTab, setSubTab] = useState(0) | |
const [embedUrl, setEmbedUrl] = useState(""); | |
const [vmid, setVmid] = useState(''); | |
const [node, setNode] = useState(''); | |
const [iframeSrc, setIframeSrc] = useState(""); | |
const [tab, setTab] = useState('') | |
const [iframeTitle, setIframeTitle] = useState(""); // Added state for iframeTitle | |
const handleSubmit = async (e) => { | |
e.preventDefault(); | |
console.debug("Embed submit:", iframeSrc); | |
props.onAddEmbed({ url: iframeSrc, title: iframeTitle, type: 'embed' }); // Modified to include title | |
}; | |
const handleProxmoxSubmit = async (e) => { | |
e.preventDefault(); | |
const requestData = { vmid, node }; | |
fetch("http://localhost:2000/api/proxmox/vnc", { | |
method: "POST", | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify(requestData) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.iframe_src) { | |
setIframeSrc(data.iframe_src); | |
props.onAddEmbed({ url: data.iframe_src, type: 'embed' }); | |
} else { | |
console.error("Failed to get iframe source URL"); | |
} | |
}) | |
.catch(error => { | |
console.error("Error fetching iframe source URL:", error); | |
}); | |
}; | |
return (<div> | |
<Modal | |
basic | |
className='' | |
open={props.open} | |
size='fullscreen' | |
> | |
<div className='w-full shadow-lg rounded-lg'> | |
<ul className="flex flex-wrap text-sm font-medium text-center text-gray-500 bg-gray-100 rounded-t-lg border-gray-200 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800" id="defaultTab" data-tabs-toggle="#defaultTabContent" role="tablist"> | |
<li className="" onClick={() => { | |
setTab("gradio") | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button id="gradio-tab" data-tabs-target="#Gradio" type="button" role="tab" aria-controls="gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 rounded-tl-lg ${tab === "gradio" ? 'bg-gray-200' : 'hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 focus:bg-gray-700'}`}><Gradio className=" w-20 h-10" /></button> | |
</li> | |
<li className="" onClick={() => { | |
setTab("streamlit") | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button id="services-tab" data-tabs-target="#Streamlit" type="button" role="tab" aria-controls="services" aria-selected="false" className={`inline-block p-4 rounded-tl-lg ${tab === "streamlit" ? 'bg-gray-200' : 'hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 focus:bg-gray-700'}`}><Streamlit className=" w-20 h-10" /></button> | |
</li> | |
<li className="" onClick={() => { | |
setTab("proxmox") | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button id="proxmox-tab" data-tabs-target="#Proxmox" type="button" role="tab" aria-controls="proxmox" aria-selected={tab === "proxmox" ? "true" : "false"} className={`inline-block p-4 rounded-tl-lg ${tab === "proxmox" ? 'bg-gray-200' : 'hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 focus:bg-gray-700'}`}><Proxmox className=" w-20 h-10" /></button> | |
</li> | |
</ul> | |
<div className='absolute right-5 top-5 z-20 mr-5' | |
onClick={() => { | |
props.quitHandeler(false) | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button type="button" | |
className=" bg-neutral-300 rounded-2xl p-2 inline-flex items-center justify-center dark:bg-neutral-700 hover:opacity-70 focus:outline-none"> | |
<Exit className=" w-[20px] h-[20px] text-gray-400 dark:text-white" /> | |
</button> | |
</div> | |
</div> | |
{tab === "gradio" && | |
<div className='w-full bg-white'> | |
<ul className="flex flex-wrap text-sm font-medium text-center text-gray-500 bg-gray-200 border-gray-200 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800" id="defaultTab" data-tabs-toggle="#defaultTabContent" role="tablist"> | |
<li className="" onClick={() => { | |
setSubTab(0) | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button id="local-sub-tab" data-tabs-target="#local" type="button" role="tab" aria-controls="local-gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 px-6 text-base font-sans font-bold ${subTab === 0 ? 'bg-gray-300' : ''} hover:bg-gray-300 `}>Local</button> | |
</li> | |
<li className="" onClick={() => { | |
setSubTab(1) | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
}}> | |
<button id="shared-sub-tab" data-tabs-target="#Gradio" type="button" role="tab" aria-controls="shared-gradio" aria-selected={tab === "gradio" ? "true" : "false"} className={`inline-block p-4 px-6 text-base font-sans font-bold ${subTab === 1 ? 'bg-gray-300' : ''} hover:bg-gray-300 `}>Shared</button> | |
</li> | |
</ul> | |
{subTab === 0 && <Local />} | |
{subTab === 1 && <Shared type="gradio" textHandler={props.textHandler} appendHandler={props.appendHandler} handelError={props.handelError} catch={props.catch} />} | |
{props.catch && <div className='p-5'> | |
<Message floating negative> | |
<Message.Header className=" text-lg text-center">🚫 Something went wrong...</Message.Header> | |
<br /> | |
<h1 className=" underline pb-3 font-bold text-lg">🤔 Possible Things That could of happen <br /></h1> | |
<ul className="font-bold"> | |
<li key={"error_1"}>- The input was empty</li> | |
<li key={"error_2"}>- The connection was forbidden</li> | |
<li key={"error_3"}>- The name was already taken</li> | |
<li key={"error_4"}>- The link you gave did not pass the regex</li> | |
<ul className="px-6"> | |
<li key={"error_5"}>- http://localhost:xxxx</li> | |
<li key={"error_6"}>- http://xxxxx.gradio.app</li> | |
<li key={"error_7"}>- https://hf.space/embed/$user/$space_name/+</li> | |
</ul> | |
<li>- link already exist within the menu</li> | |
</ul> | |
</Message> | |
</div>} | |
</div> | |
} | |
{tab === "streamlit" && | |
<div className='w-full bg-white'> | |
<Shared type="streamlit" textHandler={props.textHandler} appendHandler={props.appendHandler} handelError={props.handelError} catch={props.catch} /> | |
</div> | |
} | |
{tab === "IframeURL" && ( | |
<form onSubmit={handleSubmit}> | |
<input | |
type="text" | |
placeholder="Enter iframe title here..." | |
value={iframeTitle} | |
onChange={(e) => setIframeTitle(e.target.value)} | |
className="input" | |
/> | |
<input | |
type="text" | |
placeholder="Enter iframe link here..." | |
value={iframeSrc} | |
onChange={(e) => setIframeSrc(e.target.value)} | |
className="input" | |
/> | |
<button type="submit" className="button">Embed</button> | |
</form> | |
)} | |
{iframeSrc && ( | |
<div className='p-5 flex flex-col items-start'> | |
<iframe src={iframeSrc} frameBorder="0" style={{ width: "100%", height: "400px" }} allowFullScreen></iframe> | |
</div> | |
)} | |
{iframeSrc && | |
<div className='p-5 flex flex-col items-start'> | |
<iframe src={iframeSrc} frameBorder="0" style={{ width: "100%", height: "400px" }} allowFullScreen></iframe> | |
</div> | |
} | |
{tab === "proxmox" && | |
<div className='w-full bg-white'> | |
<form onSubmit={handleProxmoxSubmit} className="p-5"> | |
<input | |
type="text" | |
placeholder="VM ID" | |
className="input input-bordered input-primary w-full max-w-xs" | |
value={vmid} | |
onChange={(e) => setVmid(e.target.value)} | |
/> | |
<input | |
type="text" | |
placeholder="Node" | |
className="input input-bordered input-primary w-full max-w-xs mt-2" | |
value={node} | |
onChange={(e) => setNode(e.target.value)} | |
/> | |
<button className="btn btn-primary mt-2" type="submit"> | |
Generate Proxmox noVNC Session | |
</button> | |
</form> | |
{iframeSrc && | |
<div className='p-5 flex flex-col items-start'> | |
<a href={iframeSrc} target="_blank" rel="noopener noreferrer" className="mb-2 underline text-blue-600 hover:text-blue-800 visited:text-purple-600">Access Proxmox noVNC Session</a> | |
<button onClick={() => navigator.clipboard.writeText(iframeSrc)} className="btn btn-primary"> | |
Copy Session Link | |
</button> | |
</div> | |
} | |
</div> | |
} | |
<div className='embed-form p-5'> | |
<input | |
type="text" | |
placeholder="Embed URL" | |
className="input input-bordered input-primary w-full max-w-xs" | |
value={embedUrl} | |
onChange={(e) => setEmbedUrl(e.target.value)} | |
/> | |
<button className="btn btn-primary mt-2" onClick={() => props.onAddEmbed({ url: embedUrl, type: 'embed' })}> | |
Add Embed | |
</button> | |
</div> | |
</Modal> | |
</div>) | |
} | |
function Local(props) { | |
return ( | |
<div className='p-5'> | |
<Message floating> | |
<Message.Header>🏗️ Comming soon...</Message.Header> | |
<Message.Content className='p-5'> | |
This tab will allow you grab your function from a given directory and build | |
your own tabular module gradio functions | |
</Message.Content> | |
</Message> | |
</div> | |
) | |
} | |
function Shared(props) { | |
const [preview, setPreview] = useState("") | |
const [fetchable, setFetch] = useState(false) | |
const isFetchable = async (url) => { | |
const pattern = { | |
share: new RegExp("^https?:\\/\\/([0-9a-zA-Z-]+)\\.gradio\\.live\\/?"), | |
hugginFace: new RegExp("^https?:\\/\\/([a-zA-Z0-9-]+)-gradio\\.hf\\.space\\/?$") | |
} | |
if (!pattern.share.test(url) && | |
!pattern.hugginFace.test(url)) { | |
setFetch(false) | |
return | |
} | |
fetch(url, { mode: "no-cors" }).then((re) => { | |
console.log(re) | |
if (re.url.includes("http://localhost:3000")) { | |
setFetch(false) | |
} else { | |
setFetch(true) | |
props.catch ? props.handelError(false) : props.handelError(props.catch) | |
} | |
}).catch((err) => { | |
setFetch(false) | |
}) | |
setFetch(false) | |
} | |
return ( | |
<div className='w-full shadow-lg' onKeyPress={(e) => { | |
if (e.key.includes("Enter")) props.appendHandler(props.type) | |
}}> | |
<div className='p-5'> | |
<Message floating> | |
<div className={`flex items-center rounded-md bg-light-white mt-6 border-dashed`}> | |
<label className="relative block w-full p-5 focus:shadow-xl"> | |
<span className={`absolute inset-y-0 left-0 flex items-center pl-8`}> | |
<BsSearch className="block float-left cursor-pointer text-gray-500" /> | |
</span> | |
<input className={`placeholder:italic placeholder:text-slate-400 text-black dark:text-white block w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 pr-3 focus:shadow-xl focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm bg-transparent`} | |
placeholder={`URL`} | |
type="text" name="search" | |
onChange={(e) => { | |
props.textHandler(e, "text") | |
setPreview(e.target.value) | |
setFetch(isFetchable(e.target.value)) | |
}} | |
/> | |
</label> | |
</div> | |
{fetchable === true && <div className=' w-full'> | |
<h1 className=' text-xl font-sans font-bold text-center text-black mb-2'> Preview </h1> | |
<div className='p-3 px-1 w-3/4 h-80 bg-gray-200 mr-auto ml-auto rounded-xl'> | |
<div className='w-full h-full overflow-hidden relative -ml-[5px]'> | |
<iframe title='Preview' src={preview} className=' absolute top-0 bottom-0 left-0 -right-[25px] overflow-y-scroll w-full h-full mr-auto ml-auto' /> | |
</div> | |
</div> | |
</div>} | |
<div className={`flex items-center rounded-md bg-light-white dark:bg-[#1b1c1d] mt-6 border-dashed`}> | |
<label className="relative block p-5 w-full focus:shadow-xl"> | |
<span className={`absolute inset-y-0 left-0 flex items-center pl-7`}> | |
<Icon className=" text-gray-500 block float-left cursor-pointer mr-2" name="address card" /> | |
</span> | |
<input className={`placeholder:italic placeholder:text-slate-400 text-black dark:text-white block bg-transparent w-full border border-slate-300 border-dashed rounded-md py-2 pl-9 pr-3 focus:shadow-xl focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm`} | |
placeholder={`Name ( > 20 Characters)`} | |
type="text" name="search" | |
autoComplete='off' | |
onChange={(e) => { | |
props.textHandler(e, "name") | |
}} | |
/> | |
</label> | |
</div> | |
<div className=' right-0 ml-5'> | |
<button className="relative inline-flex justify-center p-0.5 mb-2 mr-2 overflow-hidden text-sm font-sans font-bold text-gray-900 rounded-lg group bg-gradient-to-br from-purple-600 to-blue-500 group-hover:from-purple-600 group-hover:to-blue-500 hover:text-white dark:text-white focus:ring-4 focus:outline-none focus:ring-blue-300 dark:focus:ring-blue-800" | |
onClick={() => { props.appendHandler(props.type) }}> | |
<span className="relative px-5 py-2.5 transition-all ease-in duration-75 bg-white dark:bg-[#1b1c1d] rounded-md group-hover:bg-opacity-0"> | |
Enter | |
</span> | |
</button> | |
</div> | |
</Message> | |
</div> | |
</div> | |
) | |
} | |