Spaces:
Running
Running
refactor(App.jsx): streamline component structure for better maintainability
Browse files- src/App.jsx +167 -144
src/App.jsx
CHANGED
@@ -27,9 +27,9 @@ import {
|
|
27 |
Center,
|
28 |
Collapse,
|
29 |
Icon,
|
30 |
-
|
31 |
} from '@chakra-ui/react';
|
32 |
-
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
|
33 |
import axios from 'axios';
|
34 |
import JSZip from 'jszip';
|
35 |
import { saveAs } from 'file-saver';
|
@@ -487,7 +487,7 @@ const App = () => {
|
|
487 |
display="flex"
|
488 |
alignItems="center"
|
489 |
justifyContent="center"
|
490 |
-
bg="
|
491 |
overflow="hidden"
|
492 |
position="relative"
|
493 |
>
|
@@ -498,161 +498,121 @@ const App = () => {
|
|
498 |
left={0}
|
499 |
right={0}
|
500 |
bottom={0}
|
501 |
-
opacity={0.
|
502 |
-
backgroundImage="radial-gradient(circle at 1px 1px, #
|
503 |
backgroundSize="40px 40px"
|
504 |
pointerEvents="none"
|
505 |
/>
|
506 |
|
507 |
-
|
508 |
-
|
509 |
-
maxW={{ base: "
|
510 |
w="full"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
511 |
>
|
512 |
-
{/* Title */}
|
513 |
-
<
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
boxShadow="0 10px 30px 0 rgba(0, 0, 0, 0.4)"
|
533 |
-
overflow="hidden"
|
534 |
-
p={6}
|
535 |
-
position="relative"
|
536 |
-
>
|
537 |
{/* Main Drop Area */}
|
538 |
<Box
|
539 |
w="full"
|
540 |
-
h="180px"
|
541 |
-
borderWidth="1px"
|
542 |
-
borderRadius="lg"
|
543 |
-
borderStyle="dashed"
|
544 |
-
borderColor="rgba(255, 255, 255, 0.2)"
|
545 |
display="flex"
|
546 |
alignItems="center"
|
547 |
justifyContent="center"
|
548 |
cursor="pointer"
|
549 |
-
bg="rgba(40, 40, 40, 0.4)"
|
550 |
transition="all 0.3s"
|
551 |
-
mb={uploadedImages.length > 0 ? 6 : 0}
|
552 |
-
_hover={{
|
553 |
-
bg: "rgba(50, 50, 50, 0.5)",
|
554 |
-
borderColor: "rgba(255, 255, 255, 0.3)",
|
555 |
-
}}
|
556 |
onClick={() => fileInputRef.current?.click()}
|
|
|
|
|
557 |
>
|
558 |
-
<
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
580 |
</Box>
|
581 |
-
|
582 |
-
{/*
|
583 |
<Box position="absolute" right={4} bottom={4}>
|
584 |
-
<
|
|
|
585 |
size="sm"
|
586 |
variant="ghost"
|
587 |
-
color="rgba(255, 255, 255, 0.
|
588 |
-
_hover={{ color: "white", bg: "rgba(255, 255, 255, 0.
|
589 |
onClick={toggleApiKey}
|
590 |
-
|
591 |
-
>
|
592 |
-
Settings
|
593 |
-
</Button>
|
594 |
-
</Box>
|
595 |
-
|
596 |
-
{/* Action Buttons - only shown when images are uploaded */}
|
597 |
-
{uploadedImages.length > 0 && (
|
598 |
-
<Flex justify="center" gap={4} wrap="wrap" mb={4}>
|
599 |
-
<Button
|
600 |
-
colorScheme="blue"
|
601 |
-
variant="ghost"
|
602 |
-
isLoading={processing}
|
603 |
-
loadingText="Processing"
|
604 |
-
onClick={processImages}
|
605 |
-
disabled={processing || uploadedImages.filter(img => img.status === 'pending').length === 0}
|
606 |
-
_hover={{ bg: "rgba(66, 153, 225, 0.2)" }}
|
607 |
-
>
|
608 |
-
Process Images
|
609 |
-
</Button>
|
610 |
-
|
611 |
-
<Button
|
612 |
-
variant="ghost"
|
613 |
-
color="teal.300"
|
614 |
-
isLoading={downloading}
|
615 |
-
loadingText="Downloading"
|
616 |
-
onClick={downloadCaptions}
|
617 |
-
disabled={downloading || !hasCaptions}
|
618 |
-
_hover={{ bg: "rgba(49, 151, 149, 0.2)" }}
|
619 |
-
>
|
620 |
-
Download Captions
|
621 |
-
</Button>
|
622 |
-
|
623 |
-
<Button
|
624 |
-
variant="ghost"
|
625 |
-
color="gray.400"
|
626 |
-
onClick={clearAll}
|
627 |
-
disabled={processing || uploadedImages.length === 0}
|
628 |
-
_hover={{ bg: "rgba(160, 174, 192, 0.2)" }}
|
629 |
-
>
|
630 |
-
Clear All
|
631 |
-
</Button>
|
632 |
-
</Flex>
|
633 |
-
)}
|
634 |
-
|
635 |
-
{processing && (
|
636 |
-
<Progress
|
637 |
-
value={progress}
|
638 |
-
size="xs"
|
639 |
-
colorScheme="blue"
|
640 |
-
hasStripe
|
641 |
-
isAnimated
|
642 |
borderRadius="full"
|
643 |
-
mb={4}
|
644 |
/>
|
645 |
-
|
646 |
|
647 |
{/* Advanced settings panel - collapsible */}
|
648 |
-
<Collapse in={isApiKeyOpen} animateOpacity>
|
649 |
<Box
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
borderWidth="1px"
|
655 |
-
borderColor="rgba(255, 255, 255, 0.05)"
|
656 |
>
|
657 |
<VStack spacing={4} align="stretch">
|
658 |
<FormControl>
|
@@ -738,23 +698,86 @@ const App = () => {
|
|
738 |
</Box>
|
739 |
</Collapse>
|
740 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
741 |
{/* Images Section */}
|
742 |
{uploadedImages.length > 0 && (
|
743 |
-
<Box
|
744 |
<Flex
|
745 |
justify="space-between"
|
746 |
align="center"
|
747 |
onClick={toggleImages}
|
748 |
cursor="pointer"
|
749 |
-
py={
|
750 |
-
|
751 |
-
_hover={{
|
752 |
>
|
753 |
<Text fontWeight="medium" color="#e0e0e0">Uploaded Images ({uploadedImages.length})</Text>
|
754 |
<Icon as={isImagesOpen ? ChevronUpIcon : ChevronDownIcon} color="#e0e0e0" />
|
755 |
</Flex>
|
756 |
<Collapse in={isImagesOpen} animateOpacity>
|
757 |
-
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4}
|
758 |
{uploadedImages.map((image) => (
|
759 |
<Box
|
760 |
key={image.id}
|
@@ -770,7 +793,7 @@ const App = () => {
|
|
770 |
<Box position="absolute" top={2} right={2} zIndex={1}>
|
771 |
{getStatusBadge(image.status)}
|
772 |
{image.isEdited && (
|
773 |
-
<Badge ml={2} bg="
|
774 |
)}
|
775 |
</Box>
|
776 |
<Image
|
@@ -897,12 +920,12 @@ const App = () => {
|
|
897 |
</Collapse>
|
898 |
</Box>
|
899 |
)}
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
</
|
905 |
-
</
|
906 |
</Box>
|
907 |
);
|
908 |
};
|
|
|
27 |
Center,
|
28 |
Collapse,
|
29 |
Icon,
|
30 |
+
IconButton,
|
31 |
} from '@chakra-ui/react';
|
32 |
+
import { ChevronDownIcon, ChevronUpIcon, SettingsIcon } from '@chakra-ui/icons';
|
33 |
import axios from 'axios';
|
34 |
import JSZip from 'jszip';
|
35 |
import { saveAs } from 'file-saver';
|
|
|
487 |
display="flex"
|
488 |
alignItems="center"
|
489 |
justifyContent="center"
|
490 |
+
bg="black"
|
491 |
overflow="hidden"
|
492 |
position="relative"
|
493 |
>
|
|
|
498 |
left={0}
|
499 |
right={0}
|
500 |
bottom={0}
|
501 |
+
opacity={0.02}
|
502 |
+
backgroundImage="radial-gradient(circle at 1px 1px, #666666 1px, transparent 0)"
|
503 |
backgroundSize="40px 40px"
|
504 |
pointerEvents="none"
|
505 |
/>
|
506 |
|
507 |
+
{/* Single container with everything */}
|
508 |
+
<Box
|
509 |
+
maxW={{ base: "90%", md: "450px" }}
|
510 |
w="full"
|
511 |
+
backdropFilter="blur(25px)"
|
512 |
+
bg="rgba(28, 28, 30, 0.75)"
|
513 |
+
borderRadius="2xl"
|
514 |
+
borderWidth="1px"
|
515 |
+
borderColor="rgba(255, 255, 255, 0.08)"
|
516 |
+
boxShadow="0 20px 40px rgba(0, 0, 0, 0.4)"
|
517 |
+
overflow="hidden"
|
518 |
+
position="relative"
|
519 |
>
|
520 |
+
{/* Title and Main Area */}
|
521 |
+
<VStack spacing={0} w="full">
|
522 |
+
<Box
|
523 |
+
w="full"
|
524 |
+
pt={8}
|
525 |
+
pb={4}
|
526 |
+
px={6}
|
527 |
+
textAlign="center"
|
528 |
+
>
|
529 |
+
<Heading
|
530 |
+
as="h1"
|
531 |
+
size="lg"
|
532 |
+
color="white"
|
533 |
+
fontWeight="semibold"
|
534 |
+
letterSpacing="tight"
|
535 |
+
>
|
536 |
+
Glif LoRA Autocaption
|
537 |
+
</Heading>
|
538 |
+
</Box>
|
539 |
+
|
|
|
|
|
|
|
|
|
|
|
540 |
{/* Main Drop Area */}
|
541 |
<Box
|
542 |
w="full"
|
|
|
|
|
|
|
|
|
|
|
543 |
display="flex"
|
544 |
alignItems="center"
|
545 |
justifyContent="center"
|
546 |
cursor="pointer"
|
|
|
547 |
transition="all 0.3s"
|
|
|
|
|
|
|
|
|
|
|
548 |
onClick={() => fileInputRef.current?.click()}
|
549 |
+
px={6}
|
550 |
+
pb={6}
|
551 |
>
|
552 |
+
<Box
|
553 |
+
w="full"
|
554 |
+
h="160px"
|
555 |
+
borderWidth="1px"
|
556 |
+
borderRadius="xl"
|
557 |
+
borderStyle="dashed"
|
558 |
+
borderColor="rgba(255, 255, 255, 0.2)"
|
559 |
+
display="flex"
|
560 |
+
alignItems="center"
|
561 |
+
justifyContent="center"
|
562 |
+
bg="rgba(20, 20, 20, 0.3)"
|
563 |
+
transition="all 0.3s"
|
564 |
+
_hover={{
|
565 |
+
bg: "rgba(30, 30, 30, 0.5)",
|
566 |
+
borderColor: "rgba(255, 255, 255, 0.3)",
|
567 |
+
transform: "translateY(-1px)"
|
568 |
+
}}
|
569 |
+
>
|
570 |
+
<VStack spacing={3}>
|
571 |
+
<Text color="#e0e0e0" fontSize="md">Drop images here or click to browse</Text>
|
572 |
+
<Button
|
573 |
+
size="sm"
|
574 |
+
bg="rgba(255, 255, 255, 0.08)"
|
575 |
+
color="white"
|
576 |
+
fontWeight="medium"
|
577 |
+
_hover={{ bg: "rgba(255, 255, 255, 0.15)" }}
|
578 |
+
_active={{ bg: "rgba(255, 255, 255, 0.1)" }}
|
579 |
+
disabled={processing}
|
580 |
+
>
|
581 |
+
Select Files
|
582 |
+
</Button>
|
583 |
+
</VStack>
|
584 |
+
<input
|
585 |
+
type="file"
|
586 |
+
multiple
|
587 |
+
accept="image/*"
|
588 |
+
onChange={handleFileChange}
|
589 |
+
style={{ display: 'none' }}
|
590 |
+
ref={fileInputRef}
|
591 |
+
/>
|
592 |
+
</Box>
|
593 |
</Box>
|
594 |
+
|
595 |
+
{/* Settings icon */}
|
596 |
<Box position="absolute" right={4} bottom={4}>
|
597 |
+
<IconButton
|
598 |
+
aria-label="Settings"
|
599 |
size="sm"
|
600 |
variant="ghost"
|
601 |
+
color="rgba(255, 255, 255, 0.5)"
|
602 |
+
_hover={{ color: "white", bg: "rgba(255, 255, 255, 0.08)" }}
|
603 |
onClick={toggleApiKey}
|
604 |
+
icon={<SettingsIcon />}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
605 |
borderRadius="full"
|
|
|
606 |
/>
|
607 |
+
</Box>
|
608 |
|
609 |
{/* Advanced settings panel - collapsible */}
|
610 |
+
<Collapse in={isApiKeyOpen} animateOpacity style={{ width: '100%' }}>
|
611 |
<Box
|
612 |
+
p={5}
|
613 |
+
bg="rgba(24, 24, 26, 0.8)"
|
614 |
+
borderTopWidth="1px"
|
615 |
+
borderTopColor="rgba(255, 255, 255, 0.05)"
|
|
|
|
|
616 |
>
|
617 |
<VStack spacing={4} align="stretch">
|
618 |
<FormControl>
|
|
|
698 |
</Box>
|
699 |
</Collapse>
|
700 |
|
701 |
+
{/* Action Buttons - only shown when images are uploaded */}
|
702 |
+
{uploadedImages.length > 0 && (
|
703 |
+
<Box
|
704 |
+
px={5}
|
705 |
+
py={4}
|
706 |
+
bg="rgba(22, 22, 24, 0.8)"
|
707 |
+
w="full"
|
708 |
+
borderTopWidth="1px"
|
709 |
+
borderTopColor="rgba(255, 255, 255, 0.05)"
|
710 |
+
>
|
711 |
+
<Flex justify="center" gap={4} wrap="wrap">
|
712 |
+
<Button
|
713 |
+
variant="ghost"
|
714 |
+
color="rgba(255, 255, 255, 0.9)"
|
715 |
+
isLoading={processing}
|
716 |
+
loadingText="Processing"
|
717 |
+
onClick={processImages}
|
718 |
+
disabled={processing || uploadedImages.filter(img => img.status === 'pending').length === 0}
|
719 |
+
_hover={{ bg: "rgba(255, 255, 255, 0.1)" }}
|
720 |
+
size="sm"
|
721 |
+
>
|
722 |
+
Process Images
|
723 |
+
</Button>
|
724 |
+
|
725 |
+
<Button
|
726 |
+
variant="ghost"
|
727 |
+
color="rgba(255, 255, 255, 0.9)"
|
728 |
+
isLoading={downloading}
|
729 |
+
loadingText="Downloading"
|
730 |
+
onClick={downloadCaptions}
|
731 |
+
disabled={downloading || !hasCaptions}
|
732 |
+
_hover={{ bg: "rgba(255, 255, 255, 0.1)" }}
|
733 |
+
size="sm"
|
734 |
+
>
|
735 |
+
Download Captions
|
736 |
+
</Button>
|
737 |
+
|
738 |
+
<Button
|
739 |
+
variant="ghost"
|
740 |
+
color="rgba(255, 255, 255, 0.6)"
|
741 |
+
onClick={clearAll}
|
742 |
+
disabled={processing || uploadedImages.length === 0}
|
743 |
+
_hover={{ bg: "rgba(255, 255, 255, 0.1)" }}
|
744 |
+
size="sm"
|
745 |
+
>
|
746 |
+
Clear All
|
747 |
+
</Button>
|
748 |
+
</Flex>
|
749 |
+
|
750 |
+
{processing && (
|
751 |
+
<Progress
|
752 |
+
value={progress}
|
753 |
+
size="xs"
|
754 |
+
colorScheme="blue"
|
755 |
+
hasStripe
|
756 |
+
isAnimated
|
757 |
+
borderRadius="full"
|
758 |
+
mt={3}
|
759 |
+
/>
|
760 |
+
)}
|
761 |
+
</Box>
|
762 |
+
)}
|
763 |
+
|
764 |
{/* Images Section */}
|
765 |
{uploadedImages.length > 0 && (
|
766 |
+
<Box w="full" borderTopWidth={isImagesOpen ? "1px" : "0px"} borderTopColor="rgba(255, 255, 255, 0.05)">
|
767 |
<Flex
|
768 |
justify="space-between"
|
769 |
align="center"
|
770 |
onClick={toggleImages}
|
771 |
cursor="pointer"
|
772 |
+
py={3}
|
773 |
+
px={5}
|
774 |
+
_hover={{ bg: "rgba(255, 255, 255, 0.03)" }}
|
775 |
>
|
776 |
<Text fontWeight="medium" color="#e0e0e0">Uploaded Images ({uploadedImages.length})</Text>
|
777 |
<Icon as={isImagesOpen ? ChevronUpIcon : ChevronDownIcon} color="#e0e0e0" />
|
778 |
</Flex>
|
779 |
<Collapse in={isImagesOpen} animateOpacity>
|
780 |
+
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4} p={5}>
|
781 |
{uploadedImages.map((image) => (
|
782 |
<Box
|
783 |
key={image.id}
|
|
|
793 |
<Box position="absolute" top={2} right={2} zIndex={1}>
|
794 |
{getStatusBadge(image.status)}
|
795 |
{image.isEdited && (
|
796 |
+
<Badge ml={2} bg="rgba(124, 58, 237, 0.6)" color="white">Edited</Badge>
|
797 |
)}
|
798 |
</Box>
|
799 |
<Image
|
|
|
920 |
</Collapse>
|
921 |
</Box>
|
922 |
)}
|
923 |
+
|
924 |
+
<Text fontSize="xs" color="gray.500" textAlign="center" py={2}>
|
925 |
+
Glif LoRA Autocaption • {new Date().getFullYear()}
|
926 |
+
</Text>
|
927 |
+
</VStack>
|
928 |
+
</Box>
|
929 |
</Box>
|
930 |
);
|
931 |
};
|