@@ -16,7 +16,8 @@ import { formatTokenCount } from '@/lib/format'
1616import { modelsData } from '@/lib/generated'
1717import { localizeManifestItems } from '@/lib/manifest-i18n'
1818import { buildModelComparisonPath } from '@/lib/model-comparison'
19- import { formatPrimaryTokenRate } from '@/lib/model-pricing'
19+ import { groupModelsByLifecycle } from '@/lib/model-list'
20+ import { formatModelTokenRate } from '@/lib/model-pricing'
2021import type { ManifestModel } from '@/types/manifests'
2122
2223type Props = {
@@ -67,21 +68,10 @@ export default function ModelsPageClient({ locale }: Props) {
6768 } , [ localizedModels , searchQuery ] )
6869
6970 // Group filtered models by lifecycle
70- const modelsByLifecycle = useMemo ( ( ) => {
71- const groups = {
72- latest : [ ] as ManifestModel [ ] ,
73- maintained : [ ] as ManifestModel [ ] ,
74- deprecated : [ ] as ManifestModel [ ] ,
75- }
76- filteredModels . forEach ( model => {
77- const lifecycle = model . lifecycle || 'maintained'
78- groups [ lifecycle ] . push ( model )
79- } )
80- return groups
81- } , [ filteredModels ] )
71+ const modelsByLifecycle = useMemo ( ( ) => groupModelsByLifecycle ( filteredModels ) , [ filteredModels ] )
8272
8373 const formatListPrice = ( model : ManifestModel ) : string => {
84- const price = formatPrimaryTokenRate ( model . tokenPricing , 'input' , locale , conversion )
74+ const price = formatModelTokenRate ( model , 'input' , locale , conversion )
8575 return price
8676 ? tShared ( 'modelPricing.perMillionTokens' , { price } )
8777 : tShared ( 'modelPricing.notAvailable' )
@@ -109,11 +99,11 @@ export default function ModelsPageClient({ locale }: Props) {
10999
110100 < StackTabs activeStack = "models" locale = { locale } />
111101
112- < section className = "mb-[var(--spacing-lg)] max-w-6xl border-y border-[var(--color-border)] py-[var(--spacing-md)]" >
102+ < section className = "mb-[var(--spacing-lg)] border-y border-[var(--color-border)] py-[var(--spacing-md)]" >
113103 < h2 className = "mb-[var(--spacing-xs)] text-lg font-semibold tracking-tight" >
114104 { tPage ( 'guide.title' ) }
115105 </ h2 >
116- < p className = "max-w-4xl text-sm leading-relaxed text-[var(--color-text-secondary)]" >
106+ < p className = "text-sm leading-relaxed text-[var(--color-text-secondary)]" >
117107 { tPage ( 'guide.description' ) }
118108 </ p >
119109 < div className = "mt-[var(--spacing-sm)] flex flex-wrap gap-[var(--spacing-md)] text-sm" >
@@ -150,28 +140,35 @@ export default function ModelsPageClient({ locale }: Props) {
150140 { tShared ( `lifecycle.${ lifecycle } ` ) }
151141 </ h2 >
152142 { modelsByLifecycle [ lifecycle ] . length > 0 ? (
153- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl :grid-cols-4 gap-[var(--spacing-md)]" >
143+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 2xl :grid-cols-4 gap-[var(--spacing-md)]" >
154144 { modelsByLifecycle [ lifecycle ] . map ( model => (
155145 < article
156146 key = { model . name }
157- className = { `border p-[var(--spacing-md)] transition-all hover:-translate-y-0.5 group ${
147+ className = { `group flex flex-col border p-[var(--spacing-md)] transition-all hover:-translate-y-0.5 ${
158148 selectedIds . includes ( model . id )
159149 ? 'border-[var(--color-border-strong)] bg-[var(--color-hover)]'
160150 : 'border-[var(--color-border)] hover:border-[var(--color-border-strong)]'
161151 } `}
162152 >
163- < Link href = { `/models/${ model . id } ` } className = "block" >
153+ < Link href = { `/models/${ model . id } ` } className = "block flex-1 " >
164154 < div className = "flex items-start mb-[var(--spacing-sm)]" >
165- < div className = "flex items-center gap-[var(--spacing-xs)]" >
166- < h3 className = "text-lg font-semibold tracking-tight" > { model . name } </ h3 >
155+ < div className = "inline-flex items-center gap-[var(--spacing-xs)] whitespace-nowrap" >
156+ < h3 className = "text-lg font-semibold tracking-tight whitespace-nowrap" >
157+ { model . name }
158+ </ h3 >
167159 { model . verified && < VerifiedBadge size = "sm" /> }
168160 </ div >
169161 </ div >
170- < div className = "space-y- [var(--spacing-xs )] mb- [var(--spacing-md )]" >
162+ < div className = "mb- [var(--spacing-sm )] space-y- [var(--spacing-xs )]" >
171163 < div className = "flex items-center gap-[var(--spacing-sm)] text-xs" >
172164 < span className = "text-[var(--color-text-muted)]" > { tPage ( 'size' ) } </ span >
173165 < span className = "text-[var(--color-text-secondary)]" >
174- { model . size ?? '—' }
166+ { model . size && model . activeParameters
167+ ? tShared ( 'modelParameters.totalAndActive' , {
168+ total : model . size ,
169+ active : model . activeParameters ,
170+ } )
171+ : ( model . size ?? '—' ) }
175172 </ span >
176173 </ div >
177174 < div className = "flex items-center gap-[var(--spacing-sm)] text-xs" >
@@ -191,24 +188,26 @@ export default function ModelsPageClient({ locale }: Props) {
191188 </ span >
192189 </ div >
193190 </ div >
194- < div className = "flex items-center gap-[var(--spacing-xs)] text-xs text-[var(--color-text-muted)]" >
195- < span > { model . vendor } </ span >
196- </ div >
197191 </ Link >
198- < button
199- type = "button"
200- aria-pressed = { selectedIds . includes ( model . id ) }
201- title = { `${ tShared ( 'actions.compare' ) } : ${ model . name } ` }
202- onClick = { ( ) => toggleModel ( model . id ) }
203- className = "mt-[var(--spacing-sm)] inline-flex w-full items-center justify-center gap-[var(--spacing-xs)] border-t border-[var(--color-border)] pt-[var(--spacing-sm)] text-xs font-medium text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
204- >
205- { selectedIds . includes ( model . id ) ? (
206- < Check className = "h-3.5 w-3.5" aria-hidden = "true" />
207- ) : (
208- < Scale className = "h-3.5 w-3.5" aria-hidden = "true" />
209- ) }
210- { tShared ( 'actions.compare' ) }
211- </ button >
192+ < div className = "mt-[var(--spacing-xs)] flex items-center justify-between gap-[var(--spacing-sm)] border-t border-[var(--color-border)] pt-[var(--spacing-sm)]" >
193+ < span className = "text-xs text-[var(--color-text-muted)]" >
194+ { model . vendor }
195+ </ span >
196+ < button
197+ type = "button"
198+ aria-pressed = { selectedIds . includes ( model . id ) }
199+ title = { `${ tShared ( 'actions.compare' ) } : ${ model . name } ` }
200+ onClick = { ( ) => toggleModel ( model . id ) }
201+ className = "inline-flex items-center gap-[var(--spacing-xs)] text-xs font-medium text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
202+ >
203+ { selectedIds . includes ( model . id ) ? (
204+ < Check className = "h-3.5 w-3.5" aria-hidden = "true" />
205+ ) : (
206+ < Scale className = "h-3.5 w-3.5" aria-hidden = "true" />
207+ ) }
208+ { tShared ( 'actions.compare' ) }
209+ </ button >
210+ </ div >
212211 </ article >
213212 ) ) }
214213 </ div >
0 commit comments