feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
|
|
@ -4,23 +4,23 @@ import { Led } from './Led'
|
|||
interface BadgeProps {
|
||||
children: ReactNode
|
||||
led?: boolean
|
||||
ledColor?: 'amber' | 'green' | 'red'
|
||||
ledColor?: 'blue' | 'green' | 'red'
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function Badge({ children, led = false, ledColor = 'amber', className = '' }: BadgeProps) {
|
||||
export function Badge({ children, led = false, ledColor = 'blue', className = '' }: BadgeProps) {
|
||||
return (
|
||||
<span
|
||||
className={`
|
||||
inline-flex items-center gap-2 px-3 py-1
|
||||
font-mono text-nano uppercase tracking-widest
|
||||
text-neutral-400 bg-surface-700/50 border border-white/[0.06]
|
||||
rounded-sm select-none
|
||||
text-xs font-medium
|
||||
text-neutral-300 bg-surface-800/90 border border-white/10
|
||||
rounded-full shadow-sm select-none
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{led && <Led color={ledColor} size="sm" />}
|
||||
{children}
|
||||
<span>{children}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ interface DataPointProps {
|
|||
export function DataPoint({ label, value, unit, className = '' }: DataPointProps) {
|
||||
return (
|
||||
<div className={`flex flex-col ${className}`}>
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-neutral-500 mb-1">
|
||||
<span className="text-xs font-normal text-neutral-400 mb-1">
|
||||
{label}
|
||||
</span>
|
||||
<span className="font-display text-2xl md:text-3xl font-bold text-neutral-100 tracking-tight">
|
||||
<span className="text-2xl sm:text-3xl font-medium text-neutral-50 tracking-tight flex items-baseline">
|
||||
{value}
|
||||
{unit && (
|
||||
<span className="text-sm font-mono text-neutral-500 ml-1">{unit}</span>
|
||||
<span className="text-xs font-normal text-brand-blue ml-1.5">{unit}</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ interface GlowButtonProps {
|
|||
href?: string
|
||||
download?: string
|
||||
variant?: 'primary' | 'secondary'
|
||||
size?: 'md' | 'lg'
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
className?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export function GlowButton({
|
||||
|
|
@ -16,33 +17,34 @@ export function GlowButton({
|
|||
variant = 'primary',
|
||||
size = 'md',
|
||||
className = '',
|
||||
onClick,
|
||||
}: GlowButtonProps) {
|
||||
const base = 'glow-btn inline-flex items-center justify-center font-mono text-sm font-medium tracking-wide uppercase rounded-panel select-none'
|
||||
const base = 'glow-btn inline-flex items-center justify-center font-medium tracking-normal select-none rounded-xl transition-all duration-200 leading-normal'
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-brand-amber text-white hover:bg-brand-amber-light',
|
||||
secondary: 'bg-surface-500 text-neutral-200 border border-white/[0.08] hover:border-brand-amber/30',
|
||||
primary: 'bg-brand-blue text-white hover:bg-brand-blue-light shadow-glow-sm hover:shadow-glow-md active:scale-[0.98]',
|
||||
secondary: 'bg-surface-700/80 text-neutral-100 border border-white/10 hover:border-brand-blue/40 hover:bg-surface-600 hover:text-white active:scale-[0.98]',
|
||||
}
|
||||
|
||||
const sizes = {
|
||||
md: 'px-6 py-3 text-xs',
|
||||
lg: 'px-8 py-4 text-sm',
|
||||
sm: 'px-4.5 py-2 text-xs',
|
||||
md: 'px-6 py-2.5 sm:py-3 text-xs sm:text-sm font-medium',
|
||||
lg: 'px-8 sm:px-10 py-3.5 sm:py-4 text-sm sm:text-base font-medium',
|
||||
}
|
||||
|
||||
const cls = `${base} ${variants[variant]} ${sizes[size]} ${className}`
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<a href={href} download={download} className={cls}>
|
||||
<span className="relative z-10">{children}</span>
|
||||
<a href={href} download={download} onClick={onClick} className={cls}>
|
||||
<span className="relative z-10 flex items-center gap-2.5">{children}</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={cls}>
|
||||
<span className="relative z-10">{children}</span>
|
||||
<button type="button" onClick={onClick} className={cls}>
|
||||
<span className="relative z-10 flex items-center gap-2.5">{children}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@ interface InstrumentCardProps {
|
|||
className?: string
|
||||
hover?: boolean
|
||||
}
|
||||
|
||||
export function InstrumentCard({ children, className = '', hover = true }: InstrumentCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
relative noise-texture
|
||||
bg-surface-600 border border-white/[0.06]
|
||||
rounded-card shadow-chassis
|
||||
p-5 md:p-6
|
||||
${hover ? 'transition-all duration-250 hover:border-brand-amber/20 hover:shadow-glow-sm' : ''}
|
||||
bg-surface-800/80 border border-white/10
|
||||
rounded-2xl shadow-glass-card
|
||||
p-6 md:p-7 backdrop-blur-xl
|
||||
${hover ? 'transition-all duration-300 hover:border-brand-blue/35 hover:bg-surface-700/85 hover:shadow-glow-sm' : ''}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -21,32 +21,34 @@ export function LanguageSwitcher() {
|
|||
return (
|
||||
<div ref={ref} className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(!open)}
|
||||
className="flex items-center gap-1.5 px-2.5 py-1.5 font-mono text-nano uppercase tracking-widest text-neutral-400 hover:text-white border border-white/[0.06] rounded-panel transition-colors"
|
||||
className="flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-neutral-300 hover:text-white bg-surface-800/80 hover:bg-surface-700 border border-white/10 hover:border-brand-blue/40 rounded-lg transition-all shadow-sm"
|
||||
aria-label="Change language"
|
||||
>
|
||||
<GlobeIcon />
|
||||
{current?.label ?? 'EN'}
|
||||
<span>{current?.label ?? 'EN'}</span>
|
||||
<ChevronIcon open={open} />
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className="absolute right-0 top-full mt-2 w-40 py-1 bg-surface-700 border border-white/[0.08] rounded-card shadow-lg z-50 max-h-80 overflow-y-auto">
|
||||
<div className="absolute right-0 top-full mt-2 w-44 py-1.5 bg-surface-800/95 backdrop-blur-2xl border border-white/10 rounded-xl shadow-2xl z-50 max-h-80 overflow-y-auto">
|
||||
{LOCALES.map((l) => (
|
||||
<button
|
||||
key={l.code}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setLocale(l.code)
|
||||
setOpen(false)
|
||||
}}
|
||||
className={`w-full text-left px-3 py-2 font-mono text-xs flex items-center justify-between transition-colors ${
|
||||
className={`w-full text-left px-3.5 py-2 text-xs flex items-center justify-between transition-colors ${
|
||||
l.code === locale
|
||||
? 'text-brand-amber bg-brand-amber/[0.08]'
|
||||
: 'text-neutral-400 hover:text-white hover:bg-surface-600'
|
||||
? 'text-brand-blue bg-brand-blue/10 font-medium'
|
||||
: 'text-neutral-300 hover:text-white hover:bg-surface-700/80 font-normal'
|
||||
}`}
|
||||
>
|
||||
<span>{l.nativeName}</span>
|
||||
<span className="text-nano text-neutral-600 uppercase">{l.label}</span>
|
||||
<span className="text-[11px] text-neutral-400 font-normal">{l.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -57,7 +59,7 @@ export function LanguageSwitcher() {
|
|||
|
||||
function GlobeIcon() {
|
||||
return (
|
||||
<svg className="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg className="w-3.5 h-3.5 text-brand-blue" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M2 12h20" />
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||
|
|
@ -68,7 +70,7 @@ function GlobeIcon() {
|
|||
function ChevronIcon({ open }: { open: boolean }) {
|
||||
return (
|
||||
<svg
|
||||
className={`w-3 h-3 transition-transform ${open ? 'rotate-180' : ''}`}
|
||||
className={`w-3 h-3 text-neutral-400 transition-transform ${open ? 'rotate-180' : ''}`}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
interface LedProps {
|
||||
color?: 'amber' | 'green' | 'red'
|
||||
color?: 'blue' | 'green' | 'red'
|
||||
pulse?: boolean
|
||||
size?: 'sm' | 'md'
|
||||
className?: string
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
amber: {
|
||||
bg: 'bg-brand-amber',
|
||||
shadow: 'shadow-[0_0_6px_rgba(242,91,41,0.8)]',
|
||||
blue: {
|
||||
bg: 'bg-brand-blue',
|
||||
shadow: 'shadow-[0_0_6px_rgba(59,130,246,0.8)]',
|
||||
},
|
||||
green: {
|
||||
bg: 'bg-emerald-400',
|
||||
|
|
@ -25,7 +25,7 @@ const sizeMap = {
|
|||
md: 'w-2 h-2',
|
||||
} as const
|
||||
|
||||
export function Led({ color = 'amber', pulse = false, size = 'sm', className = '' }: LedProps) {
|
||||
export function Led({ color = 'blue', pulse = false, size = 'sm', className = '' }: LedProps) {
|
||||
const c = colorMap[color]
|
||||
const s = sizeMap[size]
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,21 +8,22 @@ interface SectionHeaderProps {
|
|||
export function SectionHeader({ index, title, subtitle, className = '' }: SectionHeaderProps) {
|
||||
return (
|
||||
<div className={`mb-12 md:mb-16 ${className}`}>
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<span className="font-mono text-nano uppercase tracking-widest text-brand-amber">
|
||||
<div className="flex items-center gap-3 mb-3.5">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium text-brand-blue bg-brand-blue/10 border border-brand-blue/25 shadow-sm">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-brand-blue" />
|
||||
{index}
|
||||
</span>
|
||||
<div className="h-px flex-1 bg-white/[0.06]" />
|
||||
<div className="h-px flex-1 bg-gradient-to-r from-brand-blue/20 via-white/[0.08] to-transparent" />
|
||||
</div>
|
||||
<h2
|
||||
className="font-display text-display font-bold text-neutral-50 tracking-tight break-keep"
|
||||
className="text-2xl sm:text-3xl md:text-4xl font-medium text-neutral-50 tracking-tight leading-[1.22] break-keep"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
{subtitle && (
|
||||
<p
|
||||
className="mt-3 max-w-xl text-base text-neutral-400 leading-relaxed break-keep"
|
||||
className="mt-3 max-w-2xl text-sm sm:text-base text-neutral-300 leading-relaxed font-normal break-keep"
|
||||
style={{ wordBreak: 'keep-all', overflowWrap: 'break-word' }}
|
||||
>
|
||||
{subtitle}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export function WaveBars({ className = '' }: WaveBarsProps) {
|
|||
<div
|
||||
key={i}
|
||||
ref={(el) => { barsRef.current[i] = el }}
|
||||
className="w-[3px] rounded-full bg-brand-amber/60"
|
||||
className="w-[3px] rounded-full bg-brand-blue/60"
|
||||
style={{ height: 4, transition: 'height 60ms ease-out' }}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue