Skip to content

Commit 3f4fde4

Browse files
committed
feat(stage-pages,stage-ui,ui): character list for v2
1 parent 3787830 commit 3f4fde4

File tree

7 files changed

+457
-32
lines changed

7 files changed

+457
-32
lines changed

apps/stage-web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,14 @@
9898
"devDependencies": {
9999
"@iconify-json/carbon": "^1.2.15",
100100
"@iconify-json/eos-icons": "^1.2.4",
101+
"@iconify-json/logos": "catalog:",
101102
"@iconify-json/lucide": "^1.2.82",
102103
"@iconify-json/mingcute": "^1.2.6",
103104
"@iconify-json/ph": "^1.2.2",
104105
"@iconify-json/simple-icons": "^1.2.63",
105106
"@iconify-json/solar": "^1.2.5",
106107
"@iconify-json/svg-spinners": "^1.2.4",
108+
"@iconify-json/tabler": "catalog:",
107109
"@iconify-json/vscode-icons": "^1.2.37",
108110
"@intlify/unplugin-vue-i18n": "^11.0.1",
109111
"@proj-airi/lobe-icons": "^1.0.18",
Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
<script setup lang="ts">
2+
import { Button } from '@proj-airi/ui'
3+
4+
const coverImage = new URL('../../../../stage-ui/src/components/menu/relu.avif', import.meta.url).href
5+
const characterWithBackgroundBackgroundImage = new URL('../../../../../docs/.vitepress/assets/home-cover-2025-12-24-bg.avif', import.meta.url).href
6+
const characterWithBackgroundAvatar = new URL('../../../../../docs/.vitepress/assets/home-cover-2025-12-24.avif', import.meta.url).href
7+
const characterAvatarImage = new URL('../../../../stage-ui/src/assets/live2d/models/hiyori/preview.png', import.meta.url).href
8+
9+
function formatCount(value: number) {
10+
const units = [
11+
{ suffix: 'Q', value: 1_000_000_000_000_000 },
12+
{ suffix: 'T', value: 1_000_000_000_000 },
13+
{ suffix: 'B', value: 1_000_000_000 },
14+
{ suffix: 'M', value: 1_000_000 },
15+
{ suffix: 'K', value: 1_000 },
16+
]
17+
18+
for (const unit of units) {
19+
if (value >= unit.value) {
20+
const scaled = value / unit.value
21+
const digits = scaled >= 10 ? 0 : 1
22+
return `${scaled.toFixed(digits)}${unit.suffix}`
23+
}
24+
}
25+
26+
return value.toString()
27+
}
28+
29+
const characters = [
30+
{
31+
id: 'nyx-001',
32+
name: 'Aster Nyx',
33+
tagline: 'Solar striker with a radiant core.',
34+
creator: 'Kara Lin',
35+
creatorRole: 'Concept Artist',
36+
avatarUrl: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=200&q=80',
37+
characterAvatarUrl: characterWithBackgroundAvatar,
38+
coverUrl: characterWithBackgroundAvatar,
39+
coverBackgroundUrl: characterWithBackgroundBackgroundImage,
40+
usedBy: 24891,
41+
interactions: 482000,
42+
likes: 94000,
43+
bookmarks: 12000,
44+
forks: 1840,
45+
liked: true,
46+
bookmarked: false,
47+
priceCredit: 49,
48+
accent: {
49+
ring: 'ring-amber-400/60',
50+
chip: 'bg-amber-400/20 text-amber-100',
51+
glow: 'bg-amber-400/20',
52+
text: 'text-amber-200',
53+
},
54+
},
55+
{
56+
id: 'vale-008',
57+
name: 'Mira Vale',
58+
tagline: 'Glacial tactician who remains calm under pressure, analyzing every battlefield with precision and executing strategies that turn the tide of combat through calculated frost manipulation.',
59+
creator: 'Evan Wu',
60+
creatorRole: 'Gameplay Designer',
61+
avatarUrl: 'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?auto=format&fit=crop&w=200&q=80',
62+
characterAvatarUrl: characterAvatarImage,
63+
coverUrl: coverImage,
64+
usedBy: 17210,
65+
interactions: 361000,
66+
likes: 68300,
67+
bookmarks: 9200,
68+
forks: 1320,
69+
liked: false,
70+
bookmarked: true,
71+
priceCredit: 0,
72+
accent: {
73+
ring: 'ring-cyan-400/60',
74+
chip: 'bg-cyan-400/20 text-cyan-100',
75+
glow: 'bg-cyan-400/20',
76+
text: 'text-cyan-200',
77+
},
78+
},
79+
{
80+
id: 'sable-014',
81+
name: 'Rook Sable',
82+
tagline: 'Umbral runner who moves before you blink.',
83+
creator: 'Nina Park',
84+
creatorRole: 'Rigger',
85+
avatarUrl: 'https://images.unsplash.com/photo-1544723795-3fb6469f5b39?auto=format&fit=crop&w=200&q=80',
86+
characterAvatarUrl: characterAvatarImage,
87+
coverUrl: coverImage,
88+
usedBy: 31400,
89+
interactions: 509500,
90+
likes: 118200,
91+
bookmarks: 19200,
92+
forks: 2650,
93+
liked: true,
94+
bookmarked: true,
95+
priceCredit: 49,
96+
accent: {
97+
ring: 'ring-fuchsia-400/60',
98+
chip: 'bg-fuchsia-400/20 text-fuchsia-100',
99+
glow: 'bg-fuchsia-400/20',
100+
text: 'text-fuchsia-200',
101+
},
102+
},
103+
{
104+
id: 'sol-021',
105+
name: 'Iri Sol',
106+
tagline: 'Storm weaver with precision control.',
107+
creator: 'Daisuke Ito',
108+
creatorRole: 'Tech Artist',
109+
avatarUrl: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=200&q=80',
110+
characterAvatarUrl: characterAvatarImage,
111+
coverUrl: coverImage,
112+
usedBy: 20980000000000000,
113+
interactions: 2984000000000000,
114+
likes: 64300,
115+
bookmarks: 870000000,
116+
forks: 1150,
117+
liked: false,
118+
bookmarked: false,
119+
priceCredit: 49,
120+
accent: {
121+
ring: 'ring-sky-400/60',
122+
chip: 'bg-sky-400/20 text-sky-100',
123+
glow: 'bg-sky-400/20',
124+
text: 'text-sky-200',
125+
},
126+
},
127+
{
128+
id: 'rin-034',
129+
name: 'Kael Rin of the Earthbound Vanguard',
130+
tagline: 'Terra guardian, resilient and steady.',
131+
creator: 'Mara Voss',
132+
creatorRole: 'Animator',
133+
avatarUrl: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=200&q=80',
134+
characterAvatarUrl: characterAvatarImage,
135+
coverUrl: coverImage,
136+
usedBy: 15860,
137+
interactions: 244200,
138+
likes: 51400,
139+
bookmarks: 6900,
140+
forks: 940,
141+
liked: false,
142+
bookmarked: true,
143+
priceCredit: 49,
144+
accent: {
145+
ring: 'ring-emerald-400/60',
146+
chip: 'bg-emerald-400/20 text-emerald-100',
147+
glow: 'bg-emerald-400/20',
148+
text: 'text-emerald-200',
149+
},
150+
},
151+
{
152+
id: 'lux-047',
153+
name: 'Nova Lux',
154+
tagline: 'Astral support, lifts the whole squad.',
155+
creator: 'Yuki Tan',
156+
creatorRole: 'Illustrator',
157+
avatarUrl: 'https://images.unsplash.com/photo-1525134479668-1bee5c7c6845?auto=format&fit=crop&w=200&q=80',
158+
characterAvatarUrl: characterAvatarImage,
159+
coverUrl: coverImage,
160+
usedBy: 11420,
161+
interactions: 189000,
162+
likes: 40200,
163+
bookmarks: 5400,
164+
forks: 680,
165+
liked: true,
166+
bookmarked: false,
167+
priceCredit: 49,
168+
accent: {
169+
ring: 'ring-rose-400/60',
170+
chip: 'bg-rose-400/20 text-rose-100',
171+
glow: 'bg-rose-400/20',
172+
text: 'text-rose-200',
173+
},
174+
},
175+
]
176+
</script>
177+
178+
<template>
179+
<div :class="['min-h-screen w-full']">
180+
<div>
181+
<div :class="['mt-10 grid gap-6', 'sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5']">
182+
<article
183+
v-for="character in characters"
184+
:key="character.id"
185+
:class="[
186+
'group relative rounded-3xl overflow-hidden',
187+
'shadow-sm',
188+
'[&_.character-card-buttons-more]:opacity-0 [&_.character-card-buttons-more]:hover:opacity-100',
189+
'aspect-[12/19]',
190+
]"
191+
>
192+
<div :class="['relative overflow-hidden w-full h-70% rounded-2xl']">
193+
<img
194+
v-if="character.coverBackgroundUrl"
195+
:src="character.coverBackgroundUrl"
196+
:alt="character.name"
197+
:class="[
198+
'absolute inset-0 z-5',
199+
'w-full h-full',
200+
'object-cover',
201+
]"
202+
>
203+
<div
204+
v-else
205+
:class="[
206+
'absolute inset-0 z-5',
207+
'w-full h-full',
208+
'bg-white',
209+
]"
210+
/>
211+
<img
212+
:src="character.coverUrl"
213+
:alt="character.name"
214+
:class="[
215+
'absolute left-0 top-0 z-5',
216+
'h-full w-full',
217+
'object-cover',
218+
'transition duration-300 ease-in-out',
219+
]"
220+
>
221+
<!-- Dropdown -->
222+
<button
223+
type="button"
224+
:class="[
225+
'character-card-buttons-more',
226+
'absolute right-3 top-3 z-6',
227+
'h-7 w-7',
228+
'flex items-center justify-center',
229+
'rounded-lg backdrop-blur-sm',
230+
'text-white',
231+
'bg-neutral-900/30 hover:bg-neutral-900/45 active:bg-neutral-900/60',
232+
'dark:bg-neutral-950/50 hover:dark:bg-neutral-900/65 active:dark:bg-neutral-900/90',
233+
'transition duration-200 ease-in-out',
234+
]"
235+
aria-label="Options for character"
236+
>
237+
<div :class="['i-solar-menu-dots-bold inline-block']" />
238+
</button>
239+
240+
<!-- <div :class="['absolute top-3 left-3 flex justify-end z-8']">
241+
<div :class="['flex items-center gap-3', 'rounded-full bg-white/85 px-3 py-1', 'text-[11px] text-primary-600 backdrop-blur']">
242+
<div :class="['flex items-center gap-1.5']">
243+
<div :class="['i-solar-download-line-duotone text-sm text-primary-600 inline-block']" />
244+
<span :class="['text-primary-600']">{{ formatCount(character.usedBy) }}</span>
245+
</div>
246+
<div :class="['flex items-center gap-1.5']">
247+
<div :class="['i-solar-chat-square-arrow-linear text-sm text-primary-600 inline-block']" />
248+
<span :class="['text-primary-600']">{{ formatCount(character.interactions) }}</span>
249+
</div>
250+
</div>
251+
</div> -->
252+
</div>
253+
254+
<div :class="['relative z-7 overflow-hidden h-30%']">
255+
<div class="relative z-1 h-full">
256+
<div class="relative h-full flex flex-col justify-between gap-2 px-3 pb-3 pt-2">
257+
<div :class="['flex items-center justify-between gap-3']">
258+
<div :class="['flex items-center gap-2']">
259+
<img
260+
:src="character.characterAvatarUrl"
261+
:alt="character.name"
262+
:class="['h-7 w-7 rounded-full object-cover']"
263+
>
264+
<div :class="['text-lg font-semibold line-clamp-1']">
265+
{{ character.name }}
266+
</div>
267+
</div>
268+
<button
269+
aria-label="Connect"
270+
:class="[
271+
'px-2 py-1 flex flex-row items-center gap-1',
272+
]"
273+
>
274+
<div class="flex flex items-center gap-1">
275+
<div i-tabler:coins />
276+
<div :class="['text-xs']">
277+
{{ formatCount(character.priceCredit) }}
278+
</div>
279+
</div>
280+
</button>
281+
</div>
282+
<div :class="['flex-1 text-xs text-ellipsis text-neutral-500 line-clamp-3 max-h-[3rem] overflow-hidden']">
283+
{{ character.tagline }}
284+
</div>
285+
<div :class="['grid grid-cols-3 items-center']">
286+
<div :class="['flex items-center justify-start']">
287+
<Button variant="ghost" size="sm" aria-label="Bookmark">
288+
<div
289+
:class="[
290+
character.bookmarked ? 'i-solar-star-bold' : 'i-solar-star-linear',
291+
'text-base inline-block',
292+
character.bookmarked ? 'text-amber-300 dark:text-amber-500' : 'text-neutral-400',
293+
]"
294+
/>
295+
<span
296+
:class="[
297+
'text-xs',
298+
character.bookmarked ? 'text-amber-500 dark:text-amber-300' : 'text-neutral-500',
299+
]"
300+
>
301+
{{ formatCount(character.bookmarks) }}
302+
</span>
303+
</Button>
304+
</div>
305+
<div :class="['flex items-center justify-center']">
306+
<Button variant="ghost" size="sm" aria-label="Like">
307+
<div
308+
:class="[
309+
character.liked ? 'i-solar-heart-bold' : 'i-solar-heart-outline',
310+
'text-base inline-block',
311+
character.liked ? 'text-rose-500 dark:text-rose-400' : 'text-neutral-400',
312+
]"
313+
/>
314+
<span
315+
:class="[
316+
'text-xs',
317+
character.liked ? 'text-rose-500 dark:text-rose-400' : 'text-neutral-500',
318+
]"
319+
>
320+
{{ formatCount(character.likes) }}
321+
</span>
322+
</Button>
323+
</div>
324+
<div :class="['flex items-center justify-end']">
325+
<div
326+
:class="[
327+
'flex flex-row items-center gap-1',
328+
'pl-1.5 pr-2 py-1 rounded-full',
329+
'bg-neutral-900/50',
330+
]"
331+
>
332+
<div
333+
:class="[
334+
'i-ph:plus-bold',
335+
'text-xs inline-block',
336+
'text-neutral-100 dark:text-neutral-900',
337+
]"
338+
/>
339+
<span
340+
:class="[
341+
'text-xs',
342+
'text-neutral-100 dark:text-neutral-900',
343+
]"
344+
>
345+
Chat
346+
</span>
347+
</div>
348+
</div>
349+
</div>
350+
</div>
351+
</div>
352+
</div>
353+
354+
<div :class="['absolute left-0 top-0 z-0 w-full h-full']">
355+
<div :class="['absolute inset-0 z-2 bg-white/70 backdrop-blur-lg w-full h-full']" />
356+
<img
357+
:src="character.coverUrl"
358+
:alt="character.name"
359+
:class="[
360+
'relative z-1',
361+
'h-full w-full',
362+
'object-contain',
363+
'transition duration-300 ease-in-out',
364+
'scale-300 group-hover:scale-350',
365+
]"
366+
>
367+
</div>
368+
</article>
369+
</div>
370+
</div>
371+
</div>
372+
</template>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script setup lang="ts">
2+
import { RouterView } from 'vue-router'
3+
</script>
4+
5+
<template>
6+
<RouterView />
7+
</template>

0 commit comments

Comments
 (0)