im exporting all anotaion with image and i can import them again like project now i want export labels too like project to load them again with all annotatoins this is my code "<template>
  <v-app>
    <v-container fluid>
      <v-row justify="center" class="py-5">
        <!-- Sidebar for Images and File Inputs -->
        <v-col cols="3">
          <!-- Image Upload with Tooltip -->
          <v-file-input label="Upload Images" accept="image/*" outlined @change="onImageUpload" multiple
            prepend-icon="mdi-upload" hint="Select multiple images for annotation" persistent-hint></v-file-input>

          <!-- List of Images with better spacing and highlighting -->
          <!-- Style modified for scrollable list -->
          <v-list class="mt-5" tile style="max-height: 650px; overflow-y: auto;">
            <v-subheader class="font-weight-medium">Images</v-subheader>
            <v-list-item v-for="(img, index) in images" :key="index" @click="selectImage(index)"
              :class="{ 'active': selectedImageIndex === index, 'v-list-item--active': selectedImageIndex === index }"
              style="cursor: pointer;" :aria-label="'Select image ' + (index + 1)">
              <v-list-item-content>
                <v-list-item-title>Image {{ index + 1 }} : {{ img.rectangles.length }}</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
        </v-col>

        <!-- Main Content for Canvas and Tools -->
        <v-col cols="9">
          <v-row>
            <v-col cols="8">
              <!-- Export Button positioned logically -->
              <v-btn color="primary" elevation="2" dark @click="exportToJson" class="mb-3"
                aria-label="Export annotated data with images">
                Export Annotations
              </v-btn>

              <!-- Canvas for Drawing -->
              <v-card elevation="1">
                <v-card-text class="pa-4">
                  <canvas ref="canvas" :width="canvasWidth" :height="canvasHeight" @wheel.prevent="onMouseWheel"
                    @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onMouseMove"
                    style="border: 1px solid #ddd;"></canvas>
                </v-card-text>
              </v-card>

              <!-- Labels with descriptive badge -->
              <v-chip-group v-if="labels.length" mandatory class="mt-3">
                <v-chip v-for="(label, index) in labels" :key="label.name" @click="selectLabel(label)"
                  :color="selectedLabel === label.name ? label.color : ''" class="ma-2" style="border-radius: 12px;">
                  <v-badge :color="label.color" :content="index + 1" inline></v-badge>
                  <v-label class="ml-2">{{ label.name }}</v-label>
                </v-chip>
              </v-chip-group>
            </v-col>

            <!-- Rectangle List and Import -->
            <v-col cols="4">
              <!-- Import JSON with feedback -->
              <v-file-input label="Import Annotated Data" accept=".json" outlined @change="importJsonData"
                prepend-icon="mdi-folder-open" hint="Import existing annotations in JSON format"
                persistent-hint></v-file-input>

              <!-- Rectangle List with emphasis on active selection -->
              <v-list v-if="rectangles.length" class="mt-5" tile>
                <v-subheader class="font-weight-medium">Rectangles</v-subheader>
                <v-list-item v-for="(rect, index) in rectangles" :key="index"
                  @click="toggleRectangleSelection(index, $event)"
                  :class="{ 'active': selectedRectangles.includes(rect), 'v-list-item--active': selectedRectangles.includes(rect) }"
                  style="cursor: pointer;">
                  <template v-slot:prepend>
                    <v-icon :style="{ color: rect.color }">mdi-circle</v-icon>
                  </template>
                  <template v-slot:append>
                    <v-btn icon @click.stop="removeRectangle(index)" color="red" variant="tonal" density="comfortable"
                      aria-label="Remove rectangle">
                      <v-icon>mdi-delete</v-icon>
                    </v-btn>
                  </template>
                  <v-list-item-content>
                    <v-list-item-title>{{ rect.label }}</v-list-item-title>
                    <v-list-item-subtitle>
                      [x: {{ rect.x.toFixed(3) }}, y: {{ rect.y.toFixed(3) }}, w: {{ rect.width.toFixed(3) }}, h:
                      {{ rect.height.toFixed(3) }}]
                    </v-list-item-subtitle>
                  </v-list-item-content>
                </v-list-item>
              </v-list>
            </v-col>
          </v-row>

          <!-- Export to ZIP Button -->
          <v-btn color="success" elevation="2" dark @click="exportToZip" class="my-3"
            aria-label="Export annotations and images as ZIP">
            Export Annotations & ZIP
          </v-btn>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>
<script>
export default {
  data() {
    return {
      canvasWidth: 800,
      canvasHeight: 600,
      context: null,
      scale: 1,
      minScale: 0.1,
      maxScale: 12,
      scaleStep: 0.1,
      originX: 0,
      originY: 0,
      images: [],
      selectedImageIndex: null,
      rectangles: [],
      labels: [
        { id: 0, name: "Crack", color: "#2200ff", key: "Digit1" },
        { id: 1, name: "Dead", color: "#ff0000", key: "Digit2" },
        { id: 2, name: "FCold", color: "#ffdd00", key: "Digit3" },
        { id: 2, name: "Scratch", color: "#aaaa00", key: "Digit4" }
      ],
      selectedLabel: "Person",
      selectedRectangles: [],
      drawStartX: 0,
      drawStartY: 0,
      drawing: false,
      isDraggingImage: false,
      isDraggingRectangle: false,
      isResizing: false,
      selectedResizeHandle: null,
      resizeHandleSize: 8,
      lastMouseX: 0,
      lastMouseY: 0,
    };
  },
  mounted() {
    this.context = this.$refs.canvas.getContext('2d');
    window.addEventListener('keydown', this.handleKeyDown);
  },
  beforeDestroy() {
    window.removeEventListener('keydown', this.handleKeyDown);
  },
  methods: {
    exportToZip() {
      if (this.images.length === 0) {
        alert("No images available for export");
        return;
      }

      const zip = new JSZip();

      this.images.forEach(image => {
        const yoloData = image.rectangles.map(rect => {
          const xCenter = rect.x + rect.width / 2;
          const yCenter = rect.y + rect.height / 2;
          return `${rect.labelid} ${xCenter} ${yCenter} ${rect.width} ${rect.height}`;
        }).join('\n');

        // Add image and annotations to the zip
        zip.file(`train/labels/${image.name.split('.')[0]}.txt`, yoloData);
        const imgBlob = this.dataURLToBlob(image.src);
        zip.file('train/images/' + image.name, imgBlob);
      });

      // Generate the zip file and prompt the user to download it
      zip.generateAsync({ type: 'blob' }).then(content => {
        const url = URL.createObjectURL(content);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'annotations.zip';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
      });
    },

    // Utility function to convert data URL to Blob
    dataURLToBlob(dataURL) {
      const byteString = atob(dataURL.split(',')[1]);
      const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
      const ab = new ArrayBuffer(byteString.length);
      const ia = new Uint8Array(ab);
      for (let i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
      }
      return new Blob([ab], { type: mimeString });
    },
    handleKeyDown(event) {
      const ctrlOrCmd = event.ctrlKey || event.metaKey; // for Mac support

      if (ctrlOrCmd && event.code === 'KeyC') {
        this.copyRectangle();
      }
      if (ctrlOrCmd && event.code === 'KeyV') {
        this.pasteRectangle();
      }

      const label = this.labels.find(label => label.key === event.code);
      if (label) {
        this.selectLabel(label);
      }
    },
    onImageUpload(event) {
      const files = event.target.files;
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        const reader = new FileReader();
        reader.onload = (e) => {
          this.images.push({
            name: file.name, // Capture file name
            src: e.target.result,
            rectangles: []
          });
          if (this.selectedImageIndex === null) {
            this.selectImage(0);
          }
        };
        reader.readAsDataURL(file);
      }
    },
    selectImage(index) {
      this.selectedImageIndex = index;
      this.rectangles = this.images[index].rectangles;
      this.loadImage(this.images[index].src);
    },
    loadImage(src) {
      const img = new Image();
      img.onload = () => {
        const widthScale = this.canvasWidth / img.width;
        const heightScale = this.canvasHeight / img.height;
        this.scale = Math.min(widthScale, heightScale);

        const width = img.width * this.scale;
        const height = img.height * this.scale;
        this.originX = (this.canvasWidth - width) / 2;
        this.originY = (this.canvasHeight - height) / 2;

        this.currentImage = img;
        this.draw();
      };
      img.src = src;
    },
    draw() {
      if (!this.currentImage) return;

      this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
      const width = this.currentImage.width * this.scale;
      const height = this.currentImage.height * this.scale;
      this.context.drawImage(this.currentImage, this.originX, this.originY, width, height);

      this.redrawRectangles();

      if (this.drawing) {
        this.context.strokeStyle = 'green';
        const drawWidth = this.currentMouseX - this.drawStartX;
        const drawHeight = this.currentMouseY - this.drawStartY;
        this.context.strokeRect(this.drawStartX, this.drawStartY, drawWidth, drawHeight);
      }
    },
    redrawRectangles() {
      if (!this.currentImage) return;
      const width = this.currentImage.width * this.scale;
      const height = this.currentImage.height * this.scale;

      this.rectangles.forEach((rect) => {
        const rectX = this.originX + rect.x * width;
        const rectY = this.originY + rect.y * height;
        const rectW = rect.width * width;
        const rectH = rect.height * height;

        this.context.strokeStyle = rect.color;
        this.context.lineWidth = 1;
        this.context.fillStyle = rect.color + "44";

        this.context.fillRect(rectX, rectY, rectW, rectH);

        // Highlight selected rectangles
        if (this.selectedRectangles.includes(rect)) {
          this.context.strokeStyle = 'red';
          this.context.lineWidth = 2;
          this.drawResizeHandles(rectX, rectY, rectW, rectH);
        }

        this.context.strokeRect(rectX, rectY, rectW, rectH);
      });
    },
    drawResizeHandles(rectX, rectY, rectW, rectH) {
      const handleSize = this.resizeHandleSize;
      const halfSize = handleSize / 2;
      this.context.fillRect(rectX - halfSize, rectY - halfSize, handleSize, handleSize); // Top-left
      this.context.fillRect(rectX + rectW - halfSize, rectY - halfSize, handleSize, handleSize); // Top-right
      this.context.fillRect(rectX - halfSize, rectY + rectH - halfSize, handleSize, handleSize); // Bottom-left
      this.context.fillRect(rectX + rectW - halfSize, rectY + rectH - halfSize, handleSize, handleSize); // Bottom-right
    },
    onMouseWheel(event) {
      const { offsetX, offsetY } = event;
      const oldScale = this.scale;
      const newScale = event.deltaY > 0
        ? Math.max(this.scale - this.scaleStep, this.minScale)
        : Math.min(this.scale + this.scaleStep, this.maxScale);

      if (newScale !== oldScale) {
        const scaleRatio = newScale / oldScale;
        this.originX = offsetX - (offsetX - this.originX) * scaleRatio;
        this.originY = offsetY - (offsetY - this.originY) * scaleRatio;
        this.scale = newScale;
        this.draw();
      }
    },
    onMouseDown(event) {
      const mouseX = event.offsetX;
      const mouseY = event.offsetY;

      if (event.button === 1) { // Middle mouse button for dragging
        this.isDraggingImage = true;
        this.lastMouseX = mouseX;
        this.lastMouseY = mouseY;
      }
      else if (event.button === 0) { // Left mouse button for actions
        this.selectedResizeHandle = this.getResizeHandle(mouseX, mouseY);
        if (this.selectedResizeHandle) {
          this.isResizing = true;
          this.lastMouseX = mouseX;
          this.lastMouseY = mouseY;
          return;
        }

        const selectedRectangle = this.rectangles.find(rect =>
          mouseX >= this.originX + rect.x * this.currentImage.width * this.scale &&
          mouseX <= this.originX + (rect.x + rect.width) * this.currentImage.width * this.scale &&
          mouseY >= this.originY + rect.y * this.currentImage.height * this.scale &&
          mouseY <= this.originY + (rect.y + rect.height) * this.currentImage.height * this.scale
        );

        if (selectedRectangle) {
          this.isDraggingRectangle = true;
          this.lastMouseX = mouseX;
          this.lastMouseY = mouseY;
          this.toggleRectangleSelection(this.rectangles.indexOf(selectedRectangle), event);
        } else {
          this.drawing = true;
          this.drawStartX = mouseX;
          this.drawStartY = mouseY;
          this.currentMouseX = mouseX;
          this.currentMouseY = mouseY;
          this.selectedRectangles = []; // Clear current selection if drawing a new rectangle
        }
      }
      this.draw();
    },
    onMouseMove(event) {
      this.currentMouseX = event.offsetX;
      this.currentMouseY = event.offsetY;

      if (this.isDraggingImage) {
        const deltaX = this.currentMouseX - this.lastMouseX;
        const deltaY = this.currentMouseY - this.lastMouseY;
        this.originX += deltaX;
        this.originY += deltaY;
        this.lastMouseX = this.currentMouseX;
        this.lastMouseY = this.currentMouseY;
        this.draw();
      } else if (this.isDraggingRectangle && this.selectedRectangles.length) {
        const deltaX = (this.currentMouseX - this.lastMouseX) / (this.currentImage.width * this.scale);
        const deltaY = (this.currentMouseY - this.lastMouseY) / (this.currentImage.height * this.scale);
        this.selectedRectangles.forEach(rect => {
          rect.x += deltaX;
          rect.y += deltaY;
        });
        this.lastMouseX = this.currentMouseX;
        this.lastMouseY = this.currentMouseY;
        this.draw();
      } else if (this.isResizing && this.selectedRectangles.length === 1) {
        const deltaX = this.currentMouseX - this.lastMouseX;
        const deltaY = this.currentMouseY - this.lastMouseY;
        this.resizeRectangle(deltaX, deltaY);
        this.lastMouseX = this.currentMouseX;
        this.lastMouseY = this.currentMouseY;
        this.draw();
      } else if (this.drawing) {
        this.draw();
      }
    },
    onMouseUp(event) {
      if (event.button === 1) {
        this.isDraggingImage = false;
      }
      if (event.button === 0) {
        if (this.drawing) {
          const mouseX = this.currentMouseX;
          const mouseY = this.currentMouseY;

          if (Math.abs(mouseX - this.drawStartX) > 0 && Math.abs(mouseY - this.drawStartY) > 0) {
            this.addRectangle(mouseX, mouseY);
          }
          this.drawing = false;
        }
        this.isDraggingRectangle = false;
        this.isResizing = false;
        this.selectedResizeHandle = null;
        this.draw();
      }
    },
    addRectangle(mouseX, mouseY) {
      const selectedLabelInfo = this.labels.find(
        (label) => label.name === this.selectedLabel
      );

      const rect = {
        x: (Math.min(this.drawStartX, mouseX) - this.originX) / (this.currentImage.width * this.scale),
        y: (Math.min(this.drawStartY, mouseY) - this.originY) / (this.currentImage.height * this.scale),
        width: Math.abs(mouseX - this.drawStartX) / (this.currentImage.width * this.scale),
        height: Math.abs(mouseY - this.drawStartY) / (this.currentImage.height * this.scale),
        labelid: selectedLabelInfo.id,
        label: selectedLabelInfo.name,
        color: selectedLabelInfo.color,
      };
      this.images[this.selectedImageIndex].rectangles.push(rect);
      this.selectedRectangles = [rect]; // Select the newly added rectangle
    },
    resizeRectangle(deltaX, deltaY) {
      if (this.selectedRectangles.length !== 1) return; // Only allow resizing of a single rectangle
      const rect = this.selectedRectangles[0];
      switch (this.selectedResizeHandle) {
        case 'top-left':
          rect.x += deltaX / (this.currentImage.width * this.scale);
          rect.y += deltaY / (this.currentImage.height * this.scale);
          rect.width -= deltaX / (this.currentImage.width * this.scale);
          rect.height -= deltaY / (this.currentImage.height * this.scale);
          break;
        case 'top-right':
          rect.y += deltaY / (this.currentImage.height * this.scale);
          rect.width += deltaX / (this.currentImage.width * this.scale);
          rect.height -= deltaY / (this.currentImage.height * this.scale);
          break;
        case 'bottom-left':
          rect.x += deltaX / (this.currentImage.width * this.scale);
          rect.width -= deltaX / (this.currentImage.width * this.scale);
          rect.height += deltaY / (this.currentImage.height * this.scale);
          break;
        case 'bottom-right':
          rect.width += deltaX / (this.currentImage.width * this.scale);
          rect.height += deltaY / (this.currentImage.height * this.scale);
          break;
      }
      this.draw();
    },
    getResizeHandle(mouseX, mouseY) {
      if (this.selectedRectangles.length !== 1) return null; // Only handle resize for one selection
      const rect = this.selectedRectangles[0];
      const width = this.currentImage.width * this.scale;
      const height = this.currentImage.height * this.scale;
      const rectX = this.originX + rect.x * width;
      const rectY = this.originY + rect.y * height;
      const rectW = rect.width * width;
      const rectH = rect.height * height;
      const handleSize = this.resizeHandleSize / this.scale;

      if (this.isWithinBounds(mouseX, mouseY, rectX, rectY, handleSize)) return 'top-left';
      if (this.isWithinBounds(mouseX, mouseY, rectX + rectW, rectY, handleSize)) return 'top-right';
      if (this.isWithinBounds(mouseX, mouseY, rectX, rectY + rectH, handleSize)) return 'bottom-left';
      if (this.isWithinBounds(mouseX, mouseY, rectX + rectW, rectY + rectH, handleSize)) return 'bottom-right';

      return null;
    },
    isWithinBounds(mouseX, mouseY, targetX, targetY, size) {
      return (
        mouseX >= targetX - size / 2 &&
        mouseX <= targetX + size / 2 &&
        mouseY >= targetY - size / 2 &&
        mouseY <= targetY + size / 2
      );
    },
    toggleRectangleSelection(index, event) {
      const rect = this.rectangles[index];
      const isSelected = this.selectedRectangles.includes(rect);

      if (event.ctrlKey || event.metaKey) {
        if (isSelected) {
          this.selectedRectangles = this.selectedRectangles.filter(r => r !== rect);
        } else {
          this.selectedRectangles.push(rect);
        }
      } else {
        this.selectedRectangles = [rect];
      }
      this.draw();
    },
    selectLabel(label) {
      this.selectedLabel = label.name;
      if (this.selectedRectangles.length) {
        this.selectedRectangles.forEach(rect => {
          rect.label = label.name;
          rect.color = label.color;
          rect.labelid = label.id;
        });
        this.draw();
      }
    },
    removeRectangle(index) {
      this.rectangles.splice(index, 1);
      this.selectedRectangles = this.selectedRectangles.filter(rect => this.rectangles.includes(rect));
      this.draw();
    },

    copyRectangle() {
      if (this.selectedRectangles.length) {
        this.copiedRectangle = this.selectedRectangles.map(rect => ({ ...rect }));
      }
    },
    pasteRectangle() {
      if (this.copiedRectangle) {
        this.copiedRectangle.forEach(copied => {
          const newRect = {
            ...copied,
            x: copied.x + 0.05,
            y: copied.y + 0.05,
          };
          this.images[this.selectedImageIndex].rectangles.push(newRect);
          this.selectedRectangles = [newRect];
        });
        this.draw();
      }
    },
    exportToJson() {
      const dataToExport = this.images.map((image, index) => ({
        imageIndex: index + 1,
        imageName: image.name,
        imageSrc: image.src,
        rectangles: image.rectangles.map(rect => ({
          x: rect.x,
          y: rect.y,
          width: rect.width,
          height: rect.height,
          label: rect.label,
          labelid: rect.labelid
        }))
      }));

      const jsonData = JSON.stringify(dataToExport, null, 2);
      const blob = new Blob([jsonData], { type: 'application/json' });
      const url = URL.createObjectURL(blob);

      const a = document.createElement('a');
      a.href = url;
      a.download = 'annotated-data-with-images.json';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    },
    importJsonData(event) {
      const file = event.target.files[0];
      if (!file) {
        alert('No file selected');
        return;
      }

      const reader = new FileReader();
      reader.onload = (e) => {
        try {
          const data = JSON.parse(e.target.result);

          if (!Array.isArray(data) || !data.every(item =>
            item.imageName && item.imageSrc && Array.isArray(item.rectangles)
          )) {
            throw new Error('Invalid data structure');
          }

          this.images = data.map(item => ({
            name: item.imageName,
            src: item.imageSrc,
            rectangles: item.rectangles.map(rect => ({
              x: rect.x,
              y: rect.y,
              width: rect.width,
              height: rect.height,
              label: rect.label,
              labelid: rect.labelid,
              color: this.getLabelColorById(rect.labelid)
            }))
          }));

          if (this.images.length > 0) {
            this.selectImage(0);
          }

          alert('Data imported successfully');
        } catch (error) {
          console.error('Error parsing JSON:', error);
          alert('Failed to import data. Please check the console for more details.');
        }
      };

      reader.readAsText(file);
    },
    getLabelColorById(labelid) {
      const label = this.labels.find(label => label.id === labelid);
      return label ? label.color : '#000000';
    }
  }
};
</script>

<style scoped>
canvas {
  display: block;
  cursor: crosshair;
  margin-bottom: 16px;
}

.active {
  background-color: rgba(0, 0, 255, 0.1);
}

.active-label {
  background-color: rgba(0, 128, 0, 0.1);
}
</style>"