How to properly import a modular JS library in Rails using Importmap (case date-fns)?

In my Rails 8 app I’m using Importmap and I would like to use date-fns in an appropriate manner given only one functionality and a couple of locales from that JS library are needed. Below is what I made so far.

To add date-fns to the project, I downloaded and put the whole date-fns JS library (many .js, .cjs, .cts, and .ts module files) within the folder app/javascript/vendor/date-fns.

Then I added the date-fns-related code to the Rails project as follows:

# config/importmap.rb

// ...

pin 'date-fns', to: 'vendor/date-fns/formatDistanceToNow.js' 
pin 'date-fns/locale/it', to: 'vendor/date-fns/locale/it.js' 
pin 'date-fns/locale/en-US', to: 'vendor/date-fns/locale/en-US.js'
# app/javascript/controllers/time_ago_controller.js

import { Controller } from "@hotwired/stimulus"
import { formatDistanceToNow } from "date-fns";
import { it } from "date-fns/locale/it";
import { enUS } from "date-fns/locale/en-US";

// Connects to data-controller="time-ago"
export default class extends Controller {
  // ...
}
# show.html.erb

<div data-controller="time-ago">
  ...
</div>

Althrough the above does work, when I visit an app page then in the browser’s “Developer Tools” > “Network” tab I see that many date-fns-related JS requests (each loading individual imported date-fns .js files including dependent ones, as well) are fired.

enter image description here

This seems to happen regardless if in the page there is or not the time-ago Stimulus controller connected. Also, the date-fns-related JS requests are not cached if I re-visit the page and the date-fns files are no fingerprinted as I would expect.

Furthermore, many visits to the page executing all these JS requests make me being blocked with a response like “Too many requests” (I’m using the rack-attack gem and maybe it is enabling the protection… but it is good to have such a protection, so the problem should be solved with loading date-fns).

How to improve/solve? How one is supposed to handle a situation like the above in Rails using Importmap? Am I missing something?

How to properly import a modular JS library in Rails (case date-fns)?

In my Rails 8 app I’m using Importmap and I would like to use date-fns in an appropriate manner given only one functionality and a couple of locales from that JS library are needed. Below is what I made so far.

To add date-fns to the project, I downloaded and put the whole date-fns JS library files (many .js, .cjs, .cts, and .ts module files) within the folder app/javascript/vendor/date-fns.

Then I added the date-fns-related code to the Rails project as follows:

# config/importmap.rb

// ...

pin 'date-fns', to: 'vendor/date-fns/formatDistanceToNow.js' 
pin 'date-fns/locale/it', to: 'vendor/date-fns/locale/it.js' 
pin 'date-fns/locale/en-US', to: 'vendor/date-fns/locale/en-US.js'
# app/javascript/controllers/time_ago_controller.js

import { Controller } from "@hotwired/stimulus"
import { formatDistanceToNow } from "date-fns";
import { it } from "date-fns/locale/it";
import { enUS } from "date-fns/locale/en-US";

// Connects to data-controller="time-ago"
export default class extends Controller {
  // ...
}
# show.html.erb

<div data-controller="time-ago">
  ...
</div>

Althrough the above does work, when I visit an app page then in the browser’s “Developer Tools” > “Network” tab I see the date-fns-related JS requests that load the individual imported date-fns .js files (including all the dependent files, as well) are fired. This seems to happen regardless if in the page there is or not the time-ago Stimulus controller connected.

enter image description here

These date-fns-related JS requests are not cached if I re-visit the page. Also, the date-fns files are no fingerprinted.

Furthermore, all these JS requests make me being blocked by the server with a response like “Too many requests” (I’m using the rack-attack gem and maybe it is enabling the protection… but it is good to have such a protection, so the problem should be solved with loading date-fns).

How to improve/solve? How one is supposed to handle a situation like the above?

Optimization of string concatenation in different languages

I have a program to perform denormalization on large data files before I import the data to an OLAP database. I am trying to make this denormalization as fast as possible. My “native” language is Javascript so I’m doing the prototype there. The inner loop of this process, and source of 95% of the latency, is a simple string concatenation that produces each row of a csv, which I stream to a file.

My question is, would another language be expected to perform this simple task much faster than javascript? The latency seems entirely CPU related. I am using parallel processes. 20 billion rows still takes about half an hour.

The following code is the inner loop. I have tried very hard to tweak this in many ways to make it faster and I’m pretty sure there’s no more juice to squeeze in js. Naked for loop isn’t faster. Nested arrays, nope.

items.forEach((id, i)=>{
            rows.push( 
                csi + "_" + id,  
                DELIMITER,
                cells[0],
                DELIMITER,
                cellValues[i],
                DELIMITER,
                "n"
            );

        });

how to sub total amount not show

To fix the issue where the subtotal is not showing correctly in the Cart Summary at the Bottom, we need to ensure that the JavaScript correctly updates the value for the .sub_total element in the cart summary, just as it does for the table’s subtotal. not display sub total how to slove this problem i am new php devloper

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cart Summary</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <style>
        .fixed-bottom-clr1 {
            background-color: #f8f9fa;
        }

        .badgescntr {
            position: absolute;
            left: 33%;
        }

        .medium {
            font-size: 1.2rem;
        }
    </style>
</head>

<body>

    <div class="container mt-5">
        <h2>Price List</h2>

        <table class="table table-bordered pricelist_table mb-0">
            <thead>
                <tr>
                    <th class="text-center">Product Name</th>
                    <th class="text-center">Price</th>
                    <th class="text-center">Quantity</th>
                    <th class="text-center">Total</th>
                </tr>
            </thead>
            <tbody>
                <!-- Product 1 -->
                <tr>
                    <td class="text-center">Product 1</td>
                    <td class="text-center">50</td>
                    <td class="text-center">
                        <input type="number" class="form-control product-quantity" data-price="50" value="1" min="1">
                    </td>
                    <td class="text-center product-total">50</td>
                </tr>
                <!-- Product 2 -->
                <tr>
                    <td class="text-center">Product 2</td>
                    <td class="text-center">30</td>
                    <td class="text-center">
                        <input type="number" class="form-control product-quantity" data-price="30" value="1" min="1">
                    </td>
                    <td class="text-center product-total">30</td>
                </tr>
                <!-- Total Calculation -->
                <tr>
                    <td colspan="3" class="text-right">Subtotal:</td>
                    <td class="text-center sub_total">80</td>
                </tr>
            </tbody>
        </table>
    </div>

    <!-- Fixed Cart Summary at Bottom -->
    <div class="container py-2">
        <div class="row justify-content-center">
            <div class="col-lg-6 text-center fixed-bottom-clr1 px-4">
                <div class="d-flex align-items-center justify-content-center">
                    <div class="medium fnt2 align-self-center">
                        <span class="total_products_count">2</span> items . 
                        <i class="bi bi-currency-rupee"></i> <span class="sub_total">80.00</span>
                    </div>
                    <span class="px-2"> | </span>
                    <span class="medium fnt1 font-weight-bold align-self-center" style="position: relative;">
                        <div class="badges1 badgescntr medium total_products_count">2</div>
                        <i class="bi bi-cart3"></i> &nbsp; View Cart
                    </span>
                </div>
            </div>
        </div>
    </div>

    <script>
        // Function to update the total when quantity changes
        document.querySelectorAll('.product-quantity').forEach(input => {
            input.addEventListener('input', updateCart);
        });

        function updateCart() {
            let totalItems = 0;
            let subTotal = 0;

            // Loop through all product rows and calculate the total and subtotal
            document.querySelectorAll('.product-quantity').forEach(input => {
                const quantity = parseInt(input.value) || 0;
                const price = parseFloat(input.getAttribute('data-price')) || 0;
                const productTotal = quantity * price;

                // Update the individual product total
                input.closest('tr').querySelector('.product-total').textContent = productTotal.toFixed(2);

                // Update subtotal and total items
                subTotal += productTotal;
                totalItems += quantity;
            });

            // Update subtotal in both table and cart summary
            document.querySelector('.sub_total').textContent = subTotal.toFixed(2);
            document.querySelector('.total_products_count').textContent = totalItems;
        }

        // Initial call to update the cart
        updateCart();
    </script>

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>

</html>

How to use onedrive for storage with a firebase authentication

Hope everyone is well…

So I’m developing a pwa (it’s my first one) for a guy with a small covenyencing company. He pays for a family plan of onedrive, allows like 5 people to use up to 1TB of storage and I’ve already built a basic sign up/ login system using firebase for the pwa.
I want to have a system such that when a user is logged in they can upload a file to the pwa which automatically makes a folder with their email on the one drive and stores their files in that folder if there is none and if there are it appends to that folder.
So it uses onedrive for the storage and the free firebase authentication as he already pays for onedrive and firebase auth is free.

I’ve seen smth about Microsoft graph api but I’m now sure if it does what I want it to, can anyone advise me on how to achieve this step by step. Furthermore I’d need the connection between pwa and onedrive to be seemless and secure as it would be sensitive information being uploaded ie. Proof of residence, ID documents etc. (files one would need to give a conveyancer when purchasing a property)

What did I try and do?
I Built the log in and sign up system and that works 100%
Tried asking him to give me full access on his onedrive but and once I have access to that folder on my side could some how link it by some means but it didn’t seem to work

Kind of in a pickle hear folks, would really appreciate it if someone could help me debug this

two sets of dialogs that aren’t active at the same time but are mapped in the same places, distinguished by a button click?

im a real beginner so bear with me here!

some background:
im making my own website because i got tired of the customization limits on sites like Carrd, so my website has a goofy style and looks like a teen in 2005 made it- its intentional.
for my Art Portfolio page, i essentially have a set of CRTs that give you a preview of each piece of mine. you click each one, and it brings up a dialog so you can see the image in further detail and even download it if youd like.

the set of CRTs previewing each piece
the dialog that comes up when the first one is clicked

My problem:
i would ideally like that “Physical” button up at the top to allow you to switch to an entirely different set of CRTs with different images and dialogs. theres a set of 16 images and dialogs for Digital, and a different set of 16 for Physical. The remote at the top allows you to navigate between, for 32 unique dialogs in total.

That probably is incredibly inefficient, but that’s the way I want it to look, SO…

What’s the best way to have two sets of clickable dialogs that aren’t active at the same time but are mapped in the same places?

Thank you for bearing with me here- I tend to get overambitious with my First Time projects, but that’s just because I’m really enjoying learning about html! I appreciate any help or guidance.

I’ve successfully mapped all 16 of the Digital dialogs, and they all come up just fine with the correct images. I’ve attempted some JS to allow me to switch to a new set, but wrangling the first set took a bit of time so I’ve been stuck since. My code is probably the most inefficient way to ever do it (16 dialogs individually mapped out with CSS and in their own DIVs)- I’m okay with that for right now, just as long as it works!

How do I read Conflicting Peer dependency? Asterisk (*) causing differences?

npm error Found: @types/[email protected]
npm error node_modules/@types/react
npm error   peerOptional @types/react@"*" from @radix-ui/[email protected]
npm error   node_modules/@radix-ui/react-arrow
npm error     @radix-ui/react-arrow@"1.1.1" from @radix-ui/[email protected]
npm error     node_modules/@radix-ui/react-popper
npm error       @radix-ui/react-popper@"1.2.1" from @radix-ui/[email protected]
npm error       node_modules/@radix-ui/react-menu
npm error         @radix-ui/react-menu@"2.1.4" from @radix-ui/[email protected]
npm error         node_modules/@radix-ui/react-dropdown-menu
npm error   peerOptional @types/react@"*" from @radix-ui/[email protected]
npm error   node_modules/@radix-ui/react-checkbox
npm error     @radix-ui/react-checkbox@"^1.1.3" from the root project
npm error   37 more (@radix-ui/react-collection, ...)
npm error
npm error Could not resolve dependency:
npm error @radix-ui/react-label@"2.0.2" from the root project
npm error
npm error Conflicting peer dependency: @types/[email protected]
npm error node_modules/@types/react
npm error   peer @types/react@"^19.0.0" from @types/[email protected]
npm error   node_modules/@types/react-dom
npm error     peerOptional @types/react-dom@"*" from @radix-ui/[email protected]
npm error     node_modules/@radix-ui/react-label
npm error       @radix-ui/react-label@"2.0.2" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error

I am trying to install @radix-ui/[email protected], but I am getting this super confusing message. When I read it I see that it is trying to install @types/react-dom@"*". Given that it’s a *, why does it default to the latest types? Why doesn’t it match the types that already exists on my system (i.e. 18.3.18).

Any tips on how I should read this error message?

React: Cursor jumps to end of the textbox upon edit

I have a textbox which when changed, I need to update server (API to do DB update). The API response will be updated text which I use to update state.

Problem is: When I change something in the middle of the text, cursor jumps to the end. This does not happen if I don’t do server update.

/*** THIS DOES NOT WORK ***/
<input 
    type='text' 
    value='state' 
    onChange={(e) => DB.update(e.target.vaule).then(newData => setState(newData)}
/>


/*** THIS WORKS ***/
<input 
    type='text' 
    value='state' 
    onChange={(e) => setState(e.target.value)}
/>

How can I stop cursor jumping when I set state via back-end response?

window.postMessage({ trigger: “startCounting” }, “*”); does not trigger

i have a rolodex counter that i need at times to trigger the animation via posting a message

it does not work, instead of the two digits animating counting, only a single digits flickers without ending.

even on the console window.postMessage({ trigger: “startCounting” }, “*”);
generates the same unwanted effect

i tried manipulating the animation, simplifying various actions all yielded the same result
the code snippet

console.log("Version 1.77 - Explicit Viewport Trigger Integration");

class RolodexCounterElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }

  // Specify the observed attributes
  static get observedAttributes() {
    return ["start-value", "end-value", "duration", "font-size", "font-family", "font-weight", "color"];
  }

  // Handle attribute changes
  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`Attribute changed: ${name} from ${oldValue} to ${newValue}`);
    if (oldValue !== newValue) {
      this.updateComponent(); // Re-render component with new attributes
    }
  }

  connectedCallback() {
    console.log("Rolodex Counter connected to DOM");

    // Initial render
    this.updateComponent();

    // Elements
    const numbersContainer = this.shadowRoot.querySelector(".frm-elmnt-numbers");

    // Intersection Observer for viewport trigger
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          console.log("Element entered viewport: triggering animation");
          numbersContainer.classList.add("animation");
        } else {
          numbersContainer.classList.remove("animation");
        }
      });
    });

    // Start observing the custom element
    observer.observe(this);

    // Debugging trigger for manual testing
    window.addEventListener("message", (event) => {
      if (event.data.trigger === "startCounting") {
        console.log("Manual trigger received");
        numbersContainer.classList.add("animation");
      }
    });
  }

  updateComponent() {
    const startValue = parseInt(this.getAttribute("start-value")) || 0;
    const endValue = parseInt(this.getAttribute("end-value")) || 99;
    const duration = parseInt(this.getAttribute("duration")) || 4000;
    const fontSize = this.getAttribute("font-size") || "7.5rem";
    const fontFamily = this.getAttribute("font-family") || "'Graphik', sans-serif";
    const fontWeight = this.getAttribute("font-weight") || "300";
    const color = this.getAttribute("color") || "#2e2e38";

    console.log("Attributes:", { startValue, endValue, duration, fontSize, fontFamily, fontWeight, color });

    // Template
    const template = `
            <div class="frm-elmnt-counter">
                <div class="frm-elmnt-numbers-wrapper">
                    <div class="frm-elmnt-numbers"></div>
                </div>
                <div class="frm-elmnt-percent">%</div>
            </div>
            <style>
                :host {
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    width: auto;
                    max-width: 100%;
                    height: auto;
                    max-height: 100%;
                    background: transparent;
                    overflow: hidden;
                    box-sizing: border-box;
                }
                .frm-elmnt-counter {
                    display: flex;
                    align-items: baseline;
                    font-size: ${fontSize};
                    font-weight: ${fontWeight};
                    font-family: ${fontFamily};
                    color: ${color};
                    line-height: 1;
                    overflow: hidden;
                }
                .frm-elmnt-numbers-wrapper {
                    height: ${fontSize};
                    overflow: hidden;
                    position: relative;
                }
                .frm-elmnt-numbers {
                    display: flex;
                    flex-direction: column;
                    transform: translateY(0);
                    will-change: transform;
                }
                .frm-elmnt-numbers.animation {
                    animation: frm-elmnt-roll-animation ${duration}ms ease-out forwards;
                }
                .frm-elmnt-percent {
                    margin-left: 0.1em;
                    font-weight: ${fontWeight};
                    display: inline-block;
                    line-height: 1;
                    position: relative;
                    color: ${color};
                    font-size: ${fontSize};
                    font-family: ${fontFamily};
                    margin-left: 0.05em; /* Adjust negative margin */
                    padding-left: 0.05em;
                    letter-spacing: -0.005em;
                }
                @keyframes frm-elmnt-roll-animation {
                    0% {
                        transform: translateY(0);
                    }
                    85% {
                        transform: translateY(var(--final-position));
                    }
                    90% {
                        transform: translateY(var(--overshoot-position));
                    }
                    95% {
                        transform: translateY(var(--final-position));
                    }
                    100% {
                        transform: translateY(var(--final-position));
                    }
                }
            </style>
        `;

    this.shadowRoot.innerHTML = template;

    // Elements
    const numbersContainer = this.shadowRoot.querySelector(".frm-elmnt-numbers");
    const percentElement = this.shadowRoot.querySelector(".frm-elmnt-percent");

    // Generate Numbers
    const steps = endValue - startValue + 1;
    const numbers = Array.from({ length: steps }, (_, i) => startValue + i);
    numbersContainer.innerHTML = numbers.map((num) => `<span>${num}</span>`).join("");

    console.log("Generated numbers:", numbers);

    // Calculate Positions
    const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
    const lineHeight = parseFloat(getComputedStyle(numbersContainer).fontSize);
    const remConversion = lineHeight / rootFontSize;
    const finalPosition = `-${(steps - 1) * remConversion}rem`;
    const overshootPosition = `-${(steps - 1) * remConversion + 0.3}rem`;

    numbersContainer.style.setProperty("--final-position", finalPosition);
    numbersContainer.style.setProperty("--overshoot-position", overshootPosition);

    // Align Percent Sign
    const enforceAlignment = () => {
      const numbersHeight = numbersContainer.offsetHeight;
      const percentHeight = percentElement.offsetHeight;
      const topAdjustment = Math.min((numbersHeight - percentHeight) / 2, 0);
      percentElement.style.top = `${topAdjustment}px`;
    };

    enforceAlignment();
    window.addEventListener("resize", enforceAlignment);
  }
}

customElements.define("rolodex-counter-element", RolodexCounterElement);

How to Securely Implement Gemini 2.0 Multimodal WebSocket Proxy in Node.js and React?

I’m building a voice chat system using the Gemini 2.0 Multimodal WebSocket in Node.js for the backend and React for the frontend. Google provides an example repository here: https://github.com/google-gemini/multimodal-live-api-web-console.

However, the issue is that the API key is exposed in the WebSocket connection on the frontend, which is a security risk. To mitigate this, I am trying to implement a proxy server in Node.js so the frontend communicates with my backend, and the backend securely connects to Gemini’s WebSocket API.

Here’s the workflow I am attempting to build:

Frontend: Sends voice data (via WebSocket) to my Node.js proxy server.
Backend (Node.js): Connects to Gemini’s WebSocket API with the API key and relays data/responses to/from the frontend.
The problem is:

The WebSocket connection between my backend and Gemini’s API establishes correctly the first time.
When I send the voice data, I don’t receive any response from the Gemini WebSocket server.
Here’s a simplified version of my backend proxy WebSocket implementation:

/// backend code





`const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const cors = require('cors');
const WebSocket = require('ws');

const app = express();
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
    cors: {
        origin: "*",
        methods: ["GET", "POST"],
    },
});

const GeminiKey= 'Your api key'
const model = `models/gemini-2.0-flash-exp`;
const GEMINI_API_URL = 'wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContent';

const clientGeminiSockets = new Map(); 
const clientRetryCounts = new Map(); 


const createGeminiConnection = (socketId) => {
    const geminiSocket = new WebSocket(`${GEMINI_API_URL}?key=${GeminiKey}`);

    geminiSocket.onopen = () => {
        console.log(`Connected to Gemini API for client: ${socketId}`);
        const setupMessage = {
            setup: {
                generation_config: { response_modalities: ["AUDIO"] },
                model: model
            },
        };
        geminiSocket.send(JSON.stringify(setupMessage));
        // Reset retry count on successful connection
        clientRetryCounts.set(socketId, 0);
    };


    geminiSocket.onmessage = (event) => {
        try {
             console.log(`Received message from Gemini API for client ${socketId}:`, event.data);
              // Handle binary data
              if (event.data instanceof Buffer) {
                console.log(`Sending Gemini Audio data to client ${socketId}: binary data`);
                //Send binary data
                 io.to(socketId).emit('geminiAudio', event.data.toString('base64'));
            }
            else{
                const message = JSON.parse(event.data)
                io.to(socketId).emit('message', message);
           }
       } catch (err) {
           console.error("Error handling gemini message:", err);
          io.to(socketId).emit('message', JSON.stringify({ error: "Error handling Gemini API message" }));
      }
    };


    geminiSocket.onerror = (error) => {
        console.error(`Gemini API WebSocket Error for client ${socketId}:`, error);
        io.to(socketId).emit('message', JSON.stringify({ error: `Gemini API WebSocket Error: ${error.message}` }));
       handleReconnection(socketId, geminiSocket);
    };

    geminiSocket.onclose = (event) => {
        console.log(`Gemini API WebSocket closed for client ${socketId}:`, event.code, event.reason);
       io.to(socketId).emit('message', JSON.stringify({ error: `Gemini API WebSocket Closed ${event.code}, ${event.reason}` }));
       clientGeminiSockets.delete(socketId);
       clientRetryCounts.delete(socketId)
    };
    clientGeminiSockets.set(socketId, geminiSocket);
    return geminiSocket
};

const handleReconnection = (socketId, geminiSocket) => {
    const retryCount = clientRetryCounts.get(socketId) || 0;
   const delay = Math.min(1000 * Math.pow(2, retryCount), 30000); // Maximum 30 sec
   console.log(`Attempting to reconnect to Gemini for client: ${socketId} in ${delay}ms`);
    setTimeout(() => {
        if(!geminiSocket || geminiSocket.readyState !== WebSocket.OPEN){
            console.log(`Reconnecting Gemini API for client ${socketId}`);
           createGeminiConnection(socketId);
       }
   }, delay);
   clientRetryCounts.set(socketId, retryCount + 1);
}


io.on('connection', (socket) => {
   console.log(`Client connected: ${socket.id}`);
    let geminiSocket = createGeminiConnection(socket.id);
    socket.on('message', (formattedMessage) => {
        try {
            console.log(`Received message from client ${socket.id}:`,);
           if (geminiSocket && geminiSocket.readyState === WebSocket.OPEN) {
                const messageToSend = {
                   realtimeInput: {
                        mediaChunks: [
                           {
                                mimeType: "audio/pcm;rate=16000",
                                data: formattedMessage.mediaChunks[0].data,
                            }
                        ]
                   }
                };
                console.log(`Sending data to gemini API for client ${socket.id}:`, );
               geminiSocket.send(JSON.stringify(messageToSend));
            } else {
                console.error(`Gemini API Websocket is not open for client ${socket.id}`);
               socket.emit('message', JSON.stringify({ error: "Gemini API Websocket is not open" }));
           }
        } catch (error) {
            console.error(`Error forwarding message to Gemini API for client ${socket.id}:`, error);
            socket.emit('message', JSON.stringify({ error: `Error forwarding message ${error.message}` }));
        }
    });

    socket.on('disconnect', () => {
        console.log(`Client disconnected: ${socket.id}`);
        const geminiSocket = clientGeminiSockets.get(socket.id);
        if (geminiSocket) {
           geminiSocket.close();
            clientGeminiSockets.delete(socket.id);
             clientRetryCounts.delete(socket.id)
            console.log(`Gemini connection closed for client: ${socket.id}`);
        } else {
            console.log(`No gemini connection found for client: ${socket.id}`);
       }
    });
});

const PORT = 5000;
server.listen(PORT, () => {
    console.log(`Proxy server running on port ${PORT}`);
});
`




/// frontend code

`

import { useEffect, useRef, useState } from 'react';
import io from 'socket.io-client';

export default function Home() {
    const [isRecording, setIsRecording] = useState(false);
    const [socket, setSocket] = useState(null);
    const audioRef = useRef(null);
    const [geminiAudio, setGeminiAudio] = useState(null)
    const audioContextRef = useRef(null);
    const scriptProcessorRef = useRef(null);
    const [connectionError, setConnectionError] = useState(null)

    const startRecording = async () => {
        if (!socket) {
            console.error("Socket is not connected.");
            return;
        }
       if(connectionError){
            console.log("There is a connection error, cannot start recording")
            return
        }
        try {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            audioContextRef.current = new AudioContext({ sampleRate: 16000 }); // Create audio context with 16kHz
            const source = audioContextRef.current.createMediaStreamSource(stream);
            scriptProcessorRef.current = audioContextRef.current.createScriptProcessor(4096, 1, 1); // Create script processor
             source.connect(scriptProcessorRef.current);
             scriptProcessorRef.current.connect(audioContextRef.current.destination);

            scriptProcessorRef.current.onaudioprocess = (event) => {
                 if (socket && socket.connected) {
                     const pcmData = event.inputBuffer.getChannelData(0); // Get PCM data
                    console.log("PCM data", pcmData)
                    const base64Audio = arrayBufferToBase64(pcmData.buffer);
                    console.log("base64Audio data", base64Audio);
                    const formattedMessage = {
                        mediaChunks: [
                            {
                                mimeType: "audio/pcm;rate=16000",
                                data: base64Audio,
                            },
                        ],
                    };
                    console.log("Formatted message sent to backend:", formattedMessage);
                   socket.emit('message', formattedMessage);
                } else {
                    console.error("Socket is not connected");
               }
           };
            setIsRecording(true);
       } catch (err) {
           console.error("Error recoring audio", err)
       }
   };


    const stopRecording = () => {
        if (scriptProcessorRef.current) {
           scriptProcessorRef.current.disconnect();
            scriptProcessorRef.current.onaudioprocess = null;
            audioContextRef.current.close();
             setIsRecording(false);
       }
    };


    function arrayBufferToBase64(buffer) {
        let binary = '';
         const bytes = new Uint8Array(buffer);
       const len = bytes.byteLength;
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
       return btoa(binary);
    }


    useEffect(() => {
       const newSocket = io('http://localhost:5000'); // Replace with your backend URL

        newSocket.on('connect', () => {
            console.log("connected to websocket server")
             setConnectionError(null)
        });
       newSocket.on('message', (message) => {
            console.log('Received message:', message);
            try {
                const parsedMessage = JSON.parse(message)
               if(parsedMessage.error){
                   setConnectionError(parsedMessage.error)
                    console.log("Setting connection error", parsedMessage.error)
                 }
           }`your text`
           catch (error){
                console.log("Message is not in JSON format")
          }
       });
       newSocket.on('geminiAudio', (audioData) => {
            console.log('Received geminiAudio data from backend:', audioData);
            setGeminiAudio(audioData)
       })
       setSocket(newSocket);
       return () => {
            newSocket.disconnect();
       };
   }, []);

    useEffect(() => {
         if (geminiAudio) {
           const audio = audioRef.current;
            const audioBlob = new Blob([Uint8Array.from(atob(geminiAudio), c => c.charCodeAt(0))], { type: 'audio/webm' });
            console.log("Audio blob created", audioBlob);
            const audioUrl = URL.createObjectURL(audioBlob);
            console.log("Audio URL created", audioUrl);
            audio.src = audioUrl;
            audio.play();
        }
    }, [geminiAudio])

   return (
        <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
           <h1 className="text-3xl font-bold mb-6">Live Voice Conversation</h1>
           {connectionError && <div className="text-red-500 mb-4">{connectionError}</div>}
           <div className="space-x-4">
                <button
                   className={`px-4 py-2 rounded ${
                      isRecording
                        ? 'bg-red-500 text-white'
                        : 'bg-green-500 text-white hover:bg-green-600'
                    }`}
                   onClick={isRecording ? stopRecording : startRecording}
               >
                   {isRecording ? 'Stop Recording' : 'Start Recording'}
                </button>
                 <audio ref={audioRef} controls>
                     Your browser does not support the audio element.
               </audio>
           </div>
      </div>
    );
}`

// What I Tried :
Checked WebSocket Connection – The WebSocket successfully connects to both the frontend and the Gemini API.
Logged Sent Messages – Verified that the frontend is sending voice data correctly, and the backend is forwarding it to Gemini.
Checked for Responses – Added console.log to print any incoming messages from Gemini’s WebSocket.
Handled Errors – No error messages are appearing in the WebSocket error event.

// What I Expected :
When the frontend sends voice data, the backend should relay it to Gemini’s WebSocket API.
Gemini should process the voice input and return a response, which my backend would send back to the frontend.
The frontend should receive the response and display/play it.

React fiber it is a mechanism of rendering or mechanism of reconcilation within rendering?

Different sources give a bunch of different definitions of react fiber. But two key ones are: Fiber is a matching mechanism within rendering and Fiber is a rendering mechanism. I’m more inclined to think it’s a rendering mechanism, but I’d like to get an exact answer to my question.

As far as I currently understand the mechanics of how Fiber works are:

  1. In case of mount: first in the render phase, creating a ReactDom representation of the components, and creating a list of Fiber nodes. Next in the commit phase:
    The nodes are inserted into the DOM tree,
    installing the refs,
    launch layout effects,
    rendering of the DOM by the browser,
    Execution of effects passed to useEffect.

  2. In case of update:
    First in the rendering phase creating a new ReactDom view of the components, and creating a new list of Fiber nodes.
    Then in the differing(or reconciliation) phase.
    Comparing the two node views using a special algorithm.
    Next in the commit phase:
    The nodes are inserted into the DOM tree,
    setting up the refs,
    cleanup of the useLayoutEffect() function
    launch layout effects,
    The DOM is reprinted,
    The cleanup functions returned by useEffect are called.
    Execution of effects passed to useEffect

If you have any comments on the interpretation of the mechanics of the work, I would also be grateful.

Why does Universal ctags fail to record some functions in the TAGS file for a project using React and JSX?

I use Emacs, and when I began my React/jsx project I found that etags missed too many declared names, but it wasn’t a problem because the project was small. Now the project has grown to 10,000 lines and working tags would be really useful, so I tried Universal ctags:

% ctags --version
Universal Ctags 6.0.0(caeb22af), Copyright (C) 2015-2022 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Jul 19 2023, 23:18:38
  URL: https://ctags.io/
  Output version: 0.0
  Optional compiled features: +wildcards, +regex, +gnulib_fnmatch, +gnulib_regex, +iconv, +option-directory, +xpath, +json, +interactive, +yaml, +case-insensitive-filenames, +packcc, +optscript, +pcre2

I used this command line:

ctags -e -R --languages=javascript --exclude=node_modules --exclude=build

A number of functions failed to appear in the TAGS file. Here’s the simplest code fragment I was able to create that exhibits the problem:

  const Comp1 = () => {
    const x = (arg) => console.log(arg)
    x(4)
    return(<Comp2 text={<p>Some Text</p>}/>)
  }
             
  const Comp2 = (props) => {
    return (
      props.text
    )
  }

I put this code in a file called test.js and ran it through this ctags command:

ctags -e --languages=javascript --exclude=node_modules --exclude=build --quiet test.js

Then this appears in the TAGS file. You can see that the Comp2 function is missing. I’ve replaced the control characters with printing characters:

test.js,81
  const Comp1 = () => {^?Comp1^A1,0
    const x = (arg) => console.log(arg)^?x^A2,24

But if you do something as simple as remove the line “x(4)” so that the code appears like this:

  const Comp1 = () => {
    const x = (arg) => console.log(arg)
    return(<Comp2 text={<p>Some Text</p>}/>)
  }
             
  const Comp2 = (props) => {
    return (
      props.text
    )
  }

Then the TAGS file looks like this:

test.js,122
  const Comp1 = () => {^?Comp1^A1,0
    const x = (arg) => console.log(arg)^?x^A2,24
  const Comp2 = (props) => {^?Comp2^A7,139

Now the Comp2 function is present. Removing the JSX code in the return statement will also cause the Comp2 function to appear in the TAGS file.

My guess is that the JSX code is confusing the ctags Javascript parser, and I’m hoping that there’s a solution for this, or perhaps a JSX regex parser for ctags, or maybe some way to workaround it.

Electron + Nextjs: Links have incorrect href after building

My Electron + Next.js application has the client-side generated sidebar with the link:

<Link href="/wallets">wallets</Link>

Which works as expected when ran locally.

When the app is built and packed, these links transform not into the expected format with app directory included and .html in the end, but remain as they were in original source code.

It will result in this issue also in case if added in layout.tsx which is SSR-generated.

In original source:

/wallets

Expected after build & pack:

file:///Users/user/Desktop/project/dist/mac-arm64/MintBox.app/Contents/Resources/app.asar/out/wallets.html

Actual:

<a href="/wallets"> ... </a> which transforms into file:///wallets after clicking

Project structure:

.
├── app
│   ├── layout.tsx
│   ├── page.tsx
│   ├── providers.tsx
│   └── wallets
│       └── page.tsx
├── components
│   ├── list-item.tsx
│   └── sidebar
│       └── app_sidebar.tsx
├── electron
│   ├── main.js
│   └── preload.js
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── styles
│   └── global.css
├── tailwind.config.js
├── tsconfig.json
└── yarn.lock

The electron/main.js function:

function createWindow() {

    mainWindow = new BrowserWindow({
        width: 1200,
        height: 800,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            nodeIntegration: false,
            contextIsolation: true,
        },
        icon: path.join(__dirname, 'assets', 'icon.png')
    });
    mainWindow.setBackgroundColor("black");

    // this page will load correctly, but in case of the Link with href='/' it will result in the same error as described  
    const appUrl = isDev
        ? 'http://localhost:3000'
        : `file://${path.join(app.getAppPath(), 'out', 'index.html')}`;

    mainWindow.loadURL(appUrl);

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

The package.json has the following pack script:

"main": "electron/main.js",
  "scripts": {
    "dev": "concurrently "next dev" "cross-env NODE_ENV=development electron ."",
    "build": "next build",
    "pack-app": "npm run build && electron-builder --dir",
    "start": "electron ."
  },

next.config.js:

const isProd = process.env.NODE_ENV === 'production';
module.exports = {
    reactStrictMode: true,
    assetPrefix: isProd ? './' : '',
    output: "export",
};

I’ve tried to:

  • switch webSecurity in main.js
  • assetPrefix in next.config.js
  • make original paths to end with / e.g., /wallets/
  • use electron-builder with and without --dir
  • create custom file protocol

As a result either a Not allowed to load local resource or the file shown as not found in the requests of the app.