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
4 changes: 2 additions & 2 deletions app/components/HomeWhy.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { RecentPullRequest } from '#shared/open-source-pull-requests'
import type { TrendingBoardRow } from '#shared/trending-range'
import type { HomeDemoItem } from '~/utils/home-demos'
import type { HomeDemoPreview } from '~/utils/home-demos'
import { HOME_WHY_REASONS, WHY_REASONS } from '~/utils/why-skilld'
import WhyTrustLine from './why/_WhyTrustLine.vue'
import WhyVisual from './why/_WhyVisual.vue'
Expand All @@ -18,7 +18,7 @@ const { trendingRow, demos, pulls } = defineProps<{
/** The first trending row ranked by posts, for the people picture. */
trendingRow: TrendingBoardRow | null
/** The approved demos, for the previews picture. */
demos: readonly HomeDemoItem[]
demos: readonly HomeDemoPreview[]
/** Recently merged pull requests, for the open source picture. */
pulls: readonly RecentPullRequest[]
}>()
Expand Down
6 changes: 3 additions & 3 deletions app/components/why/_WhyPreviewsVisual.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { HomeDemoItem } from '~/utils/home-demos'
import type { HomeDemoPreview } from '~/utils/home-demos'
import { demoHref, demoPreviews, demoRecording } from '~/utils/home-demos'
import WhyPanel from './_WhyPanel.vue'

Expand All @@ -12,12 +12,12 @@ import WhyPanel from './_WhyPanel.vue'
* two demos in one group the picture is not drawn.
*/
const { demos } = defineProps<{
demos: readonly HomeDemoItem[]
demos: readonly HomeDemoPreview[]
}>()

const picked = computed(() => demoPreviews(demos))

function still(demo: HomeDemoItem) {
function still(demo: HomeDemoPreview) {
if (demo.video)
return { src: demo.video.poster, width: demo.video.width, height: demo.video.height, alt: `A frame of the film the Agent made with /${demo.name}.` }
const shot = demo.shots.find(candidate => candidate.viewport === 'desktop') ?? demo.shots[0]
Expand Down
4 changes: 2 additions & 2 deletions app/components/why/_WhyVisual.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { RecentPullRequest } from '#shared/open-source-pull-requests'
import type { TrendingBoardRow } from '#shared/trending-range'
import type { HomeDemoItem } from '~/utils/home-demos'
import type { HomeDemoPreview } from '~/utils/home-demos'
import type { WhyReasonId } from '~/utils/why-skilld'
import WhyBehaviorsVisual from './_WhyBehaviorsVisual.vue'
import WhyCostVisual from './_WhyCostVisual.vue'
Expand All @@ -19,7 +19,7 @@ import WhyWeightsVisual from './_WhyWeightsVisual.vue'
const { id, trendingRow = null, demos = [], pulls = [], compact = false } = defineProps<{
id: WhyReasonId
trendingRow?: TrendingBoardRow | null
demos?: readonly HomeDemoItem[]
demos?: readonly HomeDemoPreview[]
pulls?: readonly RecentPullRequest[]
/** The fewest parts each picture can show, for a narrow column. */
compact?: boolean
Expand Down
26 changes: 22 additions & 4 deletions app/utils/home-demos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,37 @@ export const HOME_DEMOS_MIN = 3
export const HOME_DEMOS_MAX = 6

/** Compare up to three recordings in one group. Landing pages lead when available. */
export function demoPreviews(demos: readonly HomeDemoItem[]) {
export function demoPreviews<T extends Pick<HomeDemoItem, 'makes'>>(demos: readonly T[]) {
const groups = DEMO_GROUPS
.map(group => ({ group, items: demos.filter(demo => demo.makes === group.makes) }))
.filter(entry => entry.items.length >= 2)
const entry = groups.find(candidate => candidate.group.makes === 'landing-page') ?? groups[0]
return entry ? { label: entry.group.label, items: entry.items.slice(0, 3) } : null
}

export type HomeDemoPreview = Pick<HomeDemoItem, 'owner' | 'repo' | 'name' | 'skillPath' | 'makes' | 'agent' | 'model' | 'effort' | 'shots' | 'video'>

/** Comparison stills need recording details, not full prompts or writing documents. */
export function homeDemoPreview(demo: HomeDemoItem): HomeDemoPreview {
return {
owner: demo.owner,
repo: demo.repo,
name: demo.name,
skillPath: demo.skillPath,
makes: demo.makes,
agent: demo.agent,
model: demo.model,
effort: demo.effort,
shots: [demoDesktopShot(demo)].filter((shot): shot is HomeDemoShot => !!shot),
video: demo.video,
}
}

/** Keep only rendered demos in the homepage payload, plus the full feed count. */
export function homeDemoFeed(feed: { items: HomeDemoItem[] }) {
return {
items: feed.items.slice(0, HOME_DEMOS_MAX),
previews: demoPreviews(feed.items)?.items ?? [],
previews: (demoPreviews(feed.items)?.items ?? []).map(homeDemoPreview),
total: feed.items.length,
}
}
Expand All @@ -88,12 +106,12 @@ export function demoKey(demo: Pick<HomeDemoItem, 'owner' | 'repo' | 'name'>): st
}

/** The Demo panel on the Skill page. */
export function demoHref(demo: HomeDemoItem): string {
export function demoHref(demo: Pick<HomeDemoItem, 'skillPath'>): string {
return `${demo.skillPath}#demo`
}

/** The shot for wide screens: the desktop page, else whatever was recorded. */
export function demoDesktopShot(demo: HomeDemoItem): HomeDemoShot | undefined {
export function demoDesktopShot(demo: Pick<HomeDemoItem, 'shots'>): HomeDemoShot | undefined {
return demo.shots.find(shot => shot.viewport === 'desktop') ?? demo.shots[0]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ function showMore() {
</div>

<ul class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 list-none p-0">
<li v-for="skill in visibleSkills" :key="skill.slug">
<li v-for="skill in visibleSkills" :key="skill.registryPath">
<SkillCard :skill />
</li>
</ul>
Expand Down
5 changes: 2 additions & 3 deletions layers/marketing/app/utils/framework-directory.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { TagProfile } from '#layers/registry/server/api/tags/[slug].get'

export type FrameworkDirectoryProfile = Omit<TagProfile, 'skills'> & {
skills: Pick<TagProfile['skills'][number], 'owner' | 'repo' | 'name' | 'slug' | 'registryPath' | 'description' | 'stars' | 'authorName'>[]
skills: Pick<TagProfile['skills'][number], 'owner' | 'repo' | 'name' | 'registryPath' | 'description' | 'stars' | 'authorName'>[]
}

/** The directory keeps every result and only the fields its cards read. */
export function frameworkDirectory(profile: TagProfile): FrameworkDirectoryProfile {
return {
...profile,
skills: profile.skills.map(({ owner, repo, name, slug, registryPath, description, stars, authorName }) => ({
skills: profile.skills.map(({ owner, repo, name, registryPath, description, stars, authorName }) => ({
owner,
repo,
name,
slug,
registryPath,
description,
stars,
Expand Down
18 changes: 15 additions & 3 deletions test/unit/home-demos.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { HomeDemoItem } from '../../app/utils/home-demos'
import { describe, expect, it } from 'vitest'
import { demoPhoneRatio, demoRecording, demoSocialPicture, formatDemoDuration, homeDemoFeed } from '../../app/utils/home-demos'
import { demoDesktopShot, demoHref, demoPhoneRatio, demoRecording, demoSocialPicture, formatDemoDuration, homeDemoFeed } from '../../app/utils/home-demos'

function demo(overrides: Partial<HomeDemoItem> = {}): HomeDemoItem {
return {
Expand All @@ -26,19 +26,31 @@ function demo(overrides: Partial<HomeDemoItem> = {}): HomeDemoItem {
}

describe('homeDemoFeed', () => {
it('keeps comparison links, images, and recording details within a small payload', () => {
const films = Array.from({ length: 7 }, (_, index) => demo({ name: `film-${index}`, makes: 'film' }))
const pages = ['page-a', 'page-b'].map(name => demo({ name, makes: 'landing-page', prompt: 'Long recording prompt. '.repeat(1000) }))
const result = homeDemoFeed({ items: [...films, ...pages] })
expect(result.previews.map(item => ({ name: item.name, href: demoHref(item), shot: demoDesktopShot(item), recording: demoRecording(item).sentence }))).toEqual(pages.map(item => ({ name: item.name, href: demoHref(item), shot: demoDesktopShot(item), recording: demoRecording(item).sentence })))
expect(Buffer.byteLength(JSON.stringify(result.previews))).toBeLessThan(2048)
})
it('keeps the teaser order, total count, and preferred comparison beyond the teaser', () => {
const films = Array.from({ length: 7 }, (_, index) => demo({ name: `film-${index}`, makes: 'film' }))
const pages = Array.from({ length: 4 }, (_, index) => demo({ name: `page-${index}`, makes: 'landing-page' }))
const result = homeDemoFeed({ items: [...films, ...pages] })
expect(result).toEqual({ items: films.slice(0, 6), previews: pages.slice(0, 3), total: 11 })
expect(result.items).toEqual(films.slice(0, 6))
expect(result.previews.map(item => item.name)).toEqual(pages.slice(0, 3).map(item => item.name))
expect(result.total).toBe(11)
})

it('falls back to the first eligible group in display order', () => {
const component = demo({ name: 'component' })
const page = demo({ name: 'page', makes: 'landing-page' })
const films = [demo({ name: 'film-a', makes: 'film' }), demo({ name: 'film-b', makes: 'film' })]
const items = [component, page, ...films, demo({ name: 'other-component' })]
expect(homeDemoFeed({ items })).toEqual({ items, previews: films, total: 5 })
const result = homeDemoFeed({ items })
expect(result.items).toEqual(items)
expect(result.previews.map(item => item.name)).toEqual(films.map(item => item.name))
expect(result.total).toBe(5)
})

it('leaves comparison empty when no group has two recordings', () => {
Expand Down
Loading