-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Polish dashboard, separate out components
- Loading branch information
1 parent
22f8c50
commit fc4f2e5
Showing
6 changed files
with
210 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<script lang="ts"> | ||
import { EXAMPLES_TO_SHOW, EXAMPLE_PROMPTS } from '$lib/consts' | ||
export let input: HTMLTextAreaElement | null | ||
const getExammplePrompts = () => | ||
[...EXAMPLE_PROMPTS].sort(() => Math.random() - 0.5).slice(0, EXAMPLES_TO_SHOW) | ||
let examplePrompts = getExammplePrompts() | ||
</script> | ||
|
||
<div {...$$restProps}> | ||
<p class="font-mono pt-1 text-xs uppercase text-chalkboard-100 dark:text-chalkboard-20"> | ||
Example prompts: | ||
</p> | ||
<div class="prompt-buttons"> | ||
{#each examplePrompts as prompt (prompt)} | ||
<button | ||
on:click={() => { | ||
if (input) { | ||
input.value = prompt | ||
input.focus() | ||
examplePrompts = getExammplePrompts() | ||
} | ||
}} | ||
> | ||
<span class="mt-0.5">{prompt}</span> | ||
</button> | ||
{/each} | ||
</div> | ||
</div> | ||
|
||
<style lang="postcss"> | ||
.prompt-buttons { | ||
@apply grid md:grid-cols-2 gap-4 mt-4; | ||
} | ||
.prompt-buttons button { | ||
@apply text-sm text-left font-mono tracking-wider rounded border p-3 md:p-4; | ||
@apply bg-transparent hover:bg-green/50; | ||
@apply text-chalkboard-70 dark:text-chalkboard-50 hover:text-chalkboard-120 dark:hover:text-chalkboard-10; | ||
@apply border-chalkboard-30 dark:border-chalkboard-70; | ||
@apply hover:border-chalkboard-120 dark:hover:border-chalkboard-10; | ||
@apply hover:bg-green/20 text-chalkboard-120 dark:text-chalkboard-20 hover:hue-rotate-15; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<script lang="ts"> | ||
import autosize from 'svelte-autosize' | ||
import { page } from '$app/stores' | ||
import type { Models } from '@kittycad/lib' | ||
import ArrowRight from './Icons/ArrowRight.svelte' | ||
import { endpoints } from '$lib/endpoints' | ||
import { localGenerations } from '$lib/stores' | ||
import { paths } from '$lib/paths' | ||
import { goto } from '$app/navigation' | ||
export let input: HTMLTextAreaElement | null | ||
export let token: string | ||
let inputValue = $page.url.searchParams.get('prompt') ?? '' | ||
let form = null as HTMLFormElement | null | ||
let button = null as HTMLButtonElement | null | ||
let error: string | null = null | ||
let isSubmitting = false | ||
let showSuccessMessage: boolean = false | ||
let isCoolingDown = false | ||
function handleKeydown(e: KeyboardEvent) { | ||
if (e.key === 'Enter') { | ||
button?.click() | ||
} | ||
} | ||
const submitPrompt = async (prompt: string) => { | ||
const OUTPUT_FORMAT: Models['FileExportFormat_type'] = 'gltf' | ||
const response = await fetch(endpoints.prompt(OUTPUT_FORMAT), { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: 'Bearer ' + token | ||
}, | ||
body: JSON.stringify({ prompt }) | ||
}) | ||
if (!response.ok) { | ||
error = 'Failed to submit prompt' | ||
return | ||
} | ||
const responseData = (await response.json()) as Models['TextToCad_type'] | ||
$localGenerations = responseData ? [responseData, ...$localGenerations] : $localGenerations | ||
goto(paths.VIEW(responseData.id)) | ||
} | ||
const submitForm = async (e: Event) => { | ||
e.preventDefault() | ||
isSubmitting = true | ||
const prompt = (e.target as HTMLFormElement).prompt.value | ||
await submitPrompt(prompt) | ||
const form = e.target as HTMLFormElement | ||
form.reset() | ||
showSuccessMessage = true | ||
isCoolingDown = true | ||
setTimeout(() => { | ||
isCoolingDown = false | ||
}, 3000) | ||
isSubmitting = false | ||
} | ||
</script> | ||
|
||
<form on:submit={submitForm} class="flex border items-stretch text-lg" bind:this={form}> | ||
<label class="flex-1 grid place-items-center"> | ||
<span class="sr-only">Enter a text-to-CAD prompt:</span> | ||
<textarea | ||
autocapitalize="false" | ||
name="prompt" | ||
placeholder="e.g. Create a plate with 4 holes and rounded corners" | ||
required | ||
spellcheck="false" | ||
disabled={isSubmitting} | ||
class="w-full tracking-wide px-4 py-1 focus:outline-none focus:bg-green/20 focus:placeholder-shown:bg-green/10" | ||
bind:this={input} | ||
bind:value={inputValue} | ||
on:keydown={handleKeydown} | ||
on:input={() => { | ||
showSuccessMessage = false | ||
error = null | ||
}} | ||
use:autosize | ||
/> | ||
</label> | ||
<button type="submit" class="submit" disabled={isCoolingDown || isSubmitting} bind:this={button}> | ||
<span class="sr-only md:not-sr-only md:pt-0.5"> | ||
{#if isSubmitting} | ||
Submitting | ||
{:else} | ||
Submit | ||
{/if} | ||
</span> | ||
<ArrowRight class="w-8 h-8 md:w-5 md:h-5" /> | ||
</button> | ||
</form> | ||
{#if error} | ||
<p class="text-red mt-2">{error}</p> | ||
{:else if showSuccessMessage} | ||
<p class="border border-t-0 border-chalkboard-70 dark:border-chalkboard-40 p-2 bg-green/40"> | ||
Prompt submitted! | ||
</p> | ||
{/if} | ||
|
||
<style lang="postcss"> | ||
.submit { | ||
@apply m-1 md:px-4 lg:px-6 md:pt-1 border; | ||
@apply self-end flex items-center justify-center gap-2; | ||
@apply font-mono uppercase tracking-[1px] text-sm; | ||
@apply border-chalkboard-100 dark:border-chalkboard-20; | ||
@apply bg-green; | ||
@apply disabled:bg-chalkboard-40 dark:disabled:bg-chalkboard-70; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<div class="my-12 text-chalkboard-100 dark:text-chalkboard-20"> | ||
<h2 class="font-mono pt-1 text-xs uppercase">Prompt writing tips:</h2> | ||
<ul class="list-disc pl-6 text-lg"> | ||
<li class="my-4"> | ||
Describe an object that can be represented in geometric shapes, and less nebulous concepts | ||
such as "a tiger" or "the universe", unless you just want to see what it does with that 😏 | ||
</li> | ||
<li class="my-4"> | ||
Be as explicit as possible. For example, if you want a plate with 4 holes, say <em>where</em> | ||
you want the holes placed and <em>how big</em> of a diameter each should have | ||
</li> | ||
<li class="my-4"> | ||
Our ML models are better at describing single objects than assemblies <em>for now</em> | ||
</li> | ||
</ul> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,158 +1,39 @@ | ||
<script lang="ts"> | ||
import { endpoints } from '$lib/endpoints' | ||
import type { Models } from '@kittycad/lib' | ||
import PromptForm from 'components/PromptForm.svelte' | ||
import type { PageData } from '../view/$types' | ||
import { localGenerations } from '$lib/stores' | ||
import { goto } from '$app/navigation' | ||
import { EXAMPLE_PROMPTS } from '$lib/consts' | ||
import { page } from '$app/stores' | ||
import ExamplePrompts from 'components/ExamplePrompts.svelte' | ||
import { paths } from '$lib/paths' | ||
import autosize from 'svelte-autosize' | ||
import ArrowRight from 'components/Icons/ArrowRight.svelte' | ||
import PromptGuide from 'components/PromptGuide.svelte' | ||
export let data: PageData | ||
let form = null as HTMLFormElement | null | ||
let input = null as HTMLTextAreaElement | null | ||
let button = null as HTMLButtonElement | null | ||
const getExammplePrompts = () => [...EXAMPLE_PROMPTS].sort(() => Math.random() - 0.5).slice(0, 3) | ||
let examplePrompts = getExammplePrompts() | ||
let error: string | null = null | ||
let isSubmitting = false | ||
let showSuccessMessage: boolean = false | ||
let isCoolingDown = false | ||
let listSection = null as HTMLDivElement | null | ||
let inputValue = $page.url.searchParams.get('prompt') ?? '' | ||
function handleKeydown(e: KeyboardEvent) { | ||
if (e.key === 'Enter') { | ||
button?.click() | ||
} | ||
} | ||
const submitPrompt = async (prompt: string) => { | ||
const OUTPUT_FORMAT: Models['FileExportFormat_type'] = 'gltf' | ||
const response = await fetch(endpoints.prompt(OUTPUT_FORMAT), { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Authorization: 'Bearer ' + data.token | ||
}, | ||
body: JSON.stringify({ prompt }) | ||
}) | ||
if (!response.ok) { | ||
error = 'Failed to submit prompt' | ||
return | ||
} | ||
const responseData = (await response.json()) as Models['TextToCad_type'] | ||
$localGenerations = responseData ? [responseData, ...$localGenerations] : $localGenerations | ||
goto(paths.VIEW(responseData.id)) | ||
} | ||
const submitForm = async (e: Event) => { | ||
e.preventDefault() | ||
isSubmitting = true | ||
const prompt = (e.target as HTMLFormElement).prompt.value | ||
await submitPrompt(prompt) | ||
const form = e.target as HTMLFormElement | ||
form.reset() | ||
showSuccessMessage = true | ||
isCoolingDown = true | ||
setTimeout(() => { | ||
isCoolingDown = false | ||
}, 3000) | ||
listSection?.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) | ||
isSubmitting = false | ||
} | ||
</script> | ||
|
||
<section class="mx-auto max-w-2xl my-16 md:my-24 lg:my-48"> | ||
<h1 class="text-4xl md:text-5xl mb-2"> | ||
Text-to-<span class="text-green">CAD</span> | ||
</h1> | ||
<form on:submit={submitForm} class="flex border items-stretch text-lg" bind:this={form}> | ||
<label class="flex-1 grid place-items-center"> | ||
<span class="sr-only">Enter a text-to-CAD prompt:</span> | ||
<textarea | ||
autocapitalize="false" | ||
name="prompt" | ||
placeholder="e.g. Create a plate with 4 holes and rounded corners" | ||
required | ||
spellcheck="false" | ||
disabled={isSubmitting} | ||
class="w-full tracking-wide px-4 py-1 focus:outline-none focus:bg-green/20 focus:placeholder-shown:bg-green/10" | ||
bind:this={input} | ||
bind:value={inputValue} | ||
on:keydown={handleKeydown} | ||
on:input={() => { | ||
showSuccessMessage = false | ||
error = null | ||
}} | ||
use:autosize | ||
/> | ||
</label> | ||
<button | ||
type="submit" | ||
class="submit" | ||
disabled={isCoolingDown || isSubmitting} | ||
bind:this={button} | ||
> | ||
<span class="sr-only md:not-sr-only md:pt-0.5"> | ||
{#if isSubmitting} | ||
Submitting | ||
{:else} | ||
Submit | ||
{/if} | ||
</span> | ||
<ArrowRight class="w-8 h-8 md:w-5 md:h-5" /> | ||
</button> | ||
</form> | ||
{#if error} | ||
<p class="text-red mt-2">{error}</p> | ||
{:else if showSuccessMessage} | ||
<p class="border border-t-0 border-chalkboard-70 dark:border-chalkboard-40 p-2 bg-green/40"> | ||
Prompt submitted! | ||
</p> | ||
{/if} | ||
<div class="prompt-buttons"> | ||
<span class="font-mono pt-1 text-xs uppercase text-chalkboard-70 dark:text-chalkboard-40" | ||
>Example prompts:</span | ||
> | ||
{#each examplePrompts as prompt (prompt)} | ||
<button | ||
on:click={() => { | ||
if (input) { | ||
input.value = prompt | ||
input.focus() | ||
examplePrompts = getExammplePrompts() | ||
} | ||
}}>{prompt}</button | ||
> | ||
{/each} | ||
<section class="mx-auto min-h-screen flex flex-col justify-end"> | ||
<div class="max-w-2xl mx-auto mt-16"> | ||
<h1 class="text-4xl md:text-5xl mb-2"> | ||
Text-to-<span class="text-green">CAD</span> | ||
</h1> | ||
<div class="tracking-wide"> | ||
<PromptForm bind:input token={data.token} /> | ||
<ExamplePrompts {input} class="my-12" /> | ||
<PromptGuide /> | ||
</div> | ||
</div> | ||
<footer | ||
class="max-w-4xl w-full mx-auto flex flex-col md:flex-row gap-4 md:items-center justify-between px-2 lg:px-4 py-1 border border-b-0 text-xs font-mono text-chalkboard-70 dark:text-chalkboard-40" | ||
> | ||
<p> | ||
Built with the{' '} | ||
<a href={paths.ZOO_ML} target="_blank" rel="noopener noreferrer" class="underline"> | ||
ML-ephant API by Zoo | ||
</a> | ||
</p> | ||
<p> | ||
View and contribute on{' '} | ||
<a href={paths.GITHUB_REPO} target="_blank" rel="noopener noreferrer" class="underline"> | ||
GitHub | ||
</a> | ||
</p> | ||
</footer> | ||
</section> | ||
|
||
<style lang="postcss"> | ||
.submit { | ||
@apply m-1 md:px-4 lg:px-6 md:pt-1 border; | ||
@apply self-end flex items-center justify-center gap-2; | ||
@apply font-mono uppercase tracking-[1px] text-sm; | ||
@apply border-chalkboard-100 dark:border-chalkboard-20; | ||
@apply bg-green; | ||
@apply disabled:bg-chalkboard-40 dark:disabled:bg-chalkboard-70; | ||
} | ||
.prompt-buttons { | ||
@apply flex flex-wrap gap-2 mt-4; | ||
} | ||
.prompt-buttons button { | ||
@apply text-sm tracking-wider rounded-full border pt-0.5 pb-0 px-3; | ||
@apply bg-transparent hover:bg-green/50; | ||
@apply text-chalkboard-70 dark:text-chalkboard-50 hover:text-chalkboard-120 dark:hover:text-chalkboard-10; | ||
@apply border-chalkboard-30 dark:border-chalkboard-70; | ||
@apply hover:border-chalkboard-120 dark:hover:border-chalkboard-10; | ||
@apply hover:bg-green/20 text-chalkboard-120 dark:text-chalkboard-20 hover:hue-rotate-15; | ||
} | ||
</style> |