Skip to content

Commit cb2242d

Browse files
committed
feat(monkeyai): add localized login marquee
1 parent ed7a62b commit cb2242d

15 files changed

Lines changed: 351 additions & 7 deletions

File tree

monkeyai/admin/src/components/login-form.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useState } from "react"
33
import { HugeiconsIcon } from "@hugeicons/react"
44
import { useTranslation } from "react-i18next"
55

6+
import { LoginMarquees } from "@/components/login-marquees"
67
import { ShaderBackground } from "@/components/motion/shader-background"
78
import { Button } from "@/components/ui/button"
89
import { Card, CardContent } from "@/components/ui/card"
@@ -111,6 +112,7 @@ export function LoginForm({
111112
swirl={0.35}
112113
speed={0.35}
113114
/>
115+
<LoginMarquees />
114116
</div>
115117
</CardContent>
116118
</Card>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { useTranslation } from "react-i18next"
2+
3+
import { Marquee } from "@/components/motion/marquee"
4+
5+
const modelNames = [
6+
"GPT-6",
7+
"Claude Fable 5",
8+
"Deepseek-flash",
9+
"Qwen3.8",
10+
"Kimi K3",
11+
"GLM-5.3",
12+
]
13+
14+
export function LoginMarquees() {
15+
const { t } = useTranslation()
16+
const rows = [
17+
{
18+
id: "capabilities",
19+
items: t("login.marqueeCapabilities", {
20+
returnObjects: true,
21+
}) as string[],
22+
direction: "left",
23+
speed: 32,
24+
},
25+
{ id: "models", items: modelNames, direction: "right", speed: 38 },
26+
{
27+
id: "skills",
28+
items: t("login.marqueeSkills", { returnObjects: true }) as string[],
29+
direction: "left",
30+
speed: 35,
31+
},
32+
] as const
33+
34+
return (
35+
<div
36+
aria-hidden="true"
37+
dir="ltr"
38+
className="absolute inset-0 z-10 flex flex-col justify-center gap-8 py-12"
39+
>
40+
{rows.map(({ id, items, direction, speed }) => (
41+
<Marquee
42+
key={id}
43+
direction={direction}
44+
speed={speed}
45+
pauseOnHover={true}
46+
fade={true}
47+
gap="0.75rem"
48+
className="w-full"
49+
>
50+
{items.map((item) => (
51+
<span
52+
key={item}
53+
dir="auto"
54+
className="inline-flex h-10 items-center rounded-full bg-neutral-50/6 px-4 text-sm font-medium whitespace-nowrap text-neutral-950/60 dark:bg-neutral-900/6 dark:text-neutral-50/60"
55+
>
56+
{item}
57+
</span>
58+
))}
59+
</Marquee>
60+
))}
61+
</div>
62+
)
63+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// beui.dev/components/motion/marquee
2+
import { Children, type ReactNode, useState } from "react"
3+
import { cn } from "@/lib/utils"
4+
5+
export interface MarqueeProps {
6+
children: ReactNode
7+
direction?: "left" | "right" | "up" | "down"
8+
speed?: number
9+
pauseOnHover?: boolean
10+
gap?: string
11+
className?: string
12+
fade?: boolean
13+
}
14+
15+
export function Marquee({
16+
children,
17+
direction = "left",
18+
speed = 30,
19+
pauseOnHover = true,
20+
gap = "1rem",
21+
className,
22+
fade = true,
23+
}: MarqueeProps) {
24+
const vertical = direction === "up" || direction === "down"
25+
const reverse = direction === "right" || direction === "down"
26+
const items = Children.toArray(children)
27+
const [hovered, setHovered] = useState(false)
28+
29+
return (
30+
<div
31+
onMouseEnter={pauseOnHover ? () => setHovered(true) : undefined}
32+
onMouseLeave={pauseOnHover ? () => setHovered(false) : undefined}
33+
className={cn("relative overflow-hidden", className)}
34+
style={{ "--gap": gap } as React.CSSProperties}
35+
>
36+
<div
37+
className={cn(
38+
"flex w-full overflow-hidden",
39+
vertical ? "flex-col" : "flex-row",
40+
fade &&
41+
!vertical &&
42+
"[mask-image:linear-gradient(to_right,transparent,black_25%,black_75%,transparent)]",
43+
fade &&
44+
vertical &&
45+
"[mask-image:linear-gradient(to_bottom,transparent,black_25%,black_75%,transparent)]"
46+
)}
47+
style={{ gap }}
48+
>
49+
{[0, 1].map((dup) => (
50+
<div
51+
key={dup}
52+
aria-hidden={dup === 1}
53+
inert={dup === 1}
54+
style={{
55+
animationDuration: `${speed}s`,
56+
animationDirection: reverse ? "reverse" : "normal",
57+
animationPlayState: hovered ? "paused" : "running",
58+
gap,
59+
}}
60+
className={cn(
61+
"flex shrink-0 items-center",
62+
vertical
63+
? "animate-marquee-vertical flex-col"
64+
: "animate-marquee flex-row",
65+
"motion-reduce:animate-none"
66+
)}
67+
>
68+
{items.map((child, i) => (
69+
<div key={i} className="shrink-0">
70+
{child}
71+
</div>
72+
))}
73+
</div>
74+
))}
75+
</div>
76+
</div>
77+
)
78+
}

monkeyai/admin/src/i18n/locales/ar.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ export const ar = {
6161
forgotPassword: "هل نسيت كلمة المرور؟",
6262
submit: "تسجيل الدخول",
6363
otherLoginMethods: "طرق تسجيل الدخول الأخرى",
64+
marqueeCapabilities: [
65+
"النماذج",
66+
"المهارات",
67+
"الخبراء",
68+
"الأدوات",
69+
"الموصلات",
70+
"قواعد المعرفة",
71+
],
72+
marqueeSkills: [
73+
"مراجعة الشفرة",
74+
"البحث في المستندات",
75+
"تحليل البيانات",
76+
"البحث على الويب",
77+
"تلخيص المحتوى",
78+
"توليد الشفرة",
79+
"الأسئلة والأجوبة",
80+
],
6481
loginWith: "تسجيل الدخول باستخدام {{provider}}",
6582
oauthAdminRequired: "حساب OAuth هذا غير مرتبط بمسؤول نشط",
6683
oauthFailed: "فشل تسجيل الدخول عبر OAuth. حاول مرة أخرى",

monkeyai/admin/src/i18n/locales/de-DE.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ export const deDE = {
6262
forgotPassword: "Passwort vergessen?",
6363
submit: "Anmelden",
6464
otherLoginMethods: "Andere Anmeldemethoden",
65+
marqueeCapabilities: [
66+
"Modelle",
67+
"Skills",
68+
"Experten",
69+
"Werkzeuge",
70+
"Konnektoren",
71+
"Wissensdatenbanken",
72+
],
73+
marqueeSkills: [
74+
"Code-Review",
75+
"Dokumentensuche",
76+
"Datenanalyse",
77+
"Websuche",
78+
"Zusammenfassung",
79+
"Codegenerierung",
80+
"Fragen und Antworten",
81+
],
6582
loginWith: "Mit {{provider}} anmelden",
6683
oauthAdminRequired:
6784
"Dieses OAuth-Konto ist keinem aktiven Administrator zugeordnet",

monkeyai/admin/src/i18n/locales/en-US.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ export const enUS = {
190190
forgotPassword: "Forgot your password?",
191191
submit: "Sign in",
192192
otherLoginMethods: "Other sign-in methods",
193+
marqueeCapabilities: [
194+
"Models",
195+
"Skills",
196+
"Experts",
197+
"Tools",
198+
"Connectors",
199+
"Knowledge Bases",
200+
],
201+
marqueeSkills: [
202+
"Code Review",
203+
"Document Search",
204+
"Data Analysis",
205+
"Web Search",
206+
"Summarization",
207+
"Code Generation",
208+
"Q&A",
209+
],
193210
loginWith: "Sign in with {{provider}}",
194211
oauthAdminRequired:
195212
"This OAuth account is not linked to an active administrator",

monkeyai/admin/src/i18n/locales/es-419.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ export const es419 = {
6262
forgotPassword: "¿Olvidaste tu contraseña?",
6363
submit: "Iniciar sesión",
6464
otherLoginMethods: "Otros métodos de inicio de sesión",
65+
marqueeCapabilities: [
66+
"Modelos",
67+
"Habilidades",
68+
"Expertos",
69+
"Herramientas",
70+
"Conectores",
71+
"Bases de conocimiento",
72+
],
73+
marqueeSkills: [
74+
"Revisión de código",
75+
"Búsqueda de documentos",
76+
"Análisis de datos",
77+
"Búsqueda web",
78+
"Resumen de contenido",
79+
"Generación de código",
80+
"Preguntas y respuestas",
81+
],
6582
loginWith: "Iniciar sesión con {{provider}}",
6683
oauthAdminRequired:
6784
"Esta cuenta de OAuth no está vinculada a un administrador activo",

monkeyai/admin/src/i18n/locales/fr-FR.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ export const frFR = {
6262
forgotPassword: "Mot de passe oublié ?",
6363
submit: "Se connecter",
6464
otherLoginMethods: "Autres méthodes de connexion",
65+
marqueeCapabilities: [
66+
"Modèles",
67+
"Compétences",
68+
"Experts",
69+
"Outils",
70+
"Connecteurs",
71+
"Bases de connaissances",
72+
],
73+
marqueeSkills: [
74+
"Revue de code",
75+
"Recherche documentaire",
76+
"Analyse de données",
77+
"Recherche web",
78+
"Résumé",
79+
"Génération de code",
80+
"Questions-réponses",
81+
],
6582
loginWith: "Se connecter avec {{provider}}",
6683
oauthAdminRequired:
6784
"Ce compte OAuth n’est pas associé à un administrateur actif",

monkeyai/admin/src/i18n/locales/ja-JP.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ export const jaJP = {
6262
forgotPassword: "パスワードをお忘れですか?",
6363
submit: "ログイン",
6464
otherLoginMethods: "その他のログイン方法",
65+
marqueeCapabilities: [
66+
"モデル",
67+
"スキル",
68+
"エキスパート",
69+
"ツール",
70+
"コネクター",
71+
"ナレッジベース",
72+
],
73+
marqueeSkills: [
74+
"コードレビュー",
75+
"文書検索",
76+
"データ分析",
77+
"ウェブ検索",
78+
"要約",
79+
"コード生成",
80+
"質疑応答",
81+
],
6582
loginWith: "{{provider}} でログイン",
6683
oauthAdminRequired:
6784
"この OAuth アカウントは有効な管理者に関連付けられていません",

monkeyai/admin/src/i18n/locales/ko-KR.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ export const koKR = {
6161
forgotPassword: "비밀번호를 잊으셨나요?",
6262
submit: "로그인",
6363
otherLoginMethods: "다른 로그인 방법",
64+
marqueeCapabilities: [
65+
"모델",
66+
"스킬",
67+
"전문가",
68+
"도구",
69+
"커넥터",
70+
"지식 베이스",
71+
],
72+
marqueeSkills: [
73+
"코드 검토",
74+
"문서 검색",
75+
"데이터 분석",
76+
"웹 검색",
77+
"내용 요약",
78+
"코드 생성",
79+
"지식 질의응답",
80+
],
6481
loginWith: "{{provider}}로 로그인",
6582
oauthAdminRequired:
6683
"이 OAuth 계정은 활성 관리자 계정에 연결되어 있지 않습니다",

0 commit comments

Comments
 (0)