saq1b commited on
Commit
aca7242
·
1 Parent(s): 6654d7d

refactor(vite.config.js): enhance build configuration with manual chunking and dependency optimization

Browse files
Files changed (2) hide show
  1. src/App.jsx +275 -261
  2. vite.config.js +15 -1
src/App.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from 'react';
2
  import {
3
  Box,
4
  Container,
@@ -16,6 +16,7 @@ import {
16
  Badge,
17
  Flex,
18
  Divider,
 
19
  Textarea,
20
  Tooltip,
21
  InputGroup,
@@ -25,8 +26,10 @@ import {
25
  HStack,
26
  } from '@chakra-ui/react';
27
  import axios from 'axios';
28
- import JSZip from 'jszip';
29
- import { saveAs } from 'file-saver';
 
 
30
 
31
  // Function to get filename without extension
32
  const getFilenameWithoutExtension = filename => {
@@ -340,13 +343,18 @@ const App = () => {
340
  setDownloading(true);
341
 
342
  try {
 
 
 
 
 
 
343
  // Create a new zip file
344
- const zip = new JSZip();
345
 
346
  // Add text files to the zip with the same filenames as the images but .txt extension
347
  completedImages.forEach(image => {
348
  const fileName = getFilenameWithoutExtension(image.name) + ".txt";
349
- // Use the edited caption if available, otherwise use the original caption
350
  const captionToSave = image.isEdited ? image.editedCaption : image.caption;
351
  zip.file(fileName, captionToSave);
352
  });
@@ -470,272 +478,278 @@ const App = () => {
470
  const hasCaptions = uploadedImages.some(img => img.status === 'completed' && img.caption);
471
 
472
  return (
473
- <Container maxW="container.xl" py={10}>
474
- <VStack spacing={8} align="stretch">
475
- <Box textAlign="center">
476
- <Heading as="h1" size="2xl" mb={2} bgGradient="linear(to-r, cyan.400, blue.500, purple.600)" bgClip="text">
477
- Glif LoRA Autocaption
478
- </Heading>
479
- <Text fontSize="lg" color="gray.400">
480
- Generate captions for your images using Glif API
481
- </Text>
482
- </Box>
483
-
484
- <Divider />
485
-
486
- <Box>
487
- <FormControl mb={4}>
488
- <FormLabel>API Key</FormLabel>
489
- <Flex>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  <Input
491
- type="password"
492
- placeholder="Enter your Glif API key"
493
- value={apiKey}
494
- onChange={handleApiKeyChange}
495
- mr={2}
496
  />
497
- <Button
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  size="md"
499
- onClick={clearApiKey}
500
- variant="outline"
501
- colorScheme="red"
502
  >
503
- Clear
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  </Button>
505
  </Flex>
506
- <Text fontSize="xs" color="gray.500" mt={1}>
507
- Your API key is securely saved in your browser for future use.
508
- </Text>
509
- </FormControl>
510
-
511
- <FormControl mb={4}>
512
- <FormLabel>Custom Glif ID or URL</FormLabel>
513
- <InputGroup>
514
- <Input
515
- type="text"
516
- placeholder="Enter custom Glif ID or URL (optional)"
517
- value={customGlifInput}
518
- onChange={handleCustomGlifInputChange}
519
  />
520
- <InputRightElement width="auto" mr={2}>
521
- <Button size="sm" onClick={saveCustomGlifId} mr={2}>
522
- Save
523
- </Button>
524
- <Button size="sm" onClick={clearCustomGlifId} variant="outline" colorScheme="red">
525
- Clear
526
- </Button>
527
- </InputRightElement>
528
- </InputGroup>
529
- <Text fontSize="xs" color="gray.500" mt={1}>
530
- Enter a custom Glif ID or URL to use for autocaption, follow the format here: https://glif.app/@saqib/glifs/cm7yya7850000la0ckalxpix2. Leave empty to use the default Glif.
531
- </Text>
532
- </FormControl>
533
-
534
- <FormControl mb={4}>
535
- <FormLabel>Trigger Word</FormLabel>
536
- <Input
537
- type="text"
538
- placeholder="Enter trigger word (optional)"
539
- value={triggerWord}
540
- onChange={handleTriggerWordChange}
541
- />
542
- <Text fontSize="xs" color="gray.500" mt={1}>
543
- Add a trigger word to all captions (e.g., "masterpiece", "best quality").
544
- </Text>
545
- </FormControl>
546
-
547
- <FormControl mb={4}>
548
- <FormLabel>Trigger Word Placement</FormLabel>
549
- <RadioGroup onChange={handleTriggerPlacementChange} value={triggerPlacement}>
550
- <HStack spacing={4}>
551
- <Radio value="none">No Trigger</Radio>
552
- <Radio value="before">Before Caption</Radio>
553
- <Radio value="after">After Caption</Radio>
554
- </HStack>
555
- </RadioGroup>
556
- <Text fontSize="xs" color="gray.500" mt={1}>
557
- Choose where to place the trigger word in relation to the generated caption.
558
- </Text>
559
- </FormControl>
560
-
561
- <Flex flexWrap="wrap" gap={4} mb={6} justify="center">
562
- <Button
563
- onClick={() => fileInputRef.current?.click()}
564
- variant="solid"
565
- size="md"
566
- disabled={processing}
567
- >
568
- Upload Images
569
- </Button>
570
- <input
571
- type="file"
572
- multiple
573
- accept="image/*"
574
- onChange={handleFileChange}
575
- style={{ display: 'none' }}
576
- ref={fileInputRef}
577
- />
578
-
579
- <Button
580
- onClick={processImages}
581
- colorScheme="blue"
582
- isLoading={processing}
583
- loadingText="Processing"
584
- disabled={processing || uploadedImages.filter(img => img.status === 'pending').length === 0}
585
- >
586
- Process Images
587
- </Button>
588
-
589
- <Button
590
- onClick={downloadCaptions}
591
- colorScheme="teal"
592
- isLoading={downloading}
593
- loadingText="Downloading"
594
- disabled={downloading || !hasCaptions}
595
- >
596
- Download Captions
597
- </Button>
598
-
599
- <Button
600
- onClick={clearAll}
601
- variant="outline"
602
- disabled={processing || uploadedImages.length === 0}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  >
604
- Clear All
605
- </Button>
606
- </Flex>
607
-
608
- {processing && (
609
- <Progress
610
- value={progress}
611
- size="sm"
612
- colorScheme="blue"
613
- hasStripe
614
- mb={4}
615
- borderRadius="md"
616
- />
617
  )}
618
- </Box>
619
-
620
- {uploadedImages.length > 0 ? (
621
- <SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={6}>
622
- {uploadedImages.map((image) => (
623
- <Box
624
- key={image.id}
625
- borderWidth="1px"
626
- borderRadius="lg"
627
- overflow="hidden"
628
- bg="gray.900"
629
- position="relative"
630
- >
631
- <Box position="absolute" top={2} right={2} zIndex={1}>
632
- {getStatusBadge(image.status)}
633
- {image.isEdited && (
634
- <Badge ml={2} colorScheme="purple">Edited</Badge>
635
- )}
636
- </Box>
637
- <Image
638
- src={image.preview}
639
- alt={image.name}
640
- w="100%"
641
- h="200px"
642
- objectFit="cover"
643
- />
644
- <Box p={4}>
645
- <Flex justify="space-between" align="center" mb={2}>
646
- <Text fontWeight="semibold" isTruncated maxW="70%">{image.name}</Text>
647
- <Button
648
- size="xs"
649
- onClick={() => removeImage(image.id)}
650
- disabled={processing}
651
- >
652
- Remove
653
- </Button>
654
- </Flex>
655
-
656
- {image.status === 'completed' && (
657
- <>
658
- {editingImageId === image.id ? (
659
- <Box mt={2}>
660
- <Textarea
661
- value={image.editedCaption}
662
- onChange={(e) => handleEditCaptionChange(image.id, e.target.value)}
663
- size="sm"
664
- mb={2}
665
- />
666
- <Flex justify="flex-end" gap={2}>
667
- <Button size="xs" onClick={cancelEditingCaption}>
668
- Cancel
669
- </Button>
670
- <Button
671
- size="xs"
672
- colorScheme="blue"
673
- onClick={() => saveEditedCaption(image.id, image.editedCaption)}
674
- >
675
- Save
676
- </Button>
677
- </Flex>
678
- </Box>
679
- ) : (
680
- <Box mt={2} p={2} bg="gray.800" borderRadius="md">
681
- <Text fontSize="sm">
682
- {image.isEdited ? image.editedCaption : image.caption}
683
- </Text>
684
- <Flex justify="flex-end" mt={2} gap={2}>
685
- {image.isEdited && (
686
- <Tooltip label="Reset to original caption">
687
- <Button
688
- size="xs"
689
- colorScheme="orange"
690
- onClick={() => resetCaption(image.id)}
691
- >
692
- Reset
693
- </Button>
694
- </Tooltip>
695
- )}
696
- <Button
697
- size="xs"
698
- colorScheme="teal"
699
- onClick={() => startEditingCaption(image.id)}
700
- >
701
- Edit
702
- </Button>
703
- </Flex>
704
- </Box>
705
- )}
706
- </>
707
- )}
708
-
709
- {image.status === 'error' && (
710
- <Box mt={2} p={2} bg="red.900" borderRadius="md">
711
- <Text fontSize="sm" color="red.200">{image.error}</Text>
712
- </Box>
713
- )}
714
- </Box>
715
- </Box>
716
- ))}
717
- </SimpleGrid>
718
- ) : (
719
- <Box
720
- textAlign="center"
721
- p={10}
722
- borderWidth="1px"
723
- borderRadius="lg"
724
- borderStyle="dashed"
725
- >
726
- <Text color="gray.500">Upload images to get started</Text>
727
  </Box>
728
- )}
729
-
730
- <Divider mt={6} />
731
-
732
- <Box as="footer" textAlign="center">
733
- <Text fontSize="sm" color="gray.500">
734
- Glif LoRA Autocaption • {new Date().getFullYear()}
735
- </Text>
736
- </Box>
737
- </VStack>
738
- </Container>
739
  );
740
  };
741
 
 
1
+ import React, { useState, useRef, useEffect, lazy, Suspense } from 'react';
2
  import {
3
  Box,
4
  Container,
 
16
  Badge,
17
  Flex,
18
  Divider,
19
+ Spinner,
20
  Textarea,
21
  Tooltip,
22
  InputGroup,
 
26
  HStack,
27
  } from '@chakra-ui/react';
28
  import axios from 'axios';
29
+
30
+ // Lazy load heavier components that aren't needed immediately
31
+ const JSZip = lazy(() => import('jszip'));
32
+ const { saveAs } = lazy(() => import('file-saver'));
33
 
34
  // Function to get filename without extension
35
  const getFilenameWithoutExtension = filename => {
 
343
  setDownloading(true);
344
 
345
  try {
346
+ // Dynamically import JSZip and file-saver only when needed
347
+ const [JSZip, { saveAs }] = await Promise.all([
348
+ import('jszip'),
349
+ import('file-saver')
350
+ ]);
351
+
352
  // Create a new zip file
353
+ const zip = new JSZip.default();
354
 
355
  // Add text files to the zip with the same filenames as the images but .txt extension
356
  completedImages.forEach(image => {
357
  const fileName = getFilenameWithoutExtension(image.name) + ".txt";
 
358
  const captionToSave = image.isEdited ? image.editedCaption : image.caption;
359
  zip.file(fileName, captionToSave);
360
  });
 
478
  const hasCaptions = uploadedImages.some(img => img.status === 'completed' && img.caption);
479
 
480
  return (
481
+ <Suspense fallback={
482
+ <Box display="flex" justifyContent="center" alignItems="center" height="100vh">
483
+ <Spinner size="xl" />
484
+ </Box>
485
+ }>
486
+ <Container maxW="container.xl" py={10}>
487
+ <VStack spacing={8} align="stretch">
488
+ <Box textAlign="center">
489
+ <Heading as="h1" size="2xl" mb={2} bgGradient="linear(to-r, cyan.400, blue.500, purple.600)" bgClip="text">
490
+ Glif LoRA Autocaption
491
+ </Heading>
492
+ <Text fontSize="lg" color="gray.400">
493
+ Generate captions for your images using Glif API
494
+ </Text>
495
+ </Box>
496
+
497
+ <Divider />
498
+
499
+ <Box>
500
+ <FormControl mb={4}>
501
+ <FormLabel>API Key</FormLabel>
502
+ <Flex>
503
+ <Input
504
+ type="password"
505
+ placeholder="Enter your Glif API key"
506
+ value={apiKey}
507
+ onChange={handleApiKeyChange}
508
+ mr={2}
509
+ />
510
+ <Button
511
+ size="md"
512
+ onClick={clearApiKey}
513
+ variant="outline"
514
+ colorScheme="red"
515
+ >
516
+ Clear
517
+ </Button>
518
+ </Flex>
519
+ <Text fontSize="xs" color="gray.500" mt={1}>
520
+ Your API key is securely saved in your browser for future use.
521
+ </Text>
522
+ </FormControl>
523
+
524
+ <FormControl mb={4}>
525
+ <FormLabel>Custom Glif ID or URL</FormLabel>
526
+ <InputGroup>
527
+ <Input
528
+ type="text"
529
+ placeholder="Enter custom Glif ID or URL (optional)"
530
+ value={customGlifInput}
531
+ onChange={handleCustomGlifInputChange}
532
+ />
533
+ <InputRightElement width="auto" mr={2}>
534
+ <Button size="sm" onClick={saveCustomGlifId} mr={2}>
535
+ Save
536
+ </Button>
537
+ <Button size="sm" onClick={clearCustomGlifId} variant="outline" colorScheme="red">
538
+ Clear
539
+ </Button>
540
+ </InputRightElement>
541
+ </InputGroup>
542
+ <Text fontSize="xs" color="gray.500" mt={1}>
543
+ Enter a custom Glif ID or URL to use for autocaption, follow the format here: https://glif.app/@saqib/glifs/cm7yya7850000la0ckalxpix2. Leave empty to use the default Glif.
544
+ </Text>
545
+ </FormControl>
546
+
547
+ <FormControl mb={4}>
548
+ <FormLabel>Trigger Word</FormLabel>
549
  <Input
550
+ type="text"
551
+ placeholder="Enter trigger word (optional)"
552
+ value={triggerWord}
553
+ onChange={handleTriggerWordChange}
 
554
  />
555
+ <Text fontSize="xs" color="gray.500" mt={1}>
556
+ Add a trigger word to all captions (e.g., "masterpiece", "best quality").
557
+ </Text>
558
+ </FormControl>
559
+
560
+ <FormControl mb={4}>
561
+ <FormLabel>Trigger Word Placement</FormLabel>
562
+ <RadioGroup onChange={handleTriggerPlacementChange} value={triggerPlacement}>
563
+ <HStack spacing={4}>
564
+ <Radio value="none">No Trigger</Radio>
565
+ <Radio value="before">Before Caption</Radio>
566
+ <Radio value="after">After Caption</Radio>
567
+ </HStack>
568
+ </RadioGroup>
569
+ <Text fontSize="xs" color="gray.500" mt={1}>
570
+ Choose where to place the trigger word in relation to the generated caption.
571
+ </Text>
572
+ </FormControl>
573
+
574
+ <Flex flexWrap="wrap" gap={4} mb={6} justify="center">
575
+ <Button
576
+ onClick={() => fileInputRef.current?.click()}
577
+ variant="solid"
578
  size="md"
579
+ disabled={processing}
 
 
580
  >
581
+ Upload Images
582
+ </Button>
583
+ <input
584
+ type="file"
585
+ multiple
586
+ accept="image/*"
587
+ onChange={handleFileChange}
588
+ style={{ display: 'none' }}
589
+ ref={fileInputRef}
590
+ />
591
+
592
+ <Button
593
+ onClick={processImages}
594
+ colorScheme="blue"
595
+ isLoading={processing}
596
+ loadingText="Processing"
597
+ disabled={processing || uploadedImages.filter(img => img.status === 'pending').length === 0}
598
+ >
599
+ Process Images
600
+ </Button>
601
+
602
+ <Button
603
+ onClick={downloadCaptions}
604
+ colorScheme="teal"
605
+ isLoading={downloading}
606
+ loadingText="Downloading"
607
+ disabled={downloading || !hasCaptions}
608
+ >
609
+ Download Captions
610
+ </Button>
611
+
612
+ <Button
613
+ onClick={clearAll}
614
+ variant="outline"
615
+ disabled={processing || uploadedImages.length === 0}
616
+ >
617
+ Clear All
618
  </Button>
619
  </Flex>
620
+
621
+ {processing && (
622
+ <Progress
623
+ value={progress}
624
+ size="sm"
625
+ colorScheme="blue"
626
+ hasStripe
627
+ mb={4}
628
+ borderRadius="md"
 
 
 
 
629
  />
630
+ )}
631
+ </Box>
632
+
633
+ {uploadedImages.length > 0 ? (
634
+ <SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={6}>
635
+ {uploadedImages.map((image) => (
636
+ <Box
637
+ key={image.id}
638
+ borderWidth="1px"
639
+ borderRadius="lg"
640
+ overflow="hidden"
641
+ bg="gray.900"
642
+ position="relative"
643
+ >
644
+ <Box position="absolute" top={2} right={2} zIndex={1}>
645
+ {getStatusBadge(image.status)}
646
+ {image.isEdited && (
647
+ <Badge ml={2} colorScheme="purple">Edited</Badge>
648
+ )}
649
+ </Box>
650
+ <Image
651
+ src={image.preview}
652
+ alt={image.name}
653
+ w="100%"
654
+ h="200px"
655
+ objectFit="cover"
656
+ />
657
+ <Box p={4}>
658
+ <Flex justify="space-between" align="center" mb={2}>
659
+ <Text fontWeight="semibold" isTruncated maxW="70%">{image.name}</Text>
660
+ <Button
661
+ size="xs"
662
+ onClick={() => removeImage(image.id)}
663
+ disabled={processing}
664
+ >
665
+ Remove
666
+ </Button>
667
+ </Flex>
668
+
669
+ {image.status === 'completed' && (
670
+ <>
671
+ {editingImageId === image.id ? (
672
+ <Box mt={2}>
673
+ <Textarea
674
+ value={image.editedCaption}
675
+ onChange={(e) => handleEditCaptionChange(image.id, e.target.value)}
676
+ size="sm"
677
+ mb={2}
678
+ />
679
+ <Flex justify="flex-end" gap={2}>
680
+ <Button size="xs" onClick={cancelEditingCaption}>
681
+ Cancel
682
+ </Button>
683
+ <Button
684
+ size="xs"
685
+ colorScheme="blue"
686
+ onClick={() => saveEditedCaption(image.id, image.editedCaption)}
687
+ >
688
+ Save
689
+ </Button>
690
+ </Flex>
691
+ </Box>
692
+ ) : (
693
+ <Box mt={2} p={2} bg="gray.800" borderRadius="md">
694
+ <Text fontSize="sm">
695
+ {image.isEdited ? image.editedCaption : image.caption}
696
+ </Text>
697
+ <Flex justify="flex-end" mt={2} gap={2}>
698
+ {image.isEdited && (
699
+ <Tooltip label="Reset to original caption">
700
+ <Button
701
+ size="xs"
702
+ colorScheme="orange"
703
+ onClick={() => resetCaption(image.id)}
704
+ >
705
+ Reset
706
+ </Button>
707
+ </Tooltip>
708
+ )}
709
+ <Button
710
+ size="xs"
711
+ colorScheme="teal"
712
+ onClick={() => startEditingCaption(image.id)}
713
+ >
714
+ Edit
715
+ </Button>
716
+ </Flex>
717
+ </Box>
718
+ )}
719
+ </>
720
+ )}
721
+
722
+ {image.status === 'error' && (
723
+ <Box mt={2} p={2} bg="red.900" borderRadius="md">
724
+ <Text fontSize="sm" color="red.200">{image.error}</Text>
725
+ </Box>
726
+ )}
727
+ </Box>
728
+ </Box>
729
+ ))}
730
+ </SimpleGrid>
731
+ ) : (
732
+ <Box
733
+ textAlign="center"
734
+ p={10}
735
+ borderWidth="1px"
736
+ borderRadius="lg"
737
+ borderStyle="dashed"
738
  >
739
+ <Text color="gray.500">Upload images to get started</Text>
740
+ </Box>
 
 
 
 
 
 
 
 
 
 
 
741
  )}
742
+
743
+ <Divider mt={6} />
744
+
745
+ <Box as="footer" textAlign="center">
746
+ <Text fontSize="sm" color="gray.500">
747
+ Glif LoRA Autocaption • {new Date().getFullYear()}
748
+ </Text>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  </Box>
750
+ </VStack>
751
+ </Container>
752
+ </Suspense>
 
 
 
 
 
 
 
 
753
  );
754
  };
755
 
vite.config.js CHANGED
@@ -4,8 +4,22 @@ import react from '@vitejs/plugin-react';
4
  export default defineConfig({
5
  plugins: [react()],
6
  server: {
7
- port: 7860, // Default port for HuggingFace Spaces
8
  host: '0.0.0.0',
9
  allowedHosts: ["saq1b-glif-lora-autocaption.hf.space"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
  });
 
4
  export default defineConfig({
5
  plugins: [react()],
6
  server: {
7
+ port: 7860,
8
  host: '0.0.0.0',
9
  allowedHosts: ["saq1b-glif-lora-autocaption.hf.space"]
10
+ },
11
+ build: {
12
+ rollupOptions: {
13
+ output: {
14
+ manualChunks: {
15
+ 'chakra': ['@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion'],
16
+ 'utils': ['jszip', 'file-saver', 'axios']
17
+ }
18
+ }
19
+ },
20
+ chunkSizeWarningLimit: 1000
21
+ },
22
+ optimizeDeps: {
23
+ include: ['@chakra-ui/react', '@emotion/react', '@emotion/styled', 'framer-motion']
24
  }
25
  });