Having trouble with a text formatting function

Probably some easy rep points here.

I have this method:

function formatText(input) {
    return input
        .replaceAll(
            /[#[0-9a-fA-F]{3,6}]/g,
            (match) => `<span style="color:${match.slice(1, -1)}">`
        )
        .replaceAll(/[b]/g, '<b>')
        .replaceAll(/[i]/g, '<i>')
        .replaceAll(/[u]/g, '<u>')
        .replaceAll(/[!b]/g, '</b>')
        .replaceAll(/[!i]/g, '</i>')
        .replaceAll(/[!u]/g, '</u>')
        .replaceAll(/[!c]/g, '</span>');
}

It’s intended to be a simple formatting language. However, when I put any text through it (even without formatting) it doesn’t print and raises an error.

Here’s my full code:

<!doctype html>
<html lang="en">

<head>
    <title>ECMAtermOS</title>
    <link rel="stylesheet" href="style.css">
    <script src="jquery.js" defer></script>
    <script src="ecmaterm.js" defer></script>
    <meta charset="utf-8">
</head>

<body>
    <script defer>
        addEventListener("DOMContentLoaded", _ => {
            const term = new ECMAterm();
            term.newTerm('body');
            term.print(['[#0f0]test[!c]']);
            term.print(['test 2']);
    });
    </script>
</body>

</html>
class ECMAterm {
    constructor() {
        this.terminal = /*html*/ `
            <pre><code id="terminal">ECMAtermOS v1.0.0-alpha.1</code></pre>
        `;
    }

    newTerm(containerSel) {
        this.instance = $(containerSel).append(this.terminal);
        this.output = $('#terminal');
    }

    print(args, type = 'text') {
        function formatText(input) {
            return input
                .replaceAll(
                    /[#[0-9a-fA-F]{3,6}]/g,
                    (match) => `<span style="color:${match.slice(1, -1)}">`
                )
                .replaceAll(/[b]/g, '<b>')
                .replaceAll(/[i]/g, '<i>')
                .replaceAll(/[u]/g, '<u>')
                .replaceAll(/[!b]/g, '</b>')
                .replaceAll(/[!i]/g, '</i>')
                .replaceAll(/[!u]/g, '</u>')
                .replaceAll(/[!c]/g, '</span>');
        }

        for (const arg of args) {
            arg = formatText(arg);

            this.output.append(/*html*/ `<div class=${type}>${arg}</div>`);
        }
    }
}

LLMs can’t seem to fix it, which leads me to think it’s a stupid mistake I made somewhere, but I can’t find it.

Here’s how I’m using it:

addEventListener("DOMContentLoaded", _ => {
    const term = new ECMAterm();
    term.newTerm('body');
    term.print(['[#0f0]test[!c]']); // Should return <span style="color:#0f0">test</span>
    term.print(['test 2']); // Should return itself
});

Neither of the strings print.

EDIT:
Note – if I don’t run arg through formatText() it prints as normal

Playing MP3 Stream in Javascript

I have a POST endpoint that returns an MP3 stream, and I want to play it in JavaScript using the native browser fetch API. The goal is to implement a cross-browser solution, but so far, nothing works in Firefox. Since I need to use a POST endpoint, using new Audio(url) isn’t an option. To make this easier to reproduce, I will use a GET endpoint for a random radio station that streams MP3, but the issue remains the same.

What I’ve tried:

  1. Using MediaSource
const playMP3Stream = async () => {
  const audio = new Audio();
  const mediaSource = new MediaSource();
  audio.src = URL.createObjectURL(mediaSource);

  mediaSource.onsourceopen = async () => {
    const sourceBuffer = mediaSource.addSourceBuffer("audio/mpeg");

    const response = await fetch("http://sc6.radiocaroline.net:8040/mp3");

    if (!response.ok) {
      return;
    }

    const reader = response.body?.getReader();

    if (!reader) return;

    while (mediaSource.readyState === "open") {
      const { value, done } = await reader.read();

      if (done) {
        mediaSource.endOfStream();
        break;
      }
      sourceBuffer.appendBuffer(value);
    }
  };

  await audio.play();
};
  1. Using AudioContext
const playMP3Stream = async () => {
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();

  const response = await fetch('http://sc6.radiocaroline.net:8040/mp3');
  const reader = response.body.getReader();

  let streamBuffer = new Uint8Array();
  let startTime = audioContext.currentTime;

  async function processChunk() {
    const { done, value } = await reader.read();

    if (done) {
      return;
    }

    try {
      const newBuffer = new Uint8Array(streamBuffer.length + value.length);
      newBuffer.set(streamBuffer);
      newBuffer.set(value, streamBuffer.length);
      streamBuffer = newBuffer;

      if (streamBuffer.length > 10000) {
        const audioBuffer = await audioContext.decodeAudioData(
          streamBuffer.buffer,
        );
        const source = audioContext.createBufferSource();
        source.buffer = audioBuffer;
        source.connect(audioContext.destination);
        source.start(startTime);
        startTime += audioBuffer.duration;

        streamBuffer = new Uint8Array();
      }
    } catch (error) {
      console.error('Error decoding audio chunk:', error);
    }

    processChunk();
  }

  processChunk();
}

MediaSource works well in Chromium browsers, but unfortunately, the "audio/mpeg" MIME type is not supported in Firefox. The AudioContext approach works to some extent, but there are gaps between chunks, and it still only functions in Chromium browsers. I’m not sure if this is particularly difficult, or if I’m just struggling with the implementation, but I can’t figure out how to play an MP3 audio stream in Firefox.

I would greatly appreciate any help, whether it’s a working example of gapless MP3 streaming in Firefox or anything else that could help me make this work.

Thanks in advance!

Custom vue component library fails to resolve vuetify components when installed

I’m making a component library for vue.js components that I’ve created that I’d like to reuse. It’s my first time making a library. I’ve run the library locally and “installed” it locally in another project using ‘npm link’ successfully. But when I actually download the published package it can’t seem to resolve the vuetify components that I’ve used to create my first component. I get the following error:

[Vue warn]: Failed to resolve component: v-autocomplete
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
  at <EmployeeAutocomplete> 
  at <Tester> 
  at <About onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouterView> 
  at <VMain> 
  at <VLayout> 
  at <Default onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouterView> 
  at <VApp> 
  at <App>

The component library can be found here
I’m using a default vuetify project to test it. That’s here

I will try to publish the package itself to my npm this evening, I think I’m blocked at work.

How to Embed a Sketchfab 3D Model in a React JSX Hero Section with Proper Sizing?

I’m trying to embed a Sketchfab 3D model in my React JSX hero section, but every time I do, the model appears extremely small—like a single pixel.
However, the model remains tiny. How can I properly size and scale the 3D model inside my hero section so it fits well and is fully visible?
https://sketchfab.com/3d-models/traveler-f86f5f2a779d496c95c9aca8ba13246f#comments
im trying to use that exact model directed by the link. Other models appear fine but not this.

Any help is appreciated!

import React, { Suspense, useRef, useEffect, useState } from "react";
import { Canvas, useFrame, useLoader, useThree } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls, Environment, Html, useGLTF } from "@react-three/drei";
import { Box3, Vector3 } from "three";
import * as THREE from 'three';

// Model component that loads and animates the 3D model
function Model() {
  const gltf = useLoader(GLTFLoader, "/traveler.glb");
  const modelRef = useRef();
  const mixer = useRef();
  const { camera } = useThree();

  useEffect(() => {
    if (!gltf || !modelRef.current) return;

    // Setup animation mixer
    if (gltf.animations.length) {
      mixer.current = new THREE.AnimationMixer(gltf.scene);
      gltf.animations.forEach((clip) => {
        mixer.current.clipAction(clip).play();
      });
    }

    // Auto-scale and center model
    const box = new THREE.Box3().setFromObject(modelRef.current);
    const center = box.getCenter(new THREE.Vector3());
    const size = box.getSize(new THREE.Vector3());

    const maxDim = Math.max(size.x, size.y, size.z);
    const fov = camera.fov * (Math.PI / 180);
    const distance = Math.abs(maxDim / Math.sin(fov / 2));

    camera.position.set(center.x, center.y, distance * 1.5);
    camera.lookAt(center);

    const scaleFactor = 2 / maxDim;
    modelRef.current.scale.setScalar(scaleFactor);
    modelRef.current.position.set(-center.x, -center.y, -center.z);
  }, [gltf]);

  useFrame((_, delta) => {
    if (mixer.current) mixer.current.update(delta);
  });

  return <primitive ref={modelRef} object={gltf.scene} />;
}

// Loader component for displaying during model loading
function Loader() {
  return (
    <Html center>
      <div style={{ color: "white", display: "flex", flexDirection: "column", alignItems: "center" }}>
        <div className="loader"></div>
        <p>Loading 3D model...</p>
      </div>
    </Html>
  );
}

// Main component
const HeroModel3D = () => {
  return (
    <div className="hero-model-3d">
      <Canvas
        camera={{ position: [0, 0, 10], fov: 50 }}
        style={{ background: "transparent" }}
      >
        <ambientLight intensity={0.8} />
        <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} intensity={1} castShadow />
        <Suspense fallback={<Loader />}>
          <Model />
          <Environment preset="city" />
          <OrbitControls 
            enableZoom={true}
            enablePan={true}
            rotateSpeed={0.5}
            autoRotate={false}
          />
        </Suspense>
      </Canvas>
    </div>
  );
};

export default HeroModel3D;

this is my inspo
This is my inspo

Intersection observer aggressively batching entries in safari IOS

In the following (pretty basic) implementation of intersection observer, I’m noticing that IOS safari is batching the entries very aggressively in a single callback: if I scroll through the six items my observer is watching very quickly, the callback only fires once with six entries, whereas on desktop the callback fires six times, once for each target.

Is this just a safari problem, or is there something I’m doing wrong in my code which could be contributing to this?

Minimum reproducible example:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        window.addEventListener('load',function(){
            let steps=[...document.querySelectorAll('.step')];

            const observerOptions = {
                threshold: [0,1],
                rootMargin: `0px 0px 0px 0px`,
                root: document
            };

            let observer = new IntersectionObserver((entries) => {
                console.log('callback-------------------------')
                console.log('batched entries:',entries.length)
                let active=null;
                for (let entry of entries) {
                    if(entry.intersectionRatio==1){
                        active=entry.target;
                    }
                }
                console.log('detected fully in view:',active);
                if(active){
                    for(let step of steps){
                        step.classList.toggle('active',active==step);
                    }
                }

            }, observerOptions);

            for(let step of steps) observer.observe(step);
        })
    </script>
    <style>
        .step-outer{
            height:60svh;
            width:100%;
            position:relative;
        }

        .step{
            position:absolute;
            top:0;
            left:0;
            height:100%;
            width:100%;
            box-sizing:border-box;
            border-bottom:1px solid red;
            transition:background-color 0.3s;
        }

        .step.active{
            background-color: coral;
        }

        .outer{
            margin-top:100px;
            margin-bottom:100px;
        }
    </style>
  </head>
  <body>
    <div class="outer">
        <div class="step-outer"><div class="step"></div></div>
        <div class="step-outer"><div class="step"></div></div>
        <div class="step-outer"><div class="step"></div></div>
        <div class="step-outer"><div class="step"></div></div>
        <div class="step-outer"><div class="step"></div></div>
        <div class="step-outer"><div class="step"></div></div>
    </div>
    
  </body>
</html>

How to implement a Dropzone for folders in a React/Electron app and process the folder contents?

Is there a way in electron based apps to let a user drop a folder onto a React-component and handle the drop event so that the main process can fully access it and recursively scan the folder’s contents?

My setup so far:

  • the Renderer has a <DropZone> component with onDrop and onDragOver.
  • the drop event fires and i can see event.dataTransfer.files, but the path (webkitRelativePath) for dropped folders is empty also for files.
const folderDropped = async (event: React.DragEvent) => {
  event.preventDefault();

  // If a folder or file has been dragged
  if (event.dataTransfer.files.length > 0) {
    const filePath = event.dataTransfer.files[0].path;
    console.log("Drop event:", event.dataTransfer.files);
    console.log("Dropped file path:", filePath);

    // Call the IPC handler: readDroppedFile
    const result = await window.api.readDroppedFile(filePath, browserWindowId);
    console.log("readDroppedFile result:", result);

     /* Further Process:
       After the api call, the object is checked if its a folder and that it contains files
       of a specific type (for example, only PNG files). Once confirmed, the main process will scan through all the
       files within the folder recursively.
    */
  }
};

Visual Studio Code, which is also built with Electron, can accept dropped files and folders from the Explorer, so it should be possible for my app as well.

Additional details (optional):

  • Electron version: 35
  • React version: 19
  • Windows 11

How to properly simulate electric field lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="700" height="500"></canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

function vec3_add(a, b) {
   return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
}

function vec3_sub(a, b) {
   return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
}

function vec3_scale(v, s) {
   return [v[0] * s, v[1] * s, v[2] * s];
}

function vec3_magnitude(v) {
   return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

function vec3_normalize(v) {
   const mag = vec3_magnitude(v);
   if (mag === 0) return [0, 0, 0];
   return [v[0] / mag, v[1] / mag, v[2] / mag];
}
const charges = [{
       pos: [250, 250, 0],
       id: "negative"
   },
   {
       pos: [450, 250, 0],
       id: "positive"
   }
];
const lines = 16;
const step_size = 4;
const max_steps = 1000;

function calculate_field_at_point(point, charges) {
   let field = [0, 0, 0];
   for (let charge of charges) {
       let dir = vec3_sub(charge.pos, point);
       let dist = vec3_magnitude(dir);
       if (dist < 5) continue;
       let strength = charge.id.includes("negative") ? 1 : -1;
       strength /= (dist * dist);
       field = vec3_add(field, vec3_scale(dir, strength));
   }
   return vec3_normalize(field);
}

function rk4_step(point, charges) {
   let k1 = calculate_field_at_point(point, charges);
   let temp = vec3_add(point, vec3_scale(k1, step_size * 0.5));
   let k2 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k2, step_size * 0.5));
   let k3 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k3, step_size));
   let k4 = calculate_field_at_point(temp, charges);
   return vec3_scale(
       vec3_add(
           vec3_add(
               vec3_scale(k1, 1 / 6),
               vec3_scale(k2, 1 / 3)
           ),
           vec3_add(
               vec3_scale(k3, 1 / 3),
               vec3_scale(k4, 1 / 6)
           )
       ),
       step_size
   );
}

function generate_circle_points(center, radius, num_points) {
   let points = [];
   for (let i = 0; i < num_points; i++) {
       let angle = (i / num_points) * Math.PI * 2.0;
       let x = center[0] + radius * Math.cos(angle);
       let y = center[1] + radius * Math.sin(angle);
       points.push([x, y, 0]);
   }
   return points;
}

function integrate_field_line(start_point, charges) {
   let points = [start_point];
   let current_point = [...start_point];
   for (let i = 0; i < max_steps; i++) {
       let step = rk4_step(current_point, charges);
       if (vec3_magnitude(step) < 0.1) break;
       current_point = vec3_add(current_point, step);
       points.push([...current_point]);
       let too_close = charges.some(charge => vec3_magnitude(vec3_sub(current_point, charge.pos)) < 15);
       if (too_close) break;
       if (current_point[0] < 0 || current_point[0] > canvas.width ||
           current_point[1] < 0 || current_point[1] > canvas.height) break;
   }
   return points;
}
const positive_charges = charges.filter(c => c.id.includes("positive"));
for (let charge of positive_charges) {
   let start_points = generate_circle_points(charge.pos, 35, lines);
   for (let start_point of start_points) {
       let points = integrate_field_line(start_point, charges);
       if (points.length > 5) {
           ctx.beginPath();
           ctx.moveTo(points[0][0], points[0][1]);
           for (let i = 1; i < points.length; i++) {
               ctx.lineTo(points[i][0], points[i][1]);
           }
           ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
           ctx.lineWidth = 2;
           ctx.stroke();
       }
   }
}
ctx.beginPath();
ctx.arc(charges[0].pos[0], charges[0].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[0].pos[0] - 15, charges[0].pos[1]);
ctx.lineTo(charges[0].pos[0] + 15, charges[0].pos[1]);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.arc(charges[1].pos[0], charges[1].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[1].pos[0] - 15, charges[1].pos[1]);
ctx.lineTo(charges[1].pos[0] + 15, charges[1].pos[1]);
ctx.moveTo(charges[1].pos[0], charges[1].pos[1] - 15);
ctx.lineTo(charges[1].pos[0], charges[1].pos[1] + 15);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
</script>
</body>
</html>

I am trying to simulate an electric field between two charged particles.
To do I sample points around the positive charge, take a series of small steps and approximate the direction of the electric field each time using Runge–Kutta.

However in my snippet the field lines cannot reach behind the negative charge on the left.

I tried generating lines from negative to positive charges but lines start colliding and it looks ugly.

Basically this is what I have:
enter image description here

And what I want:
enter image description here

Hwo to proprely simulate electric field lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="700" height="500"></canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

function vec3_add(a, b) {
   return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
}

function vec3_sub(a, b) {
   return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
}

function vec3_scale(v, s) {
   return [v[0] * s, v[1] * s, v[2] * s];
}

function vec3_magnitude(v) {
   return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

function vec3_normalize(v) {
   const mag = vec3_magnitude(v);
   if (mag === 0) return [0, 0, 0];
   return [v[0] / mag, v[1] / mag, v[2] / mag];
}
const charges = [{
       pos: [250, 250, 0],
       id: "negative"
   },
   {
       pos: [450, 250, 0],
       id: "positive"
   }
];
const lines = 16;
const step_size = 4;
const max_steps = 1000;

function calculate_field_at_point(point, charges) {
   let field = [0, 0, 0];
   for (let charge of charges) {
       let dir = vec3_sub(charge.pos, point);
       let dist = vec3_magnitude(dir);
       if (dist < 5) continue;
       let strength = charge.id.includes("negative") ? 1 : -1;
       strength /= (dist * dist);
       field = vec3_add(field, vec3_scale(dir, strength));
   }
   return vec3_normalize(field);
}

function rk4_step(point, charges) {
   let k1 = calculate_field_at_point(point, charges);
   let temp = vec3_add(point, vec3_scale(k1, step_size * 0.5));
   let k2 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k2, step_size * 0.5));
   let k3 = calculate_field_at_point(temp, charges);
   temp = vec3_add(point, vec3_scale(k3, step_size));
   let k4 = calculate_field_at_point(temp, charges);
   return vec3_scale(
       vec3_add(
           vec3_add(
               vec3_scale(k1, 1 / 6),
               vec3_scale(k2, 1 / 3)
           ),
           vec3_add(
               vec3_scale(k3, 1 / 3),
               vec3_scale(k4, 1 / 6)
           )
       ),
       step_size
   );
}

function generate_circle_points(center, radius, num_points) {
   let points = [];
   for (let i = 0; i < num_points; i++) {
       let angle = (i / num_points) * Math.PI * 2.0;
       let x = center[0] + radius * Math.cos(angle);
       let y = center[1] + radius * Math.sin(angle);
       points.push([x, y, 0]);
   }
   return points;
}

function integrate_field_line(start_point, charges) {
   let points = [start_point];
   let current_point = [...start_point];
   for (let i = 0; i < max_steps; i++) {
       let step = rk4_step(current_point, charges);
       if (vec3_magnitude(step) < 0.1) break;
       current_point = vec3_add(current_point, step);
       points.push([...current_point]);
       let too_close = charges.some(charge => vec3_magnitude(vec3_sub(current_point, charge.pos)) < 15);
       if (too_close) break;
       if (current_point[0] < 0 || current_point[0] > canvas.width ||
           current_point[1] < 0 || current_point[1] > canvas.height) break;
   }
   return points;
}
const positive_charges = charges.filter(c => c.id.includes("positive"));
for (let charge of positive_charges) {
   let start_points = generate_circle_points(charge.pos, 35, lines);
   for (let start_point of start_points) {
       let points = integrate_field_line(start_point, charges);
       if (points.length > 5) {
           ctx.beginPath();
           ctx.moveTo(points[0][0], points[0][1]);
           for (let i = 1; i < points.length; i++) {
               ctx.lineTo(points[i][0], points[i][1]);
           }
           ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
           ctx.lineWidth = 2;
           ctx.stroke();
       }
   }
}
ctx.beginPath();
ctx.arc(charges[0].pos[0], charges[0].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[0].pos[0] - 15, charges[0].pos[1]);
ctx.lineTo(charges[0].pos[0] + 15, charges[0].pos[1]);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.arc(charges[1].pos[0], charges[1].pos[1], 30, 0, Math.PI * 2);
ctx.fillStyle = "red";
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "black";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(charges[1].pos[0] - 15, charges[1].pos[1]);
ctx.lineTo(charges[1].pos[0] + 15, charges[1].pos[1]);
ctx.moveTo(charges[1].pos[0], charges[1].pos[1] - 15);
ctx.lineTo(charges[1].pos[0], charges[1].pos[1] + 15);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
</script>
</body>
</html>

I am trying to simulate an electric field between two charged particles.
To do I sample points around the positive charge, take a series of small steps and approximate the direction of the electric field each time using Runge–Kutta.

However in my snippet the field lines cannot reach behind the negative charge on the left.

I tried generating lines from negative to positive charges but lines start colliding and it looks ugly.

Basically this is what I have:
enter image description here

And what I want:
enter image description here

Vite React Django5 allauth jwt google login

My google login code seems to be working but does redirect back to the home page. the access and refresh token are in the browser application but it still wont redirect back to the home page, it keeps going to the login page. but if i enter the home page url directly it shows authenticated. Google is sending the code and token and this is being swapped into jwt tokens

import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import Cookies from 'js-cookie'; // Import js-cookie

const GoogleCallback = () => {
    const navigate = useNavigate();

    useEffect(() => {
        const handleCallback = async () => {
            try {
                await axios.get(
                    "http://localhost:8000/api/google/callback/" + window.location.search,
                    { withCredentials: true } // Ensure cookies are sent & received
                );

                // Check for both tokens using js-cookie with domain and path
                const accessToken = Cookies.get('access_token', { domain: '127.0.0.1', path: '/' });
                const refreshToken = Cookies.get('refresh_token', { domain: '127.0.0.1', path: '/' });

                if (accessToken && refreshToken) {
                    navigate("/");
                } else {
                    console.warn("Access token or refresh token not found in cookies under 127.0.0.1:5173");
                    navigate("/login");
                }


            } catch (error) {
                console.error("Google login failed", error);
                navigate("/login");
            }
        };

        handleCallback();
    }, [navigate]);

    return <h2>Logging in...</h2>;
};

export default GoogleCallback;
from django.shortcuts import redirect
from django.http import JsonResponse
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from allauth.socialaccount.models import SocialAccount, SocialToken
from rest_framework_simplejwt.tokens import RefreshToken
from datetime import timedelta
from django.utils.timezone import now
from django.core.exceptions import ObjectDoesNotExist

User = get_user_model()

def google_login(request):
    """Redirects the user to Google's OAuth 2.0 authentication page."""
    google_oauth_url = (
        "https://accounts.google.com/o/oauth2/auth"
        "?response_type=code"
        f"&client_id={settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID}"
        f"&redirect_uri={settings.SOCIAL_AUTH_GOOGLE_REDIRECT_URI}"  
        "&scope=email%20profile%20openid"
        "&access_type=offline"
        "&prompt=consent"
    )
    return redirect(google_oauth_url)

def google_login_callback(request):
    """Handles OAuth callback, exchanges code for Google tokens, and generates JWTs."""
    code = request.GET.get('code')

    if not code:
        return JsonResponse({'error': 'Missing authorization code'}, status=400)

    # Exchange code for access & refresh tokens
    token_url = "https://oauth2.googleapis.com/token"
    data = {
        'code': code,
        'client_id': settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID,
        'client_secret': settings.SOCIAL_AUTH_GOOGLE_SECRET,
        'redirect_uri': settings.SOCIAL_AUTH_GOOGLE_REDIRECT_URI,
        'grant_type': 'authorization_code',
    }
    response = requests.post(token_url, data=data)
    token_data = response.json()

    if 'error' in token_data:
        return JsonResponse({'error': 'Failed to fetch tokens', 'details': token_data}, status=400)

    access_token = token_data.get('access_token')
    refresh_token = token_data.get('refresh_token')
    id_token = token_data.get('id_token')

    # Fetch user info from Google
    user_info_url = "https://www.googleapis.com/oauth2/v2/userinfo"
    headers = {'Authorization': f'Bearer {access_token}'}
    user_info_response = requests.get(user_info_url, headers=headers)
    user_info = user_info_response.json()

    if 'email' not in user_info:
        return JsonResponse({'error': 'Google authentication failed'}, status=400)

    email = user_info['email']

    # Check if user exists, create if not
    user, created = User.objects.get_or_create(username=email, defaults={'email': email})

    # Store Google tokens in SocialToken model
    try:
        social_account = SocialAccount.objects.get(user=user, provider='google')
    except ObjectDoesNotExist:
        social_account = SocialAccount.objects.create(user=user, provider='google', extra_data=user_info)

    social_token, _ = SocialToken.objects.update_or_create(
        account=social_account,
        defaults={'token': access_token, 'token_secret': refresh_token, 'expires_at': now() + timedelta(seconds=token_data.get("expires_in", 3600))}
    )

    # Generate JWT tokens
    refresh = RefreshToken.for_user(user)
    access = refresh.access_token

    # Set JWT tokens as secure HTTP-only cookies
    response = JsonResponse({'message': 'Login successful'})
    response.set_cookie('access_token', str(access), httponly=True, secure=True, samesite='Lax')
    response.set_cookie('refresh_token', str(refresh), httponly=True, secure=True, samesite='Lax')

    return response

def refresh_google_token(user):
    """Refreshes the user's Google OAuth token if expired."""
    try:
        social_account = SocialAccount.objects.get(user=user, provider='google')
        social_token = SocialToken.objects.get(account=social_account)

        if social_token.expires_at and social_token.expires_at < now():
            token_url = "https://oauth2.googleapis.com/token"
            data = {
                'client_id': settings.SOCIAL_AUTH_GOOGLE_CLIENT_ID,
                'client_secret': settings.SOCIAL_AUTH_GOOGLE_SECRET,
                'refresh_token': social_token.token_secret,
                'grant_type': 'refresh_token',
            }
            response = requests.post(token_url, data=data)
            new_token_data = response.json()

            if 'access_token' in new_token_data:
                social_token.token = new_token_data['access_token']
                social_token.expires_at = now() + timedelta(seconds=new_token_data.get("expires_in", 3600))
                social_token.save()
                return new_token_data['access_token']
            else:
                return None
        return social_token.token
    except ObjectDoesNotExist:
        return None
from rest_framework_simplejwt.tokens import AccessToken

def verify_user(request):
    """Verifies the user's authentication using JWT stored in cookies."""
    access_token = request.COOKIES.get("access_token")

    if not access_token:
        return JsonResponse({'authenticated': False, 'error': 'No access token'}, status=401)

    try:
        payload = AccessToken(access_token)
        user = User.objects.get(id=payload['user_id'])
        return JsonResponse({'authenticated': True, 'email': user.email})
    except Exception:
        return JsonResponse({'authenticated': False, 'error': 'Invalid or expired token'}, status=401)
import { useEffect, useState } from "react";
import { verifyUser, refreshGoogleToken } from "./api";
import { useNavigate } from "react-router-dom";

const Home = () => {
    const [user, setUser] = useState(null);
    const navigate = useNavigate();

    useEffect(() => {
        const checkUser = async () => {
            const data = await verifyUser();
            if (data.authenticated) {
                setUser(data);
            } else {
                console.warn("User verification failed:", data.error);
                navigate("/login"); // Redirect immediately if not authenticated - Removed setTimeout delay
            }
        };

        checkUser();
    }, [navigate]);

    return (
        <div>
            {user ? (
                <div>
                    <h2>Welcome, {user.email}</h2>
                </div>
            ) : (
                <h2>Loading...</h2>
            )}
        </div>
    );
};

export default Home;

Browser Cookie

Jumps and lags when switching between sections (ScrollTrigger)

I am new to frontend and have started using gsap for animations. I use gsap mainly for switching between sections. On screens larger than 576px I have a problem: when switching between sections there are jumps and they disappear after a while if you scroll up and down the page. Maybe someone has encountered this ?
I don’t seem to have a lot of animations so far, but already so laggy.

Here’s what I tried to do:

  1. Remove all possible negative margins.

  2. Add to all elements to which the transition is made, properties:

    transform: translate3d(0,0,0,);
    will-change: transform;
    backface-visibility: hidden;

  3. Set property ease – sine, power1, power2.

All this had no effect.

In the mobile version I have a lot of animation that works perfectly with gsap.

    
        <iframe height="300" style="width: 100%;" scrolling="no" title="Biorytm" src="https://codepen.io/bcnngjlt-the-scripter/embed/MYWvPvJ?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
          See the Pen <a href="https://codepen.io/bcnngjlt-the-scripter/pen/MYWvPvJ">
          Biorytm</a> by Яша Проценко (<a href="https://codepen.io/bcnngjlt-the-scripter">@bcnngjlt-the-scripter</a>)
          on <a href="https://codepen.io">CodePen</a>.
        </iframe>

react.js make component persist on specific route

I am rendering a component like this in Approuter.js on element document.getElementById("side-bar"); , element is in wordpress homepage

When I click and route to /deals , the component renders but disappears right away in 1 sec . How do I modify it so the component stays/persist as long as user is on /deals

  import React, { useEffect, useState } from "react";
import { Routes, Route, useLocation } from "react-router-dom";
import { createPortal } from "react-dom";  
...

export const AppRouter = () => {
    const location = useLocation();
    const [categories, setCategories] = useState({});
    const [sidebarContainer, setSidebarContainer] = useState(null);

    useEffect(() => {
        // Fetch categories once when the app loads
        fetchCategories().then((data) => {
            if (typeof data === "object" && data !== null) {
                setCategories(data);
            } else {
                console.error("Invalid category format:", data);
                setCategories({});
            }
        });
    }, []);

    useEffect(() => {

        // Find the sidebar container from the WordPress PHP homepage

        const container = document.getElementById("side-bar");
        setSidebarContainer(container);
    }, []); // Run once on mount

    return (
        <div className="app-wrapper">
            <Header />

            <div className="content-wrapper">
                <div className="page-container">
                    <main className="main-content">
                        <Routes>
                            <Route
                                path="/"
                                element={
                                    <>
                                    ...
                                    </>
                                }
                            />
                            ..
                            <Route path="/deals" element={<DealsList />} />
                        </Routes>
                    </main>
                </div>

                {/*  Inject Sidebar into WordPress' existing #side-bar */}
                {location.pathname === "/deals" && sidebarContainer
                    ? createPortal(<Sidebar categories={categories} />, sidebarContainer)
                    : null}
            </div>

            <Footer />
        </div>
    );
};

Remove Zoom Controls from Google Streetview

I have a Google Map implemented with basic Javascript, where a toggle between Streetview and the normal View. I am NOT talking about remove StreetView Toggles/Controls in normal views.

I want to hide the zoomControls when the StreetView is active. But the config is not working for the zoomControls. It looks like, only some of the options are working (like enableCloseButton).

It’s working for the normal view, but not explicit within the StreetView.

marked plus and minus controls on the streetview

Jsfiddle:

https://jsfiddle.net/Maybach/3j75f2w9/7/
(its just cloned from their official documentation and i added the options object)

HTML:

<!doctype html>
<!--
 @license
 Copyright 2019 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->
<html>
  <head>
    <title>Disabling the Default UI</title>

    <!-- jsFiddle will insert css and js -->
  </head>
  <body>
    <div id="map"></div>

    <!-- 
      The `defer` attribute causes the script to execute after the full HTML
      document has been parsed. For non-blocking uses, avoiding race conditions,
      and consistent behavior across browsers, consider loading using Promises. See
      https://developers.google.com/maps/documentation/javascript/load-maps-js-api
      for more information.
      -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>

Javascript:

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
let panorama

function initMap() {
  const astorPlace = { lat: 40.729884, lng: -73.990988 }
  // Set up the map
  const map = new google.maps.Map(document.getElementById("map"), {
    center: astorPlace,
    zoom: 18,
    streetViewControl: false,
  })

  document.getElementById("toggle").addEventListener("click", toggleStreetView)


  // We get the map's default panorama and set up some defaults.
  // Note that we don't yet set it visible.
  panorama = map.getStreetView() // TODO fix type
  panorama.setPosition(astorPlace)
  panorama.setPov(
    /** @type {google.maps.StreetViewPov} */ {
      heading: 265,
      pitch: 0,
    },
  )
}

function toggleStreetView() {
  const toggle = panorama.getVisible()
  panorama.setOptions({
    disableDefaultUI: true,
    enableCloseButton: true,
    linksControl: false,
    panControl: false,
    zoomControl: false,
    scrollwheel: false,
    fullscreenControl: false,
    motionTracking: false,
    motionTrackingControl: false,
    addressControl: false,
    clickToGo: false,
    showRoadLabels: false,
  })
  if (toggle == false) {
    panorama.setVisible(true)
  } else {
    panorama.setVisible(false)
  }
}

window.initMap = initMap

CSS:

/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
/* 
 * Always set the map height explicitly to define the size of the div element
 * that contains the map. 
 */
#map {
  height: 100%;
}

/* 
 * Optional: Makes the sample page fill the window. 
 */
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#floating-panel {
  position: absolute;
  top: 10px;
  left: 25%;
  z-index: 5;
  background-color: #fff;
  padding: 5px;
  border: 1px solid #999;
  text-align: center;
  font-family: "Roboto", "sans-serif";
  line-height: 30px;
  padding-left: 10px;
}

#floating-panel {
  margin-left: -100px;
}

  1. I’ve read the official documentation about the StreetView options
  2. I added the options object to remove the zoomControls: false as its working within the normal view.

I was expecting the zoomControls in the bottom right corner (the + & -) will be hidden, when the streetView is activated.