Spaces:
Running
Running
| <script lang="ts"> | |
| import { onMount, onDestroy } from "svelte"; | |
| import { fade } from "svelte/transition"; | |
| import gsap from "gsap"; | |
| export let onSendMessage: (message: string) => void; | |
| export let visible: boolean = true; | |
| const examples = [ | |
| { icon: "🏀", text: "add another ball" }, | |
| { icon: "📝", text: "explain what the code does" }, | |
| { icon: "🎮", text: "make a moving platform" }, | |
| { icon: "⬆️", text: "increase the player jump height" } | |
| ]; | |
| let exampleCards: HTMLButtonElement[] = []; | |
| onMount(() => { | |
| if (visible) { | |
| exampleCards.forEach((card, index) => { | |
| if (!card) return; | |
| gsap.fromTo(card, { | |
| opacity: 0, | |
| x: -10, | |
| scale: 0.95 | |
| }, { | |
| opacity: 1, | |
| x: 0, | |
| scale: 1, | |
| duration: 0.15, | |
| delay: index * 0.03, | |
| ease: "power2.out" | |
| }); | |
| }); | |
| } | |
| }); | |
| onDestroy(() => { | |
| exampleCards.forEach((card) => { | |
| if (card) { | |
| gsap.killTweensOf(card); | |
| } | |
| }); | |
| }); | |
| function handleClick(text: string) { | |
| onSendMessage(text); | |
| } | |
| function handleMouseEnter(event: MouseEvent) { | |
| const card = event.currentTarget as HTMLButtonElement; | |
| gsap.to(card, { | |
| scale: 1.05, | |
| duration: 0.08, | |
| ease: "power2.out" | |
| }); | |
| } | |
| function handleMouseLeave(event: MouseEvent) { | |
| const card = event.currentTarget as HTMLButtonElement; | |
| gsap.to(card, { | |
| scale: 1, | |
| duration: 0.1, | |
| ease: "power2.out" | |
| }); | |
| } | |
| function handleMouseDown(event: MouseEvent) { | |
| const card = event.currentTarget as HTMLButtonElement; | |
| gsap.to(card, { | |
| scale: 0.97, | |
| duration: 0.03, | |
| ease: "power2.in" | |
| }); | |
| } | |
| function handleMouseUp(event: MouseEvent) { | |
| const card = event.currentTarget as HTMLButtonElement; | |
| gsap.to(card, { | |
| scale: 1.05, | |
| duration: 0.05, | |
| ease: "back.out(1.5)" | |
| }); | |
| } | |
| </script> | |
| {#if visible} | |
| <div class="examples-row" transition:fade={{ duration: 200 }}> | |
| <span class="examples-label">Try an example:</span> | |
| <div class="examples-list"> | |
| {#each examples as example, i} | |
| <button | |
| bind:this={exampleCards[i]} | |
| class="example-pill" | |
| on:click={() => handleClick(example.text)} | |
| on:mouseenter={handleMouseEnter} | |
| on:mouseleave={handleMouseLeave} | |
| on:mousedown={handleMouseDown} | |
| on:mouseup={handleMouseUp} | |
| > | |
| <span class="example-icon">{example.icon}</span> | |
| <span class="example-text">{example.text}</span> | |
| </button> | |
| {/each} | |
| </div> | |
| </div> | |
| {/if} | |
| <style> | |
| .examples-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| padding: 0.75rem 1rem; | |
| background: rgba(0, 0, 0, 0.3); | |
| border-top: 1px solid rgba(255, 255, 255, 0.03); | |
| } | |
| .examples-label { | |
| font-size: 0.7rem; | |
| color: rgba(255, 255, 255, 0.35); | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| font-weight: 500; | |
| font-family: "Monaco", "Menlo", monospace; | |
| flex-shrink: 0; | |
| } | |
| .examples-list { | |
| display: flex; | |
| gap: 0.5rem; | |
| flex-wrap: wrap; | |
| flex: 1; | |
| } | |
| .example-pill { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.375rem; | |
| padding: 0.3rem 0.625rem; | |
| background: rgba(255, 255, 255, 0.02); | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| border-radius: 16px; | |
| cursor: pointer; | |
| transition: none; | |
| font-family: "Monaco", "Menlo", monospace; | |
| font-size: 0.7rem; | |
| color: rgba(255, 255, 255, 0.65); | |
| will-change: transform, box-shadow; | |
| flex-shrink: 0; | |
| min-width: 0; | |
| } | |
| .example-pill:hover { | |
| background: rgba(255, 255, 255, 0.04); | |
| border-color: rgba(255, 255, 255, 0.08); | |
| color: rgba(255, 255, 255, 0.85); | |
| } | |
| .example-icon { | |
| font-size: 0.75rem; | |
| opacity: 0.8; | |
| flex-shrink: 0; | |
| } | |
| .example-text { | |
| font-size: 0.7rem; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| @media (max-width: 768px) { | |
| .examples-row { | |
| gap: 0.75rem; | |
| padding: 0.75rem; | |
| } | |
| .examples-list { | |
| gap: 0.4rem; | |
| } | |
| .example-pill { | |
| padding: 0.25rem 0.5rem; | |
| font-size: 0.65rem; | |
| } | |
| .example-text { | |
| font-size: 0.65rem; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .examples-row { | |
| flex-direction: column; | |
| align-items: stretch; | |
| gap: 0.5rem; | |
| } | |
| .examples-label { | |
| text-align: center; | |
| font-size: 0.65rem; | |
| } | |
| .examples-list { | |
| justify-content: center; | |
| } | |
| .example-pill { | |
| flex: 0 1 auto; | |
| max-width: calc(50% - 0.25rem); | |
| } | |
| } | |
| @media (max-width: 360px) { | |
| .example-pill { | |
| max-width: 100%; | |
| } | |
| } | |
| </style> |