Can npm workspaces sequence the build of monorepo projects?

I’m trying to figure out how to build multiple typescript projects that have common dependencies.

I need to build them so that common dependencies are built before the projects that depend on them are built and after each build I need to install the project to Verdaccio, which is like an local NPM.

And I was looking at Bazel, but it’s a little “Heavy”. Can NPM workspaces figure out the right build sequence bazed on package.json dependencies?

Convert VBA to Google App Script vLookupFormat

I´ve been using a VBA code for a few years thats works great for me. Basically it keeps the format of the source cell of a vLookup search. My company migrate to Google Sheets and I can´t make it to work, The original VBA that I founded years ago was the following;

Module 1

Public IntClr, IntShade, FntClr, FntShade, CellAdd
Function LookupFormat(ByRef FndValue, ByRef LookupRng As Range, ByRef ColRef As Long)
    Set FindCell = LookupRng.Find(What:=FndValue, LookIn:=xlValues)
    CellAdd = Application.Caller.Address
    With FindCell
        FindRow = .Row
        FindCol = .Column
        With .Offset(0, ColRef - 1)
            With .Interior
                IntClr = .Color
                IntShade = .TintAndShade
            End With
            With .Font
                FntClr = .Color
                FntShade = .TintAndShade
            End With
            LookupFormat = .Value
        End With
    End With
End Function

WorkSheet Module

Sub Worksheet_Change(ByVal Target As Range)
    If CellAdd = "" Then Exit Sub
    RefChk = IntClr & IntShade & FntClr & FntShade
    If RefChk = "" Then Exit Sub
    With Range(CellAdd)
        With .Interior
            .Color = IntClr
            .TintAndShade = IntShade
        End With
        With .Font
            .Color = FntClr
            .TintAndShade = FntShade
        End With
    End With
    IntClr = ""
    IntShade = ""
    FntClr = ""
    FntShade = ""
    CellAdd = ""
End Sub

Need to make this to work in Google Sheets please

Get Session Model from webpage using jquery

I am doing an MVC 6 App. I have a model like this

public class SearchModel 
{
    public string SSN { get; set; } = string.Empty;
    public string EmployeeNumber { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string FirstName { get; set; } = string.Empty;
}

And I have it stored in a Session called SearchData

I need to see the data stored from chtml page. I have this

 $(document).ready(function () {
     var app = '@HttpContextAccessor?.HttpContext?.Session?.GetString("SearchData")';
 });

But it returns all the data as a string.

How can I get specific field? like SSN or EmployerName? Do I have to parse it into Json Model?

Thanks

Get Session Model from webpage using jquey

I and doing and MVC 6 App. I have a model like this

public class SearchModel 
{
    public string SSN { get; set; } = string.Empty;
    public string EmployeeNumber { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string FirstName { get; set; } = string.Empty;
}

e
And I have it store in Session called SearchData

I need to see the data stored from chtml page. I have this

 $(document).ready(function () {
     var app = '@HttpContextAccessor?.HttpContext?.Session?.GetString("SearchData")';
 });

But it returns all the data as a string.

How can I get specific field? like SSN or EmployerName? Do I have to parse it into Json Model?

Thanks

Wildcard javascript url redirect

Can’t use htaccess, so have to rely on javascript.

Say the url is domain.com/page1/page2/page3/ and I want to redirect this to domain.com/page4/page3/ how would I do this? (and have this work for anything that has anything as page3 and has any subfolders after that as well).

Plus: Say the url is domain.com/page1/page2/page3/page5/183/ and I want to redirect this to domain.com/page4/page3/ how would I do this? (cutting off page5 plus anything on the url after that).

app.post inserting data error: Cannot Get using NodeJS

I am trying to insert data into the SQL Server database using NodeJS.

When I ran the service and opened the browser-> localhost:5000 and I

got the page:

Error: Cannot Get (404) in NodeJS.

Seeking help if I am missing something. The database table has only 2 columns

Here’s my code. Hoping for some assistance, will be highly appreciated

    const express = require('express');
    const sql = require('mssql');
    const cors = require('cors');
    const app = express();
    app.use(cors());
    app.use(express.json());

    const config = {
    user: 'sa',
    password: 'password',
    server: 'ServerName\MSSQL2019EX',
    database: 'Contacts',
    options: {
        encrypt: false,
        trustServerCertificate: false
      }
    };

    app.post('/', async (req, res) => {
    try {
        await sql.connect(config);
        const pool = new sql.ConnectionPool(config);
        await pool.connect();

        const transaction = new sql.Transaction(pool);
        await transaction.begin();

        try {
            for (const user of req.body) {
                await new sql.Request(transaction)
                    .input('name', sql.NVarChar, user.name)
                    .input('email', sql.NVarChar, user.email)
                    .query(`
                        INSERT INTO Users (Name, Email) 
                        VALUES (@name, @email)
                    `);
            }
            await transaction.commit();
            res.sendStatus(200);
        } catch (err) {
            await transaction.rollback();
            throw err;
        }
    } catch (error) {
        console.error('SQL error:', error);
        res.status(500).send('Database error');
    }
});

app.listen(5000, () => {
    console.log('Server running on port 5000');
});

Asking for suggestion of fixing the script.

swiper-slide: How to create hover &/or click interaction so when swiper-slide is clicked on the image pops up larger

I can’t seem to get only the swiper-slide that’s clicked on to pop up. Instead all the images with the class pop up at once.

I have tried using webflow native interactions in interactions panel using affect:class with all possible options (all,parent,children) but only get all the images to pop up at once.

The swiper.js is horizontal scrolling using mousewheel with thumbnail images. I have another div (seperate collection) with an img block that i’m trying to trigger to open when clicking swiper-slide. I have also tried placing the div & img into swiper collection but that doesn’t work because when larger image is opened, it too moves with the entire swiper even when trying position fixed.

I’m new with swiper.js and don’t know much javascript…but thinking if the solution can’t be sorted using native webflow interactions then it lays somewhere in the active class &/or using pointer events, h-ref, Or maybe I need connect 2 swiper collections but then what…?

In summary looking for a solution: ‘When clicking on swiper-slide = div/img pops up’ then a button/interaction when clicked on div/img it closes out and resets.

Webflow read-only link to current build:

https://preview.webflow.com/preview/ryan-voigt?utm_medium=preview_link&utm_source=designer&utm_content=ryan-voigt&preview=fddea90ab917232484249a2a052555c8&workflow=preview

<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
const swiper = new Swiper('.swiper.is-scrolling', {
  slideToClickedSlide: false,
  slidesPerView: "auto",
  freeMode: {
    enabled: true,
    sticky: false,
    momentumBounce: false,
      },
  scrollbar: {
    el: '.swiper-scrollbar',
    draggable: true,
    dragSize: 100,
  },
  mousewheel: {
    enabled: true,
    sensitivity: 1,
  },
});
</script>

desired effect when swiper-slide is clicked on

GSAP scrolling within a container that has overflow hidden

I’m having trouble getting this section to do what I want. I’m brand new to gsap, so I’m sure I’m not even close…

Here’s my codepen: https://codepen.io/mattmager/pen/WbemwmO

ScrollTrigger.create({
  y: 0,
  trigger: ".workplace .transform-work-content",
  start: 'top top',
  end: 'bottom center',
  pin: ".workplace .transform-work-content .tabs",
  scrub: true,
  markers: true
});

In short, I have 2 columns. The left col contains 3 links. The right col contains 3 corresponding blocks of content within a div that is set to overflow hidden. I want the entire section to pin to the page when scrolled into view, then the content on the right will scroll into view within the container as you continue scrolling, until it reaches the bottom, at which time it unpins and the scrolling continues down the page as normal.

Ultimately, I’d like the links on the left to ALSO be clickable to jump to the corresponding content sections on the right, but first, I just need to get the scrolling functionality working! Any help please!?

Javascript does not work when making slider [closed]

I am trying to make kind of a personal website, I would like to put there a slider with some projects I did. I have next to no experience with Javascript. HTML and CSS works fine, however when I try to move things around with JS, whole slider part just disappears. I tried Swiper js, and Glide js both. Same problem. I went over the HTML and CSS many times and the structure seems right.

Im putting the code here, but at this point it was changed so many times there might be a lot of errors. It is currently set to use Glide.js (dont look for Swiper adjustments). I figured the problem will be probably same in both cases, since it occured at the point of applying JS. Please, if someone can help me here, it would be great. html css

For Swiper I tried: Version 8 and 11, tried to put initialization code inside a DOMContentLoaded, checked errors in browser, tried to make just a swiper as a blank- this worked.
For Glide, I just stopped and came here once I saw JS again destroyed everything.

How can I move an object in Three js and export it properly to IFC?

I am using the Components library from ThatOpen to load an IFC file to my Three.js scene. With DragControls I am able to move stuff around, and I save the moved elements to a Map with the expressIDs as keys and the FragmentsGroups as values.
Then with the Web-IFC library I am finding the moved elements and try to update their placement, but the issue is that my code works only for certain elements, and for other elements the axes are being interchanged. For example, I move a door a lot to the left and a bit forward, export to IFC, open the IFC and it is properly placed. But if I move a window a lot to the left and a bit forward, export to IFC, when I open it, it is placed a lot forward and a bit to the left, so the x and z axis are switched…

private changeObjectTransformation(selectionGroup: FRAG.FragmentsGroup, expressIds: Set<number>) {
    for (let expressId of expressIds) {
        // Get the object from ifc file
        const object = this.ifcApi.GetLine(this.modelId, expressId);
    
        const objectPlac = this.ifcApi.GetLine(this.modelId, object.ObjectPlacement.value);
        const relativePlac = this.ifcApi.GetLine(this.modelId, objectPlac.RelativePlacement.value);
        const axis = this.ifcApi.GetLine(this.modelId, relativePlac.Axis.value);
        const refDir = this.ifcApi.GetLine(this.modelId, relativePlac.RefDirection.value);
        const location = this.ifcApi.GetLine(this.modelId, relativePlac.Location.value);
    
        const position = selectionGroup.position;
        
        // Create a new placement
        const point = this.ifcApi.CreateIfcEntity(this.modelId, WEBIFC.IFCCARTESIANPOINT, [
            position.x,
            position.z,
            position.y
        ]);
        
        // Create axis2placement3d
        const axis2placement3d = this.ifcApi.CreateIfcEntity(this.modelId, WEBIFC.IFCAXIS2PLACEMENT3D, 
            point,
            axis,
            refDir
        );
        
        // Create new local placement
        const localPlacement = this.ifcApi.CreateIfcEntity(this.modelId, WEBIFC.IFCLOCALPLACEMENT,
            objectPlac.PlacementRelTo,  // RelativeTo (keeping the original reference)
            axis2placement3d       // RelativePlacement
        );
        
        // Update the object's placement
        object.ObjectPlacement = localPlacement;
        
        // Write the modified object back to the model
        this.ifcApi.WriteLine(this.modelId, object);
    }
}

output or use a js variable from the function (function ($, Drupal) {}) Drupal

After sending data from my php to my js with DrupalSettings, I can use this data only in the function

(function ($, Drupal) {
    var myData = null; 
    Drupal.behaviors.customJs = {
        attach: function (context, settings) {
            if (settings.my_module && settings.my_module.myData) {
                myData = settings.my_module.myData; 
                if (myData.length > 0 && myData[0].back_file && myData[0].back_file.url) {
                    this.load.image("card-back", myData[0].back_file.url);
                }
            }
        }
    };
})(jQuery, Drupal);

I want to be able to use my myData variable outside because I use the phaser library and if I define a class in this function to use my variable, it becomes unfindable for my other files that use it
For a concrete example I try to do this:

class Preloader extends Phaser.Scene {
  constructor() {
    super({
      key: 'Preloader'
    });
  }

  preload() {
    (function($, Drupal) {
      var myData = null;
      Drupal.behaviors.customJs = {
        attach: function(context, settings) {
          if (settings.my_module && settings.my_module.myData) {
            myData = settings.my_module.myData;
          }
        }
      };
    })(jQuery, Drupal);

    this.load.image("card-back", myData[0].back_file.url);
  }
  create() {
    this.scene.start("Play");
  }
}

Why is bootstrap able to load the backdrop for one modal but not the other

so here’s my issue im building a django project based off of tutorials ect and I found for user feedback modals to be a good idea. I didn’t want to create a lot of different code pieces for modals so i decided to opt for a generic modal that i can use for all my cases.

<div class="modal fade" id="{{ modal_id }}" tabindex="-1" role="dialog" aria-labelledby="{{ modal_id }}Label" aria-hidden="true">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="{{ modal_id }}Label">{{ title }}</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <!-- Dynamic message -->
                <p id="modalMessage">{{ message }}</p>

                <!-- Conditional content based on context_type -->
                {% if context_type == "account_deletion" %}
                    <form method="POST" action="{{ action_url }}">
                        {% csrf_token %}
                        <div class="form-group">
                            <label for="password" style="margin-bottom:1rem;">Enter Your Password to Confirm:</label>
                            <input type="password" id="password" name="password" class="form-control" required style="margin-bottom: 1rem;">
                        </div>
                        <button type="submit" class="btn btn-danger">Delete Account</button>
                    </form>
                {% elif context_type == "item_deletion" %}
                    <p>Are you sure you want to delete this item?</p>
                {% endif %}
            </div>
            <div class="modal-footer">
                {% if context_type == "item_deletion" %}
                    <form method="POST" action="{{ action_url }}">
                        {% csrf_token %}
                        <button type="submit" class="btn {{ submit_class }}">{{ submit_label }}</button>
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    </form>
                {% endif %}
            </div>
        </div>
    </div>
</div>

in this project there will only be two cases, either the user wants to do something serious like account deletion, then they would need to enter their password to confirm.

or just to confirm deletion of a recommendation or comment ect.

this is where the problem lies. For some reason the modal works just fine when i want to delete an account, but if i want to delete a recommendation from the user profile, it doesn’t open the modal and throws this error at me:

[![enter image description here][1]][1]

I figured it was the modal formatting but that’s not the case as again works fine with my account deletion, and after that i assumed it may be way im accessing it but again not sure. I also tried moving the modal include to the bottom of the loop and outside the loop entirely.

The only reasonable thing i can think it is, is that it’s either trying to load a non existent recommendation or the way i have setup the button is wrong? but i don’t know.

Here’s extra code anyway for reference.

{% extends "base.html" %}
{% load static %}

{% block content %}
    <h2>{{ profile.user.username }}'s Profile</h2>
    <p>Bio: {{ profile.bio }}</p>
    <p>Location: {{ profile.location }}</p>

    {% if profile.avatar %}
        <img src="{{ profile.avatar.url }}" 
             alt="{{ profile.user.username }}'s avatar" 
             class="profile-avatar">
    {% else %}
        <img src="{% static 'images/default_avatar.png' %}" 
             alt="Default avatar" 
             class="profile-avatar">
    {% endif %}

    {% if user.is_authenticated %}
        {% if user == profile.user %}
            <!-- Show the edit profile button only if the logged-in user is the profile owner -->
            <a href="{% url 'edit_profile' username=request.user.username %}" 
               class="btn btn-primary">
               Edit Profile
            </a>
            <a href="{% url 'delete_account' username=request.user.username %}" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteAccountModal">
                Delete Account
            </a>
        {% endif %}
    {% endif %}

    <!-- Display Recommendations -->
    <h3>Recommendations</h3>
    {% for recommendation in recommendations %}
        <div class="recommendation">
            <p>{{ recommendation.content }}</p>
            {% if user == profile.user %}
                <a href="{% url 'edit_recommendation' recommendation_id=recommendation.id %}" class="btn btn-primary">Edit</a>
                <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteRecommendationModal{{ recommendation.id }}">
                    Delete
                </button>
                <!-- Delete Recommendation Confirmation Modal -->
                {% include "modal/modal.html" with modal_id="deleteRecommendationModal"|add:recommendation.id title="Confirm Recommendation Deletion" message="Are you sure you want to delete this recommendation?" context_type="item_deletion" action_url=recommendation.delete_url submit_class="btn-danger" submit_label="Delete Recommendation" %}
            {% endif %}
        </div>
    {% endfor %}

    <!-- Account Deletion Confirmation Modal -->
    {% include "modal/modal.html" with modal_id="deleteAccountModal" title="Confirm Account Deletion" message="This action is irreversible. Please confirm." context_type="account_deletion" action_url=delete_account_url submit_class="btn-danger" submit_label="Delete Account" %}
{% endblock %}

(my profile.html code)


def profile(request, username):
    """
    Profile view to display user profile along with their recommendations.

    Args:
        request (HttpRequest): The request object to handle the request.
        username (str): The username of the user whose profile is being viewed.

    Returns:
        HttpResponse: Renders the profile page with the user's profile and recommendations.
    """
    user = get_object_or_404(User.objects.select_related('profile'), username=username)
    recommendations = Recommendation.objects.filter(user=user)
    delete_account_url = reverse("delete_account", kwargs={"username": username})
    return render(request, "profiles/profile.html", {
        "profile": user.profile,
        "recommendations": recommendations,
        "can_edit": request.user == user,
        "delete_account_url": delete_account_url,
    })

(my profile view)


def delete_recommendation(request, recommendation_id):
    """
    Handles the deletion of an existing recommendation.
    
    Args:
        request: The HTTP request object containing user data.
        recommendation_id (int): The ID of the recommendation to be deleted.
    
    Returns:
        Redirects to the user's profile page after the recommendation is deleted.
    
    Description:
        This view allows the logged-in user to delete their own recommendation. 
        If the user is not the owner of the recommendation, they are redirected 
        to their profile page.
    """
    recommendation = get_object_or_404(models.Recommendation, id=recommendation_id)
    #defensive programming stop user from navigating via url to delete others recommendation
    if recommendation.user != request.user:
        return render(request, "error/403.html", status=403)
    
    recommendation.delete()
    messages.success(request, "Recommendation deleted successfully!")
    return redirect('profile', username=request.user.username)

delete recommendation view


class Recommendation(models.Model):
    """Recommendation model

    Args:
        models (django model class): class from djangos model framework

    Description:
        recommendation model for the database, once created here get's migrated
        to the database and is used for allowing users to see active recommendations 
        made
    """

    def __str__(self):
        """String func

        Returns:
            formatted string for admin viewing 
        """
        return f"{self.title} | Recommended by {self.user}"
    
    #title of the recommendation
    title = models.CharField(max_length=100, unique=True)
    # user, our fk linking back to the user who created it
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="recommendations",
    )
    # optional description field for the recommendation
    description = models.TextField(max_length=200, blank=True, null=True)
    # we add lat/long cords for rec to put on map (invisible to user)
    latitude = models.FloatField(blank=True, null=True)  
    longitude = models.FloatField(blank=True, null=True)
    # address of place in question
    address = models.CharField(max_length=255, unique=True)
    # Many-to-many field for categories
    categories = models.ManyToManyField(
        Category,
        related_name="recommendations",
        blank=True,
    )

    def average_score(self):
        """
        Average Score Func

        Args:
            self: (num): number to average

        Description:
            Method to calculate the votes, the votes are added by users and calculated here
            when the object (recommendation) is displayed, the vote is tallied and then shown
        """
        votes = self.votes.all()
        if votes.count() == 0:
            # No votes, return 0 as default
            return 0  
        total_score = sum(vote.vote for vote in votes)
        # Return the average score, rounded to 1 decimal place
        return round(total_score / votes.count(), 1)
    
    def delete_url(self):
        return reverse("delete_recommendation", args=[self.id])

urlpatterns = [
    path("recommendations/", recommendations, name="recommendations"),
    path("add_recommendation/", add_recommendation, name="add_recommendation"),
    path("edit_recommendation/<int:recommendation_id>/", edit_recommendation, name="edit_recommendation"),
    path("delete_recommendation/<int:recommendation_id>/", delete_recommendation, name="delete_recommendation"),
]

finally recommendation model (cos the tutorial had me add a backref) and recommendation views

any help would be appreciated
[1]: https://i.sstatic.net/pT9bLGfg.png

EDIT 1:

So a quick edit i broke this button in the profile.html

From:
                <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteRecommendationModal{{ recommendation.id }}">
                    Delete
                </button>
                <!-- Delete Recommendation Confirmation Modal -->
To:
                <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteRecommendationModal{{ recommendation.id }}">

                <!-- Delete Recommendation Confirmation Modal -->

the button still doesn’t work but it shows the modal text (and buttons) and does delete when it works so i think the error is in here

React-quill-new upgrades to react 18, toolbar list menus closes themselves on opening them

I have a problem on upgrading the quill editor using react-quill-new, them menus closes themselves on click and below is my code. please help me what to do

import ReactQuill from 'react-quill-new';

import ‘react-quill-new/dist/quill.snow.css’;

export const QuillEditor = (props: any) => {
const { description, setDescription } = props;

// toolbar options for quill editor
const toolbarOptions = [
    [{ 'size': ['small', false, 'large', 'huge'] }],
    [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
    [{ 'font': [] }],
    [{ 'list': 'ordered' }, { 'list': 'bullet' }],
    [{ 'script': 'sub' }, { 'script': 'super' }],
    [{ 'direction': 'rtl' }],
    [{ 'color': [] }, { 'background': [] }],
    [{ 'align': [] }],
    ['bold', 'italic', 'underline', 'strike'],
    ['blockquote', 'code-block'],
    ['link', 'image', 'video', 'formula'],
    ['clean'],
];
return <ReactQuill
    theme="snow"
    id="description"
    value={description}
    placeholder="Start typing here..."
    onChange={setDescription}
    modules={{
        toolbar: toolbarOptions,
        history: {          
            delay: 2500,
            userOnly: true
        },
    }

    }
/>

}

Any help will be appreaciated.

epub book doesnt display using epubjs, electron, but no errors in console log

i know im asking a lot, but if someone could help i would be very grateful as i am doing this for my college project and ive been stuck for days
i am not getting any errors in my console log but nothing is being displayed
i have checked that the epub file itself is fine, which it is, since it can be displayed in a regular html file
this is my code:

html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <link rel="stylesheet" href="style.css">
        <meta charset="UTF-8">
        <meta
        http-equiv="Content-Security-Policy"
        content="default-src 'self' 'unsafe-inline' blob: data: filesystem:; script-src 'self' https://cdn.jsdelivr.net; object-src 'self'"
        />    
        <title></title>
        <script src="https://cdn.jsdelivr.net/npm/epubjs/dist/epub.min.js"></script>
    </head>
    <body>
        <div id="content">
            <!-- Library Section -->
            <div id="library">
                <h3>Your Book Library</h3>
                <button id="openBookButton">Add Book</button>
            </div>
    
            <!-- Book Display Section -->
            <div id="bookContainer"></div>
            <button id="prevPageButton">Previous Page</button>
            <button id="nextPageButton">Next Page</button>
        </div>
    </body>
    <script src="./renderer.js"></script>
</html>

style for the bookContainer:

#bookContainer {
    width: 100%;
    height: 600px;
    overflow-y: auto;
    border: 1px solid red;
    background-color: #f0f0f0;
}

main.js:

const { app, BrowserWindow, ipcMain, dialog } = require('electron/main')
const path = require('node:path')
const fs = require('fs')

let win

const createWindow = () => {
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,
      contextIsolation: true,
      sandbox: false
    }
  })

  win.loadFile('index.html')
}

ipcMain.handle('select-epub-file', async () => {
  const result = await dialog.showOpenDialog({
      filters: [
          { name: 'EPUB Files', extensions: ['epub'] }
      ],
      properties: ['openFile']
  })

  if (!result.canceled) {
      const filePath = result.filePaths[0]
      console.log('File selected in main process:', filePath);
      const fileName = path.basename(filePath)
      const destPath = path.join(app.getPath('userData'), 'books', fileName)

      // Ensure the books directory exists
      if (!fs.existsSync(path.dirname(destPath))) {
          fs.mkdirSync(path.dirname(destPath), { recursive: true })
      }

      // Copy the EPUB file to the app's persistent data folder
      fs.copyFileSync(filePath, destPath)

      return destPath
  }
  return null
})

ipcMain.handle('load-epub-file', (event, filePath) => {
  if (filePath) {
    console.log('Loading EPUB file from renderer process:', filePath);
    return filePath;
  }
  return null;
})

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

preload.js:

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('api', {
    loadEpub: () => ipcRenderer.invoke('select-epub-file'),
})

renderer.js:

const openBookButton = document.getElementById('openBookButton')


openBookButton.addEventListener('click', () => {
    console.log('Button clicked, opening file dialog...')
    window.api.loadEpub().then((filePath) => {
        console.log('Selected EPUB file path:', filePath)
        if (filePath) {
            console.log('File selected:', filePath)
            initializeEPUB(filePath)
        }
    }).catch((error) => {
        console.error('Error loading EPUB:', error)
    });
});

function loadEPUB(filePath) {
    try {
        console.log('Loading EPUB file:', filePath);
        const book = ePub(filePath);
        console.log('EPUB initialized:', book);

        // Check if book's spine is loaded
        book.loaded.spine.then((spine) => {
            console.log('Spine loaded:', spine);
            console.log('Spine items:', spine.items); // Log the spine items
        }).catch((err) => {
            console.error('Error loading spine:', err);
        });

        return book; // Return the initialized book
    } catch (error) {
        console.error('Error initializing EPUB:', error);
        return null;
    }
}

function renderBook(book) {
    try {
        console.log('Rendering the book...');
        const rendition = book.renderTo('bookContainer', {
            method: 'viewport',
            width: '100%',
            height: '600px',
        });
        console.log('Rendition created:', rendition);
        return rendition; // Return the rendition
    } catch (error) {
        console.error('Error rendering the book:', error);
        return null;
    }
}

async function initializeEPUB(filePath) {
    const book = loadEPUB(filePath);
    if (!book) {
        console.error('Failed to load EPUB.');
        return;
    } else {
        console.log('EPUB loaded:', book);
    }

    const rendition = renderBook(book);
    if (!rendition) {
        console.error('Failed to render EPUB.');
        return;
    } else {
        console.log('Rendition created:', rendition);
    }

    // Wait for the spine to load before calling display
    try {
        await book.loaded.spine;  // Ensure spine is loaded before rendering
        console.log('Spine loaded successfully.');

        // Try to display the book
        await rendition.display(); // Wait for the rendering to be completed
        console.log('Book rendered successfully.');
        //setupNavigation(rendition); // Setup navigation after the book is displayed
    } catch (error) {
        console.error('Error rendering book:', error);
    }
}