|
<script lang="ts"> |
|
import { afterNavigate } from "$app/navigation"; |
|
import Code from "@gradio/code"; |
|
import Slider from "./Slider.svelte"; |
|
import Fullscreen from "./icons/Fullscreen.svelte"; |
|
import Close from "./icons/Close.svelte"; |
|
import { page } from "$app/stores"; |
|
import share from "$lib/assets/img/anchor_gray.svg"; |
|
import { svgCheck } from "$lib/assets/copy.js"; |
|
import { browser } from "$app/environment"; |
|
|
|
export let demos: { |
|
name: string; |
|
dir: string; |
|
code: string; |
|
requirements: string[]; |
|
}[]; |
|
export let current_selection: string; |
|
export let show_nav = true; |
|
|
|
let new_demo = { |
|
name: "Blank", |
|
dir: "Blank", |
|
code: "# Write your own Gradio code here and see what it looks like!\nimport gradio as gr\n\n", |
|
requirements: [] |
|
}; |
|
|
|
demos.push(new_demo); |
|
|
|
let mounted = false; |
|
let controller: any; |
|
|
|
let dummy_elem: any = { classList: { contains: () => false } }; |
|
let dummy_gradio: any = { dispatch: (_) => {} }; |
|
|
|
let requirements = |
|
demos.find((demo) => demo.name === current_selection)?.requirements || []; |
|
let code = demos.find((demo) => demo.name === current_selection)?.code || ""; |
|
|
|
afterNavigate(() => { |
|
controller = createGradioApp({ |
|
target: document.getElementById("lite-demo"), |
|
requirements: demos[0].requirements, |
|
code: demos[0].code, |
|
info: true, |
|
container: true, |
|
isEmbed: true, |
|
initialHeight: "100%", |
|
eager: false, |
|
themeMode: null, |
|
autoScroll: false, |
|
controlPageTitle: false, |
|
appMode: true |
|
}); |
|
mounted = true; |
|
}); |
|
|
|
function update(code: string, requirements: string[]) { |
|
try { |
|
controller.run_code(code); |
|
controller.install(requirements); |
|
} catch (error) { |
|
console.error(error); |
|
} |
|
controller.run_code(code); |
|
controller.install(requirements); |
|
} |
|
|
|
let timeout: any; |
|
|
|
let copied_link = false; |
|
async function copy_link(name: string) { |
|
let code_b64 = btoa(code); |
|
name = name.replaceAll(" ", "_"); |
|
await navigator.clipboard.writeText( |
|
`${$page.url.href.split("?")[0]}?demo=${name}&code=${code_b64}` |
|
); |
|
copied_link = true; |
|
setTimeout(() => (copied_link = false), 2000); |
|
} |
|
|
|
$: code = demos.find((demo) => demo.name === current_selection)?.code || ""; |
|
$: requirements = |
|
demos.find((demo) => demo.name === current_selection)?.requirements || []; |
|
|
|
$: if (mounted) { |
|
if (timeout) { |
|
clearTimeout(timeout); |
|
} |
|
timeout = setTimeout(() => { |
|
update(code, requirements); |
|
}, 1000); |
|
} |
|
|
|
let position = 0.5; |
|
|
|
let fullscreen = false; |
|
function make_full_screen() { |
|
fullscreen = true; |
|
} |
|
let preview_width = 100; |
|
let lg_breakpoint = false; |
|
|
|
$: lg_breakpoint = preview_width - 13 >= 688; |
|
|
|
if (browser) { |
|
let linked_demo = $page.url.searchParams.get("demo"); |
|
let b64_code = $page.url.searchParams.get("code"); |
|
|
|
if (linked_demo && b64_code) { |
|
current_selection = linked_demo.replaceAll("_", " "); |
|
let demo = demos.find((demo) => demo.name === current_selection); |
|
if (demo) { |
|
demo.code = atob(b64_code); |
|
} |
|
} |
|
} |
|
</script> |
|
|
|
<svelte:head> |
|
<link |
|
rel="stylesheet" |
|
href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" |
|
/> |
|
<link rel="stylesheet" href="https://gradio-hello-world.hf.space/theme.css" /> |
|
</svelte:head> |
|
<div class="flex flex-row" style="position: absolute; top: -6%; right: 0.4%"> |
|
<button |
|
class="border border-gray-300 rounded-md mx-2 px-2 py-.5 my-[3px] text-md text-gray-600 hover:bg-gray-50 flex" |
|
on:click={() => copy_link(current_selection)} |
|
> |
|
{#if !copied_link} |
|
<img |
|
class="!w-5 align-text-top inline-block self-center mr-1" |
|
src={share} |
|
/> |
|
<p class="inline-block">Share Your App</p> |
|
{:else} |
|
<div class="inline-block align-text-top !w-5 self-center"> |
|
{@html svgCheck} |
|
</div> |
|
<p class="inline-block">Copied Link!</p> |
|
{/if} |
|
</button> |
|
</div> |
|
<div |
|
class=" absolute top-0 bottom-0 right-0" |
|
style="left:{show_nav ? 200 : 37}px" |
|
> |
|
<Slider bind:position bind:show_nav> |
|
<div class="flex-row min-w-0 h-full" class:flex={!fullscreen}> |
|
{#each demos as demo, i} |
|
<div |
|
hidden={current_selection !== demo.name} |
|
class="code-editor w-full border-r" |
|
style="width: {position * 100}%" |
|
> |
|
<div class="flex justify-between align-middle h-8 border-b pl-4 pr-2"> |
|
<h3 class="pt-1">Code</h3> |
|
<div class="flex float-right"></div> |
|
</div> |
|
|
|
<Code |
|
bind:value={demos[i].code} |
|
label="" |
|
language="python" |
|
target={dummy_elem} |
|
gradio={dummy_gradio} |
|
lines={10} |
|
interactive="true" |
|
/> |
|
</div> |
|
{/each} |
|
|
|
<div |
|
class="preview w-full mx-auto" |
|
style="width: {fullscreen ? 100 : (1 - position) * 100}%" |
|
class:fullscreen |
|
bind:clientWidth={preview_width} |
|
> |
|
<div |
|
class="flex justify-between align-middle h-8 border-b pl-4 pr-2 ml-0 sm:ml-2" |
|
> |
|
<div class="flex align-middle"> |
|
<h3 class="pr-2 pt-1">Preview</h3> |
|
<p class="pt-1.5 text-sm text-gray-600 hidden sm:block"> |
|
{preview_width - 13}px |
|
</p> |
|
<p |
|
class:text-orange-300={lg_breakpoint} |
|
class:text-gray-300={!lg_breakpoint} |
|
class="pt-2 text-sm pl-2 w-6 hidden sm:block" |
|
> |
|
<svg viewBox="0 0 110 100" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="50" height="100" rx="15" fill="currentColor" /> |
|
<rect |
|
x="60" |
|
width="50" |
|
height="100" |
|
rx="15" |
|
fill="currentColor" |
|
/> |
|
</svg> |
|
</p> |
|
<p |
|
class:text-orange-300={!lg_breakpoint} |
|
class:text-gray-300={lg_breakpoint} |
|
class="pt-2 text-sm pl-2 w-6 hidden sm:block" |
|
> |
|
<svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="110" height="45" rx="15" fill="currentColor" /> |
|
<rect |
|
y="50" |
|
width="110" |
|
height="45" |
|
rx="15" |
|
fill="currentColor" |
|
/> |
|
</svg> |
|
</p> |
|
</div> |
|
<div class="flex"> |
|
{#if !fullscreen}<button |
|
class="ml-1 w-[20px] float-right text-gray-600" |
|
on:click={() => (fullscreen = true)}><Fullscreen /></button |
|
>{:else} |
|
<button |
|
class="ml-1 w-[15px] float-right text-gray-600" |
|
on:click={() => (fullscreen = false)}><Close /></button |
|
> |
|
{/if} |
|
</div> |
|
</div> |
|
|
|
<div class="lite-demo h-[93%] pl-3" id="lite-demo" /> |
|
</div> |
|
</div> |
|
</Slider> |
|
</div> |
|
|
|
<style> |
|
:global(div.code-editor div.block) { |
|
height: calc(100% - 2rem); |
|
border-radius: 0; |
|
border: none; |
|
} |
|
|
|
:global(div.code-editor div.block .cm-gutters) { |
|
background-color: white; |
|
} |
|
|
|
:global(div.code-editor div.block .cm-content) { |
|
width: 0; |
|
} |
|
|
|
:global(div.lite-demo div.gradio-container) { |
|
height: 100%; |
|
overflow-y: scroll; |
|
margin: 0 !important; |
|
} |
|
|
|
.code-editor :global(label) { |
|
display: none; |
|
} |
|
|
|
.code-editor :global(.codemirror-wrappper) { |
|
border-radius: var(--block-radius); |
|
} |
|
|
|
.code-editor :global(> .block) { |
|
border: none !important; |
|
} |
|
|
|
.code-editor :global(.cm-scroller) { |
|
height: 100% !important; |
|
} |
|
|
|
.lite-demo :global(.embed-container) { |
|
border: none !important; |
|
} |
|
|
|
.fullscreen { |
|
position: fixed !important; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
z-index: 1000; |
|
background-color: white; |
|
} |
|
/* .preview { |
|
width: 100% !important; |
|
} */ |
|
@media (max-width: 640px) { |
|
.preview { |
|
width: 100% !important; |
|
} |
|
} |
|
</style> |
|
|