Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion COPY.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ These exact strings. Do not paraphrase them per page.
| Agents way: terminal | From a terminal Β· Teaches your agent to search and run Skills in every project. | guidance only: the second way, over `npx skilld install skilld --global` |
| Agents way: MCP | In ChatGPT, Claude, and other MCP apps Β· Add the server once. Then ask the chat to find a Skill. | guidance only: the third way, over the MCP server URL. Its link, "Setup steps for each app", opens `/developers?setup=mcp` |
| Home section: demos | See what skills make. Β· Each demo is one recorded run: the prompt, and what the Agent built with the Skill. | guidance only: the homepage demo section heading and line. The section hides below three demos |
| Nav: demos | Skill Demos | guidance only: the header link after Trending Skills, and the same item in the mobile menu. Links `/skills/demos` |
| Demos link | All {n} demos | guidance only: under the homepage demo section. Links `/skills/demos` |
| Demos page | See what skills make Β· Each demo is one recorded run: the prompt, and what the Agent built with the Skill. Pick a prompt to see the output, or open it live. | guidance only: the `/skills/demos` H1 and line. The meta title is "Claude skill examples: see what each one makes" for the admitted query |
| Demo groups | Films and launch videos Β· Landing pages Β· UI components Β· Diagrams and explainers | guidance only: `DEMO_GROUPS` in `shared/demo-groups.ts`: the `/skills/demos` group headings and their lines |
| Demo stage | Open the demo | guidance only: the homepage demo stage link to the Skill page Demo panel |
| Demo panel | Demo Β· You say Β· Open live demo Β· Open in a new tab Β· Run it yourself | guidance only: the Skill page Demo panel. "Show screenshots" replaces "Open live demo" while the live output shows |
| Demo panel | Demo Β· Prompt Β· Open live demo Β· Open in a new tab Β· Run it yourself | guidance only: the Skill page Demo panel. "Show screenshots" replaces "Open live demo" while the live output shows |
| Demo provenance | {Agent logo} {model}, such as Opus 5.5 | guidance only: the recording line on the Skill page panel and the homepage stage. Screen readers and the title get "Recorded with {agent}, {model}". No date and no GitHub link: the Skill page is where visitors inspect a Skill |
| Demo outdated | Recorded on an older version of this Skill. | guidance only: under the Demo panel when the Skill moved past the recorded commit |
| Home section: changes | Keep up with skill changes. | guidance only: the homepage section heading for watching and the digest |
Expand Down
2 changes: 2 additions & 0 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ Collisions

**Rule:** a demo shows the Agent's output, never the Skill author's work. It always names how it was recorded.

**Groups:** demos group by what the Skill makes, on `/skills/demos`: films and launch videos, landing pages, UI components, and diagrams and explainers. A group is not a track: tracks sort Skills by kind of work.

### install

**Is:** `skilld install`. Skill files land in the project and the lockfile records them. The opt-in second step.
Expand Down
8 changes: 8 additions & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ watch(() => route.fullPath, () => {
<TrendingMark :accent="false" />
<span>Trending Skills</span>
</UButton>
<UButton
to="/skills/demos"
label="Skill Demos"
color="neutral"
variant="ghost"
block
class="min-h-11 justify-start"
/>
<UButton
to="/skills"
label="Find Skills"
Expand Down
263 changes: 263 additions & 0 deletions app/components/DemoIndex.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
<script setup lang="ts">
import type { HomeDemoItem } from '~/utils/home-demos'
import { githubAvatarProxyUrl } from '#shared/image-proxy'
import { demoKey } from '~/utils/home-demos'
import { resolveAuthorName } from '~/utils/skill-byline'
import DemoStage from './DemoStage.vue'

/**
* Demos as a prompt index. The prompts are the menu: pick one and the stage
* shows what the Agent made from it, scrollable to the end of the page, or its
* film playing. Each prompt carries a stone dot, and the picked one turns rose.
* On a phone the stage opens under the picked prompt.
*/
const { demos, initialKey, surface = 'demos' } = defineProps<{
demos: HomeDemoItem[]
/** `owner/repo/name` of the demo to show first, such as one from a shared link. */
initialKey?: string
/** The analytics surface for the run chip, such as `home-demos`. */
surface?: string
}>()

const emit = defineEmits<{ pick: [demo: HomeDemoItem] }>()

const stageId = useId()

const picked = ref(Math.max(0, demos.findIndex(demo => demoKey(demo) === initialKey)))
const current = computed(() => demos[Math.min(picked.value, demos.length - 1)])

function pick(index: number): void {
picked.value = index
const demo = demos[index]
if (demo)
emit('pick', demo)
}

function author(demo: HomeDemoItem): string {
return resolveAuthorName(demo.owner, demo.authorName) ?? demo.owner
}
</script>

<template>
<!-- One root, so a parent's class lands on it. -->
<div class="demo-index">
<div v-if="current" class="demo-index__grid">
<!-- Wide screens: the list scrolls inside the stage's height. -->
<div class="demo-index__rail">
<ol class="demo-index__list list-none p-0" aria-label="Prompts">
<li v-for="(demo, index) in demos" :key="demoKey(demo)" class="demo-index__entry">
<button
type="button"
class="demo-index__pick"
:aria-pressed="index === picked"
:aria-controls="stageId"
@click="() => pick(index)"
>
<span class="demo-index__pick-head">
<span class="demo-index__dot" aria-hidden="true" />
<img
:src="githubAvatarProxyUrl(demo.owner, 40)"
alt=""
width="20"
height="20"
loading="lazy"
decoding="async"
class="demo-index__avatar"
>
<span class="demo-index__name">/{{ demo.name }}</span>
<span class="demo-index__by">{{ author(demo) }}</span>
</span>
<span class="demo-index__say line-clamp-2">
<span class="sr-only">Prompt: </span>{{ demo.prompt }}
</span>
</button>
<!-- Phones: the stage opens under the picked prompt. -->
<div v-if="index === picked" class="demo-index__inline">
<DemoStage :demo :surface />
</div>
</li>
</ol>
</div>

<div :id="stageId" class="demo-index__stage">
<Transition name="demo-index-swap" mode="out-in">
<div :key="demoKey(current)">
<DemoStage :demo="current" :surface />
</div>
</Transition>
</div>
</div>
</div>
</template>

<style scoped>
.demo-index__grid {
display: grid;
gap: 1.5rem;
}

@media (min-width: 64rem) {
.demo-index__grid {
grid-template-columns: minmax(0, 5fr) minmax(0, 8fr);
gap: 2.5rem;
align-items: start;
}
}

.demo-index__list {
display: grid;
align-content: start;
gap: 0.5rem;
}

@media (min-width: 64rem) {
.demo-index__grid {
align-items: stretch;
}

.demo-index__rail {
position: relative;
}

/* Out of flow, so the stage alone sets the row height and the list scrolls in it. */
.demo-index__list {
position: absolute;
inset: 0;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
/* Room for the focus ring, which the scroll box would clip. */
padding: 3px 0.5rem 3px 3px;
}
}

.demo-index__pick {
display: grid;
inline-size: 100%;
gap: 0.375rem;
min-block-size: 2.75rem;
padding: 0.75rem 0.875rem;
border: 1px solid var(--ui-border);
border-radius: var(--ui-radius);
background: var(--ui-bg);
text-align: start;
cursor: pointer;
transition: border-color 200ms ease-out, background-color 200ms ease-out;
}

@media (hover: hover) {
.demo-index__pick:hover {
border-color: var(--ui-border-accented);
}
}

.demo-index__pick:focus-visible {
outline: 2px solid var(--ui-border-accented);
outline-offset: 2px;
}

.demo-index__pick[aria-pressed='true'] {
border-color: var(--ui-border-accented);
background: var(--ui-bg-muted);
}

.demo-index__pick-head {
display: flex;
min-inline-size: 0;
align-items: center;
gap: 0.5rem;
}

/* Stone for every prompt, rose for the one picked: the band's one rose dot. */
.demo-index__dot {
flex: none;
inline-size: 0.5rem;
block-size: 0.5rem;
border: 1.5px solid var(--ui-text-dimmed);
border-radius: 9999px;
}

.demo-index__pick[aria-pressed='true'] .demo-index__dot {
border-color: var(--ui-primary);
background: var(--ui-primary);
}

.demo-index__avatar {
flex: none;
inline-size: 1.25rem;
block-size: 1.25rem;
border: 1px solid var(--ui-border);
border-radius: 9999px;
background: var(--ui-bg-muted);
}

.demo-index__name {
min-inline-size: 0;
overflow: hidden;
font-family: var(--font-mono);
font-size: 0.875rem;
color: var(--ui-text-highlighted);
text-overflow: ellipsis;
white-space: nowrap;
}

.demo-index__by {
flex: none;
margin-inline-start: auto;
font-size: 0.75rem;
color: var(--ui-text-muted);
}

.demo-index__say {
padding-inline-start: 1rem;
font-size: 0.875rem;
line-height: 1.55;
color: var(--ui-text-muted);
}

.demo-index__pick[aria-pressed='true'] .demo-index__say {
color: var(--ui-text);
}

/* One stage per width: under the prompt on phones, beside the list from 1024px. */
.demo-index__inline {
margin-block-start: 0.75rem;
}

.demo-index__stage {
display: none;
}

@media (min-width: 64rem) {
.demo-index__inline {
display: none;
}

.demo-index__stage {
display: block;
position: sticky;
inset-block-start: 6rem;
}
}

.demo-index-swap-enter-active,
.demo-index-swap-leave-active {
transition: opacity 150ms ease-out;
}

.demo-index-swap-enter-from,
.demo-index-swap-leave-to {
opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
.demo-index__pick {
transition: none;
}

.demo-index-swap-enter-active,
.demo-index-swap-leave-active {
transition: none;
}
}
</style>
Loading