Create standalone browser port of LTX Athena with authenticated native API transport

This commit is contained in:
Mikei386
2026-09-29 17:59:23 +02:00
commit 1333b0c36b
202 changed files with 63054 additions and 0 deletions
@@ -0,0 +1,130 @@
import { useEffect, useRef, useState } from 'react'
export type GallerySize = 'small' | 'medium' | 'large'
export const gallerySizeClasses: Record<GallerySize, string> = {
small: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7',
medium: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5',
large: 'grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3',
}
function GridSmallIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<rect x="2" y="2" width="4" height="4" rx="0.5" />
<rect x="8" y="2" width="4" height="4" rx="0.5" />
<rect x="14" y="2" width="4" height="4" rx="0.5" />
<rect x="20" y="2" width="2" height="4" rx="0.5" />
<rect x="2" y="8" width="4" height="4" rx="0.5" />
<rect x="8" y="8" width="4" height="4" rx="0.5" />
<rect x="14" y="8" width="4" height="4" rx="0.5" />
<rect x="20" y="8" width="2" height="4" rx="0.5" />
<rect x="2" y="14" width="4" height="4" rx="0.5" />
<rect x="8" y="14" width="4" height="4" rx="0.5" />
<rect x="14" y="14" width="4" height="4" rx="0.5" />
<rect x="20" y="14" width="2" height="4" rx="0.5" />
</svg>
)
}
function GridMediumIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<rect x="2" y="2" width="6" height="6" rx="1" />
<rect x="10" y="2" width="6" height="6" rx="1" />
<rect x="18" y="2" width="4" height="6" rx="1" />
<rect x="2" y="10" width="6" height="6" rx="1" />
<rect x="10" y="10" width="6" height="6" rx="1" />
<rect x="18" y="10" width="4" height="6" rx="1" />
<rect x="2" y="18" width="6" height="4" rx="1" />
<rect x="10" y="18" width="6" height="4" rx="1" />
<rect x="18" y="18" width="4" height="4" rx="1" />
</svg>
)
}
function GridLargeIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<rect x="2" y="2" width="9" height="9" rx="1.5" />
<rect x="13" y="2" width="9" height="9" rx="1.5" />
<rect x="2" y="13" width="9" height="9" rx="1.5" />
<rect x="13" y="13" width="9" height="9" rx="1.5" />
</svg>
)
}
export function GenSpaceGallerySizeMenu({
gallerySize,
onGallerySizeChange,
}: {
gallerySize: GallerySize
onGallerySizeChange: (size: GallerySize) => void
}) {
const [open, setOpen] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setOpen(false)
}
}
if (open) {
document.addEventListener('mousedown', handleClickOutside)
}
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [open])
const CurrentIcon = gallerySize === 'small'
? GridSmallIcon
: gallerySize === 'medium'
? GridMediumIcon
: GridLargeIcon
return (
<div ref={menuRef} className="relative">
<button
type="button"
onClick={() => setOpen(value => !value)}
aria-label={`Gallery size: ${gallerySize}`}
aria-haspopup="menu"
aria-expanded={open}
className={`p-2 rounded-md transition-colors ${
open ? 'bg-zinc-800 text-white' : 'text-zinc-400 hover:text-white hover:bg-zinc-800'
}`}
>
<CurrentIcon className="h-4 w-4" />
</button>
{open && (
<div className="absolute top-full mt-2 right-0 bg-zinc-800 border border-zinc-700 rounded-md p-2 min-w-[160px] shadow-xl z-50">
{([
{ value: 'small' as const, label: 'Small', icon: GridSmallIcon },
{ value: 'medium' as const, label: 'Medium', icon: GridMediumIcon },
{ value: 'large' as const, label: 'Large', icon: GridLargeIcon },
]).map(option => (
<button
key={option.value}
type="button"
onClick={() => { onGallerySizeChange(option.value); setOpen(false) }}
className={`w-full flex items-center justify-between px-2 py-2.5 rounded-md transition-colors text-left ${gallerySize === option.value ? 'bg-white/20 hover:bg-white/25' : 'hover:bg-zinc-700'}`}
>
<div className="flex items-center gap-3">
<option.icon className={`h-4 w-4 ${gallerySize === option.value ? 'text-white' : 'text-zinc-500'}`} />
<span className={`text-sm ${gallerySize === option.value ? 'text-white font-medium' : 'text-zinc-400'}`}>
{option.label}
</span>
</div>
{gallerySize === option.value && (
<svg className="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
)}
</button>
))}
</div>
)}
</div>
)
}