Upload folder using huggingface_hub
Browse files
client/src/components/Refinity.tsx
CHANGED
|
@@ -94,6 +94,19 @@ const Refinity: React.FC = () => {
|
|
| 94 |
return Math.min(Math.max(taskVersions.length - 1, 0), flowIndex);
|
| 95 |
}, [taskVersions, currentVersionId, flowIndex]);
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
const uploadDocx = async (file: File) => {
|
| 98 |
setUploading(true);
|
| 99 |
try {
|
|
@@ -351,29 +364,56 @@ const Refinity: React.FC = () => {
|
|
| 351 |
</div>
|
| 352 |
</div>
|
| 353 |
|
| 354 |
-
<div className="relative overflow-hidden py-10">
|
| 355 |
<div className="absolute left-2 top-1/2 -translate-y-1/2 z-10">
|
| 356 |
<button onClick={()=> setFlowIndex((i)=> Math.max(i-1, 0))} className="px-2 py-1 rounded-2xl bg-white/50 ring-1 ring-white/50 backdrop-blur-md text-sm">‹</button>
|
| 357 |
</div>
|
| 358 |
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-10">
|
| 359 |
<button onClick={()=> setFlowIndex((i)=> Math.min(i+1, Math.max(taskVersions.length-1, 0)))} className="px-2 py-1 rounded-2xl bg-white/50 ring-1 ring-white/50 backdrop-blur-md text-sm">›</button>
|
| 360 |
</div>
|
| 361 |
-
<div
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
{taskVersions.map((v, idx) => {
|
| 363 |
-
const
|
| 364 |
-
const
|
| 365 |
-
|
| 366 |
-
const
|
| 367 |
-
const
|
| 368 |
-
const
|
| 369 |
-
const
|
|
|
|
|
|
|
|
|
|
| 370 |
return (
|
| 371 |
-
<div
|
| 372 |
-
|
| 373 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
<div className="text-gray-800 text-sm mb-2">Original: {v.originalAuthor}</div>
|
| 375 |
<div className="text-gray-600 text-xs mb-3">Revised by: {v.revisedBy ? `${v.revisedBy} (v${v.versionNumber})` : `— (v${v.versionNumber})`}</div>
|
| 376 |
<div className="text-gray-900 whitespace-pre-wrap break-words min-h-[100px]">{snippet}</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
{isCenter && (
|
| 378 |
<div className="mt-4 flex gap-3">
|
| 379 |
<button onClick={()=>{ setPreviewVersionId(v.id); setStage('preview'); }} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 py-2 text-sm font-medium rounded-2xl text-black ring-1 ring-inset ring-white/50 backdrop-blur-md bg-white/30 active:translate-y-0.5 transition-all duration-200">
|
|
|
|
| 94 |
return Math.min(Math.max(taskVersions.length - 1, 0), flowIndex);
|
| 95 |
}, [taskVersions, currentVersionId, flowIndex]);
|
| 96 |
|
| 97 |
+
// Keyboard navigation for cover flow
|
| 98 |
+
React.useEffect(() => {
|
| 99 |
+
const handler = (e: KeyboardEvent) => {
|
| 100 |
+
if (e.key === 'ArrowLeft') {
|
| 101 |
+
setFlowIndex(i => Math.max(i - 1, 0));
|
| 102 |
+
} else if (e.key === 'ArrowRight') {
|
| 103 |
+
setFlowIndex(i => Math.min(i + 1, Math.max(taskVersions.length - 1, 0)));
|
| 104 |
+
}
|
| 105 |
+
};
|
| 106 |
+
window.addEventListener('keydown', handler);
|
| 107 |
+
return () => window.removeEventListener('keydown', handler);
|
| 108 |
+
}, [taskVersions.length]);
|
| 109 |
+
|
| 110 |
const uploadDocx = async (file: File) => {
|
| 111 |
setUploading(true);
|
| 112 |
try {
|
|
|
|
| 364 |
</div>
|
| 365 |
</div>
|
| 366 |
|
| 367 |
+
<div className="relative overflow-hidden py-10" style={{ perspective: '1200px' }}>
|
| 368 |
<div className="absolute left-2 top-1/2 -translate-y-1/2 z-10">
|
| 369 |
<button onClick={()=> setFlowIndex((i)=> Math.max(i-1, 0))} className="px-2 py-1 rounded-2xl bg-white/50 ring-1 ring-white/50 backdrop-blur-md text-sm">‹</button>
|
| 370 |
</div>
|
| 371 |
<div className="absolute right-2 top-1/2 -translate-y-1/2 z-10">
|
| 372 |
<button onClick={()=> setFlowIndex((i)=> Math.min(i+1, Math.max(taskVersions.length-1, 0)))} className="px-2 py-1 rounded-2xl bg-white/50 ring-1 ring-white/50 backdrop-blur-md text-sm">›</button>
|
| 373 |
</div>
|
| 374 |
+
<div
|
| 375 |
+
className="flex items-center justify-center gap-10 px-16 transition-transform duration-300"
|
| 376 |
+
onWheel={(e)=>{
|
| 377 |
+
if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
|
| 378 |
+
if (e.deltaX > 0) setFlowIndex(i => Math.min(i+1, Math.max(taskVersions.length-1,0)));
|
| 379 |
+
else setFlowIndex(i => Math.max(i-1, 0));
|
| 380 |
+
} else {
|
| 381 |
+
if (e.deltaY > 0) setFlowIndex(i => Math.min(i+1, Math.max(taskVersions.length-1,0)));
|
| 382 |
+
else setFlowIndex(i => Math.max(i-1, 0));
|
| 383 |
+
}
|
| 384 |
+
}}
|
| 385 |
+
>
|
| 386 |
{taskVersions.map((v, idx) => {
|
| 387 |
+
const offset = idx - focusedIndex;
|
| 388 |
+
const abs = Math.abs(offset);
|
| 389 |
+
const isCenter = abs === 0;
|
| 390 |
+
const translateX = offset * 260; // spacing between cards
|
| 391 |
+
const rotateY = -offset * 45; // angle for side cards
|
| 392 |
+
const translateZ = -abs * 140; // push side cards back a bit
|
| 393 |
+
const scale = isCenter ? 1.08 : 0.9;
|
| 394 |
+
const opacity = isCenter ? 1 : Math.max(0.6, 1 - abs * 0.15);
|
| 395 |
+
const zIndex = 100 - abs;
|
| 396 |
+
const snippet = (v.content || '').slice(0, 140) + ((v.content || '').length > 140 ? '…' : '');
|
| 397 |
return (
|
| 398 |
+
<div
|
| 399 |
+
key={v.id}
|
| 400 |
+
className="transition-transform duration-300 cursor-pointer"
|
| 401 |
+
style={{
|
| 402 |
+
transform: `translateX(${translateX}px) translateZ(${translateZ}px) rotateY(${rotateY}deg) scale(${scale})`,
|
| 403 |
+
opacity,
|
| 404 |
+
zIndex,
|
| 405 |
+
}}
|
| 406 |
+
onClick={() => { if (!isCenter) setFlowIndex(idx); }}
|
| 407 |
+
>
|
| 408 |
+
<div className="relative w-[520px] rounded-xl p-5 bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[0_20px_40px_rgba(0,0,0,0.15),inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)]">
|
| 409 |
<div className="text-gray-800 text-sm mb-2">Original: {v.originalAuthor}</div>
|
| 410 |
<div className="text-gray-600 text-xs mb-3">Revised by: {v.revisedBy ? `${v.revisedBy} (v${v.versionNumber})` : `— (v${v.versionNumber})`}</div>
|
| 411 |
<div className="text-gray-900 whitespace-pre-wrap break-words min-h-[100px]">{snippet}</div>
|
| 412 |
+
{/* Reflection */}
|
| 413 |
+
<div className="pointer-events-none absolute left-0 right-0 -bottom-20 h-16 opacity-35" style={{ transform: 'scaleY(-1)' }}>
|
| 414 |
+
<div className="w-full h-full rounded-xl bg-white/10 ring-1 ring-inset ring-white/30" />
|
| 415 |
+
<div className="absolute inset-0 bg-gradient-to-t from-white to-transparent" />
|
| 416 |
+
</div>
|
| 417 |
{isCenter && (
|
| 418 |
<div className="mt-4 flex gap-3">
|
| 419 |
<button onClick={()=>{ setPreviewVersionId(v.id); setStage('preview'); }} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-3 py-2 text-sm font-medium rounded-2xl text-black ring-1 ring-inset ring-white/50 backdrop-blur-md bg-white/30 active:translate-y-0.5 transition-all duration-200">
|