Updating attribute of TipTap node

I’m using TipTap 2.12.0.

I’m trying to update the Heading extension so it adds a name attribute with a slugified version of the content of the heading so I can add anchor links that jump to a section in my document.

My extension looks like this:

export function slugify(text) {
  return text
    .toString()
    .toLowerCase()
    .trim()
    .replace(/s+/g, "-") // Replace spaces with -
    .replace(/[^w-]+/g, "") // Remove all non-word chars
    .replace(/--+/g, "-") // Replace multiple - with single -
    .replace(/^-+/, "") // Trim - from start of text
    .replace(/-+$/, ""); // Trim - from end of text
}


const CustomHeading = Heading.extend({
  renderHTML({ node, HTMLAttributes }) {
    HTMLAttributes["name"] = slugify(node.textContent);
    return this.parent?.({ node, HTMLAttributes });
  },
});

It works well when creating new header. However, I need to update this so it updates the value of the name attribute when the user changes the content of the heading.

I’ve tried to do this by adding onUpdate, however I can’t figure out how to get the updated content and change the attribute for the node being updated.

Anyone have any ideas?

Carousel with infinity scroll have double interval. How fix that on native js?

I wrote a slide with infinite scrolling:

  1. I clone the slides
  2. I get the id of the active one,
  3. Move the container so that the active slide is in the center,
  4. add autoscroll to the slides.

Now I have a problem that after a full
cycle, when switching from the cloned slide 1 to the original slide
1, there is a double delay before switching to slide 2
. This happens
because I remove the container blending animation and instantly
switch to the original slide 1, but the interval is not reset. I
tried resetting the interval but it didn’t work for me, so I did
something wrong. Since I don’t have enough knowledge to fix this, I
ask you to help fix this behavior. Why wasn’t there a double delay
when switching from the cloned slide 1 to the original slide 1 and
then to slide 2.

    // DOM
    const slider = document.querySelector(".slider");
    const sliderContainer = document.querySelector(".slider__track");
    let slides = Array.from(document.querySelectorAll(".slider__slide"));
    
    // Values
    const slidesPerView = 3;
    let activeSlideID = 0;
    let calculatedPos;
    
    // Functions
    const cloneSlides = function () {
       // clone slides and delete attribute 'data-active'
       const firstSlides = getCuttedSlidesArr("firstSlides", slides).map((slide) => cloneSlideWithoutAttr(slide));
       const lastSlides = getCuttedSlidesArr("lastSlides", slides).map((slide) => cloneSlideWithoutAttr(slide));
    
       // add clone to the beginning of "container" and to the end
       sliderContainer.append(...firstSlides);
       sliderContainer.prepend(...lastSlides);
    
       slides = Array.from(document.querySelectorAll(".slider__slide"));
    };
    
    function getCuttedSlidesArr(type, array) {
       if (type === "firstSlides") {
          return array.slice(0, slidesPerView);
       } else if (type === "lastSlides") {
          return array.slice(-slidesPerView);
       }
    }
    
    function cloneSlideWithoutAttr(slide) {
       const clonedSlide = slide.cloneNode(true);
       clonedSlide.removeAttribute("data-active");
       return clonedSlide;
    }
    
    function getIdFromActiveSlide() {
       for (let i = 0; i < slides.length; i++) {
          if (slides[i].hasAttribute("data-active")) {
             activeSlideID = i;
          }
       }
    }
    
    function centerActiveSlide() {
       const sliderWidth = slider.offsetWidth;
    
       const activeSlideWidth = slides[activeSlideID].offsetWidth;
       const activeSlideOffset = slides[activeSlideID].offsetLeft;
    
       const calculatedPos = sliderWidth / 2 - (activeSlideOffset + activeSlideWidth / 2);
    
       sliderContainer.style.transform = `translateX(${calculatedPos}px)`;
       sliderContainer.style.transition = `all`;
    }
    
    function autoChangeSlides() {
       if (activeSlideID < slides.length) {
          activeSlideID++;
          centerActiveSlide();
          sliderContainer.style.transform = `translateX(${calculatedPos}px)`;
          sliderContainer.style.transition = `0.5s ease`;
       }
    
       if (activeSlideID > slides.length - slidesPerView) {
          activeSlideID = slidesPerView;
          centerActiveSlide();
       }
    }
    
    // Call
    cloneSlides();
    getIdFromActiveSlide();
    centerActiveSlide();
    
    const autoChangeInterval = setInterval(autoChangeSlides, 1000);

here is link to codeopen for better understand: https://codepen.io/plupiks/pen/ZYYqpae

Website refreshes when executing SQLite DB command in Fastify backend (Nodemon + WebSocket issue)

I’m working on a school project where I built a ping pong game using JavaScript/TypeScript on the frontend, a Fastify backend, and SQLite3 as the database.

When a game ends, I want to store the game data in the database using the following code:

db.prepare(`
  CREATE TABLE IF NOT EXISTS games (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_name VARCHAR(100) NOT NULL,
    match_id VARCHAR(100) NOT NULL,
    player_id INTEGER NOT NULL,
    left_player_score INTEGER NOT NULL,
    right_player_score INTEGER NOT NULL,
    game_duration INTEGER NOT NULL,
    game_end_result VARCHAR(100) NOT NULL,
    left_player_ball_hit INTEGER NOT NULL,
    right_player_ball_hit INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  )
`).run();

The first time this runs, the website refreshes and starts a new game, and the data that gets stored is incomplete. It seems that executing this DB command causes the page to reload and reestablish a WebSocket connection, starting a new game.

I suspected that nodemon was watching the intro.db file (the SQLite DB), so I added this to my package.json:

"dev": "nodemon --watch src --ignore src/db/intro.db"

But this did not fix the problem. It still behaves the same way.

Question:
Why does my site refresh and restart the game when the DB is modified, and how can I prevent this? Is this a nodemon config issue, or something else related to WebSocket or Fastify?

Any help is appreciated!

Scrapping Whatsapp Web Chats – Lazy Loading Problem

I’m trying to scrape WhatsApp Web contacts using Playwright. The goal is to scroll through the chat list and open each contact’s chat to extract their name and number from the profile panel.

WhatsApp uses lazy loading to render chats, initially loading as many chats as fit on your screen. As you scroll, additional chats are rendered, but the total number of visible chats remains the same.

Here’s the main issue I’m facing:

The script scrolls, but it reuses previously loaded chats again and again.

It doesn’t load new chats even after scrolling.

Also, the scrolling doesn’t behave correctly — it’s like the wrong container is being scrolled or the page isn’t triggering lazy loading.

Here’s the core part of my code:

const chatBoxes = await page.$$('#pane-side > div > div');
for (let i = 0; i < chatBoxes.length; i++) {
  const chat = chatBoxes[i];
  await chat.scrollIntoViewIfNeeded();
  await chat.click();
  await page.waitForTimeout(500);
  // extract contact info...
}

// scroll pane-side down
await page.evaluate(() => {
  const el = document.querySelector('#pane-side');
  if (el) el.scrollBy(0, 1000);
});

What I’ve Tried:

  • Scrolling #pane-side with scrollBy()
  • Using scrollIntoViewIfNeeded() on each chat
  • Adding delays to give WhatsApp time to load more chats
  • Trying to re-select the chat list after each scroll

Any help with a better strategy to reliably scroll and fetch all contacts from WhatsApp Web using Playwright would be appreciated!

How do I solve ollama qwen2.5-coder API error?

I have installed qwen2.5-coder:3b via ollama and when I want to use this API to generate Html/css components in my project it says
“Error generating content: API error! status: 404, message: Unknown error during generation.”

Please assist me in solving this error. As far as I know I have correctly routed to the correct functions in ai-service.js, in chat-route.js and chat-controller.js

The sendMessageToLLM() is used to communicate to the LLM in each function: generateComponentHtml() and generateFromTemplateMetadata(). in ai-service.js.
The ai-service.js is this:

const fetch = require('node-fetch');

// This service will handle all interactions with the LLM API


async function sendMessageToLLM(message) {
    // Call Ollama API for qwen 2.5-coder:3b
    const ollamaUrl = 'http://localhost:11434/api/generate';
    const payload = {
        model: 'qwen2.5-coder:3b',
        prompt: message,
        stream: false
    };
    try {
        const response = await fetch(ollamaUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
        });
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Ollama API error! status: ${response.status}, message: ${errorText}`);
        }
        const data = await response.json();
        // Ollama returns { response: "..." } for /generate
        return { reply: data.response };
    } catch (error) {
        console.error('Error calling Ollama:', error);
        throw error;
    }
}


async function generateComponentHtml(request, templateId) {
    // Compose a prompt for the LLM
    const prompt = `Generate an HTML component for template ID: ${templateId}. User request: ${request}`;
    try {
        const llmResult = await sendMessageToLLM(prompt);
        return { html: llmResult.reply };
    } catch (error) {
        return { html: `<div class='error'>AI generation failed: ${error.message}</div>` };
    }
}


async function generateContentFromTemplateMetadata(templateMetadata) {
    // Compose a prompt for the LLM
    const prompt = `Generate a full HTML page structure for a website with the following metadata: ${JSON.stringify(templateMetadata)}`;
    try {
        const llmResult = await sendMessageToLLM(prompt);
        return {
            message: `Successfully generated content structure for ${templateMetadata.name}.`,
            html: llmResult.reply
        };
    } catch (error) {
        return {
            message: `AI generation failed: ${error.message}`,
            html: `<div class='error'>AI generation failed: ${error.message}</div>`
        };
    }
}


module.exports = {

    sendMessageToLLM,

    generateComponentHtml,

    generateContentFromTemplateMetadata

};

chat-controller.js:

const fs = require('fs');
const path = require('path');
const aiService = require('./ai-service');
const templates = require('../templates.json'); // Assuming templates.json is in the root for simplicity

// Load templates from JSON file
const loadTemplates = () => {
    try {
        const templatesPath = path.join(__dirname, '..', 'templates.json');
        const templatesData = fs.readFileSync(templatesPath, 'utf8');
        return JSON.parse(templatesData).templates;
    } catch (error) {
        console.error('Error loading templates:', error);
        return [];
    }
};

// Process user message and generate AI response
exports.processMessage = (req, res) => {
    try {
        const { message, templateId } = req.body;
        
        if (!message) {
            return res.status(400).json({ error: 'Message is required' });
        }
        
        // Simulate AI processing
        setTimeout(() => {
            // Generate a response based on the message
            let response = {
                text: `I've received your message about "${message}". Let me help you build that for your website.`,
                suggestions: [
                    'Add a contact form',
                    'Change the color scheme',
                    'Add a product gallery'
                ]
            };
            
            // If template-specific message, customize response
            if (templateId) {
                const templates = loadTemplates();
                const template = templates.find(t => t.id === templateId);
                
                if (template) {
                    response.text = `I'll help you customize your ${template.name} template based on your request: "${message}"`;
                    response.suggestions = template.features.map(feature => `Customize the ${feature} section`);
                }
            }
            
            res.json(response);
        }, 1000); // Simulate processing delay
    } catch (error) {
        console.error('Error processing message:', error);
        res.status(500).json({ error: 'Failed to process message' });
    }
};

// Generate website component based on user request
exports.generateComponent = async (req, res) => {
    try {
        const { request, templateId } = req.body; // componentType is not used by ai-service.js for now

        if (!request) {
            return res.status(400).json({ error: 'Request details are required' });
        }

        // Call AI service to generate component HTML
        const aiResponse = await aiService.generateComponentHtml(request, templateId);
        
        if (aiResponse && aiResponse.html) {
            res.json({ html: aiResponse.html });
        } else {
            console.error('Error: AI service did not return HTML content.');
            res.status(500).json({ error: 'Failed to generate component from AI service' });
        }

    } catch (error) {
        console.error('Error in generateComponent controller:', error);
        res.status(500).json({ error: 'Failed to generate component' });
    }
};

// Get website template suggestions
exports.getSuggestions = (req, res) => {
    try {
        const templates = loadTemplates();
        
        // Generate suggestions based on templates
        const suggestions = [
            'Create a landing page with a hero section and contact form',
            'Add a product showcase with images and descriptions',
            'Design a blog layout with featured posts',
            'Build a portfolio page to showcase your work'
        ];
        
        // Add template-specific suggestions
        templates.forEach(template => {
            suggestions.push(`Create a ${template.name.toLowerCase()} website with ${template.features.slice(0, 2).join(' and ')}`);
        });
        
        res.json({ suggestions });
    } catch (error) {
        console.error('Error getting suggestions:', error);
        res.status(500).json({ error: 'Failed to get suggestions' });
    }
};

// Controller function to handle AI content generation from template metadata
exports.generateContentFromTemplate = async (req, res) => {
    const { templateMetadata } = req.body;

    if (!templateMetadata || !templateMetadata.id) {
        return res.status(400).json({ message: 'Template metadata with ID is required.' });
    }

    // Optionally, re-fetch/validate template data from server-side templates.json
    // to ensure integrity, though editor.html already sends it.
    // 'templates' is available due to the first search/replace block adding the require.
    const serverTemplate = templates.templates.find(t => t.id === templateMetadata.id);
    if (!serverTemplate) {
        return res.status(404).json({ message: `Template with ID '${templateMetadata.id}' not found on server.` });
    }
    // Use the metadata sent from the client, which should be the enriched version

    try {
        console.log(`Chat Controller: Received request to generate content for template: ${templateMetadata.name}`);
        const result = await aiService.generateContentFromTemplateMetadata(templateMetadata);
        res.json(result);
    } catch (error) {
        console.error('Error in generateContentFromTemplate controller:', error);
        res.status(500).json({ message: 'Error generating content from template.', error: error.message });
    }
};

chat-route.js:

const express = require('express');
const router = express.Router();
const chatController = require('./chat-controller');

// Route to handle chat messages
router.post('/message', chatController.processMessage);

// Route to get website component based on user request
router.post('/generate-component', chatController.generateComponent);

// Route to handle AI content generation based on template metadata
router.post('/generate-content', chatController.generateContentFromTemplate);

// Route to get website template suggestions
router.get('/suggestions', chatController.getSuggestions);

module.exports = router;

Please help me fix the error!Any further queries? I would like to help.Thanks.

Cloud run crash “navigator is not defined”

I am trying to deploy a cloud run function, when I call it I get the error:

('require', 'ReferenceError: navigator is not definedn at funcA (/app/vm_us.js:630:13)n at funcB (/app/vm_us.js:1808:13)n at Object.<anonymous> (/app/vm_us.js:1814:77)n at Module._compile (node:internal/modules/cjs/loader:1364:14)n at Module._extensions..js (node:internal/modules/cjs/loader:1422:10)n at Module.load (node:internal/modules/cjs/loader:1203:32)n at Module._load (node:internal/modules/cjs/loader:1019:12)n at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:203:29)n at ModuleJob.run (node:internal/modules/esm/module_job:195:25)n at async ModuleLoader.import (node:internal/modules/esm/loader:337:24)')"

My code is in python and it uses the javascript python package to call functions from a vm_us.js file. On my computer this works fine. The Window and Navigator objects in the JS code are Node.js objects (when I go to definition they are in Node->typescript). Also when I print out navigator.useragent it outputs “Node.js”. Because of this I added Node.js and typescript to the docker image, but it still does not work. I cannot ommit Navigator/Window from my code and I still want to deploy this as an api, how can I do this?

Docker File

FROM python:3.11-slim

# Install Node.js 18
RUN apt-get update && 
    apt-get install -y curl gnupg build-essential && 
    curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && 
    apt-get install -y nodejs && 
    apt-get clean && 
    rm -rf /var/lib/apt/lists/*

# Set environment
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app

# Copy requirements and install
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy Node.js package files and install Node packages
COPY package*.json ./
RUN npm install

# Copy all project files
COPY . .

# Set Flask environment variables
ENV PORT 8080
ENV GOOGLE_APPLICATION_CREDENTIALS /app/service.json

# Expose port
EXPOSE 8080

# Start the Flask app
CMD ["python", "main.py"]

Package.json

{
  "name": "gen2",
  "version": "1.0.0",
  "description": "**To Update/Deploy**",
  "main": "vm.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^22.15.3",
    "typescript": "^5.8.3"
  }
}

Code

function funcA() {
    var A = navigator.userAgent.match(/Firefox/([0-9]+)./i);
    return !(!A || !A[1]) && parseInt(A[1])
}

function funcB() {
    var A = navigator.userAgent.match(/Version/([0-9._]+).*Safari/);
    return A ? A[1].split(".").map(function (A) {
        return (A = parseInt(A, 10)) || 0
    })[0] : 0
}

// + 4k lines using navigator like this

When trying to push the result from local am getting uploading test results to azure dev ops: Request failed with status code 404

I have set up a feature file along with its corresponding step definitions. The tests are running successfully, and I am able to generate a test-result.xml file.(Using Playwright + type script with cucumber)

Now, I’m trying to push the test results from my local environment to an Azure DevOps Test Plan that I’ve created (Test Plan ID: 341345 – dummy number).

Here’s what I’ve done so far:

Generated a Personal Access Token (PAT) in Azure DevOps with full permissions.
checked the company and project both are correct

Feature file

@341472 ----(Test case id in azure devops)
  Scenario: @341472 Validate log in the user module
    When End User navigates to user module and checks Subsection presence
    Then End User add a log in release log under user and checks Whether the added data reflects in about module
    And End User deletes the record in log and check the presence in user module

test-result after running the code by “@cucumber/pretty-formatter”

<?xml version="1.0"?>
<testsuite name="Cucumber" time="151.271" tests="1" skipped="0" failures="0" errors="0" timestamp="2025-05-11T14:37:06.272Z">
  <testcase classname="regression in user module" name="@341472 Validate log in the user module" time="136.713">

        <system-out><![CDATA[When End User navigates to user module and checks Subsection presence......................passed
    When End User navigates to user module and checks Subsection presence.............................passed
    Then End User add a log in release log under user and checks Whether the added data reflects in about module......passed
    And End User deletes the record in log and check the presence in user module..passed]]></system-out>
      </testcase>
    </testsuite>

Created a upload-result.js script to upload the test results.

    const fs = require('fs');
const xml2js = require('xml2js');
const axios = require('axios');

const xmlPath = 'test-results.xml'; // Path to your test results XML file
const testPlanId = '341345'; // Dummy number
const suiteId = '341456'; // dummy
const organization ='companyA'; //Dummy company
const project ='ProjectB'; // Dummy project
const pat = process.env.AZURE_DEVOPS_EXT_PAT; // Ensure this is set in your environment

if (!pat) {
  throw new Error('AZURE_DEVOPS_EXT_PAT is not set in environment variables');
}

const apiVersion = '7.1-preview.6';
const baseUrl = `https://dev.azure.com/${organization}/${project}/_apis/test`;

const authHeader = {
  headers: {
    Authorization: `Basic ${Buffer.from(':' + pat).toString('base64')}`,
    'Content-Type': 'application/json'
  }
};

async function parseResults() {
  console.log('Starting to parse the XML file...');
  const xml = fs.readFileSync(xmlPath, 'utf8');
  const result = await xml2js.parseStringPromise(xml);
  console.log('Successfully parsed XML file...');

  const testCases = result.testsuite.testcase;
  if (!testCases) throw new Error('No test cases found in XML');

  return testCases.map(tc => {
    const name = tc.$.name;
    const classname = tc.$.classname;
    const outcome = tc.failure ? 'Failed' : 'Passed';
    const durationInMs = parseFloat(tc.$.time) * 1000;

    // Extract tag like @TC1234 to map to Azure DevOps test case
    const tagMatch = name.match(/@(d+)/);
    const automatedTestName = tagMatch ? tagMatch[1] : null;

    if (!automatedTestName) {
      console.warn(`No @xxxx tag found in "${name}". Test case will be skipped.`);
      return null; // skip test cases without a valid tag
    }

    return {
      automatedTestName: automatedTestName,
      outcome,
      automatedTestType: 'Cucumber',
      durationInMs,
      testCaseTitle: `${classname} - ${name}`
    };
  }).filter(Boolean); // remove any null entries
}
async function uploadToAzureDevOps() {
  console.log('Starting the upload process...');
  const results = await parseResults();

  try {
    const createRunUrl = `${baseUrl}/runs?api-version=${apiVersion}`;
    console.log('Making a POST request to create a test run...');
    console.log('Create Run URL:', createRunUrl);

    const runPayload = {
      name: 'Automated Test Run',
      plan: { id: testPlanId },
      automated: true,
      state: 'InProgress'
    };

    console.log('Payload for creating test run:', JSON.stringify(runPayload, null, 2));

    const runResponse = await axios.post(createRunUrl, runPayload, authHeader);

    if (!runResponse || !runResponse.data || !runResponse.data.id) {
      throw new Error(`No testRunId returned. Response: ${JSON.stringify(runResponse.data)}`);
    }

    const testRunId = runResponse.data.id;
    console.log(`Created test run with ID: ${testRunId}`);

    const resultsUrl = `${baseUrl}/runs/${testRunId}/results?api-version=${apiVersion}`;
    console.log('Uploading test results to:', resultsUrl);

    await axios.post(resultsUrl, results, authHeader);
    console.log('Uploaded test results');

    const completeUrl = `${baseUrl}/runs/${testRunId}?api-version=${apiVersion}`;
    console.log('Marking test run as completed at:', completeUrl);

    await axios.patch(completeUrl, { state: 'Completed' }, authHeader);
    console.log('Marked test run as completed');
  } catch (err) {
    console.error('Error uploading test results:', err.message);
    if (err.response) {
      console.error('Response data:', JSON.stringify(err.response.data, null, 2));
    }
  }
}


// Execute the upload
uploadToAzureDevOps();

However, when I run the command node src/upload-result.js, I encounter an error in console when trying to upload the result.Please note i have added console.log to get the where error is happening so that why any to see the messages

Making a POST request to create a test run...

    Create Run URL: https://dev.azure.com/companyA/ProjectB/_apis/test/runs?api-version=7.1-preview.6
    Payload for creating test run: {
      "name": "Automated Test Run",
      "plan": {
        "id": "341345"
      },
      "automated": true,
      "state": "InProgress"
    }
    Error uploading test results: Request failed with status code 404
    Response data: "Page not found"

Any help is much appreciated .if any additional details are required, please ask away. This is the first time trying this

Componenet has an invalid “default” export: Props Type is not valid

I am using these versions

"next": "14.2.3",
"react": "18.2.0",
"react-dom": "18.2.0"

Now This is the props type of my TimeLine componenet in my NextJS 14 app.Now I am trying to send the id and a setState function in my componenet as the Props in this code.

{
          activeTab === 'Timeline' && (
            id && (
              <TimeLine
              id={id}
              setIsModalNewTaskOpen={setIsModalNewTaskOpen}
            >
            </TimeLine>
            )
          )
        }

This is the whole Component, which is a client component.

"use client";

import { useAppSelector } from "@/app/redux";
import { DisplayOption, Gantt, ViewMode } from "gantt-task-react";
import "gantt-task-react/dist/index.css";
import React, { useMemo, useState } from "react";
import { Project } from "../state/types";
import Header from "../(components)/Header";
import { useGetProjectsQuery } from "../state/api";
import { TimeLineProps } from "../project/types";

type TaskTypeItems = "task" | "milestone" | "project";

const Timeline : React.FC<TimeLineProps> = () => {
  const isDarkMode = useAppSelector((state) => state.global.isDarkModeOn);
  const { data: projects , isLoading, isError } = useGetProjectsQuery();

  const [displayOptions, setDisplayOptions] = useState<DisplayOption>({
    viewMode: ViewMode.Month,
    locale: "en-US",
  });

  const ganttTasks = useMemo(() => {
    console.log(projects)
    return (
      Array.isArray(projects) && projects.length > 0 &&
      projects?.map((project : Project) => ({
        start: new Date(project.startDate as string),
        end: new Date(project.endDate as string),
        name: project.name,
        id: `Project-${project.id}`,
        type: "project" as TaskTypeItems,
        progress: 50,
        isDisabled: false,
      })) || []
    );
  }, [projects]);

  const handleViewModeChange = (
    event: React.ChangeEvent<HTMLSelectElement>,
  ) => {
    setDisplayOptions((prev) => ({
      ...prev,
      viewMode: event.target.value as ViewMode,
    }));
  };

  if (isLoading) return <div>Loading...</div>;
  if (isError || !projects)
    return <div>An error occurred while fetching projects</div>;

  return [
    <div className="max-w-full p-8">
      <header className="mb-4 flex items-center justify-between">
        <Header name="Projects Timeline" />
        <div className="relative inline-block w-64">
          <select
            className="focus:shadow-outline block w-full appearance-none rounded border border-gray-400 bg-white px-4 py-2 pr-8 leading-tight shadow hover:border-gray-500 focus:outline-none dark:border-dark-secondary dark:bg-dark-secondary dark:text-white"
            value={displayOptions.viewMode}
            onChange={handleViewModeChange}
          >
            <option value={ViewMode.Day}>Day</option>
            <option value={ViewMode.Week}>Week</option>
            <option value={ViewMode.Month}>Month</option>
          </select>
        </div>
      </header>

      <div className="overflow-hidden rounded-md bg-white shadow dark:bg-dark-secondary dark:text-white">
        <div className="timeline">
          {
            Array.isArray(ganttTasks) && ganttTasks.length > 0 &&
            <Gantt
            tasks={ganttTasks}
            {...displayOptions}
            columnWidth={displayOptions.viewMode === ViewMode.Month ? 150 : 100}
            listCellWidth="100px"
            projectBackgroundColor={isDarkMode ? "#101214" : "#1f2937"}
            projectProgressColor={isDarkMode ? "#1f2937" : "#aeb8c2"}
            projectProgressSelectedColor={isDarkMode ? "#000" : "#9ba1a6"}
          />
          }
        </div>
      </div>
    </div>
  ]
};

export default Timeline;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

Now on running this command of npm run build I am getting this error:

Linting and checking validity of types …Failed to compile.src/app/timeline/page.tsx
Type error: Page “src/app/timeline/page.tsx” has an invalid “default” export:
Type “TimeLineProps” is not valid.

What should I lean to start coding [closed]

I have learnt javascript for a long time and still struggling how to become succesfull

What should I tried to learn. I have learnt javascript for a long time and still struggling how to become succesfull

What should I tried to learn I have learnt javascript for a long time and still struggling how to become succesfull

What should I tried to learn

Performance Discrepancy in Image Pixel Comparison Across Browsers Using Canvas

I am using the Canvas NPM library to perform a delta comparison between two images. Both images have the same dimensions (equal width and height). The Method takes two images as base64 string and I am comparing every pixel of the two images. In Chrome and Edge, the loop takes about 20–50ms to execute, whereas in Firefox, it takes approximately 700–800ms. Could this be due to Firefox having slower memory management compared to Chrome and Edge?

async function createDeltaImageFromBase64(currentImageBase64, previousImageBase64) {

    if(currentImageBase64 == null || previousImageBase64 ==null){
        return previousImageBase64;
    }
    // Load images using base64 data URIs
    const currentImage = await loadImage(currentImageBase64);
    const previousImage = await loadImage(previousImageBase64);

    // Ensure the dimensions match
    const width = Math.min(currentImage.width, previousImage.width);
    const height = Math.min(currentImage.height, previousImage.height);

    // Create a canvas for the delta image
    const canvas = createCanvas(width, height);
    const ctx = canvas.getContext('2d');

    // Draw the current image on the canvas
    ctx.drawImage(currentImage, 0, 0, width, height);
    const currentImageData = ctx.getImageData(0, 0, width, height);

    // Draw the previous image on a temporary canvas
    const tempCanvas = createCanvas(width, height);
    const tempCtx = tempCanvas.getContext('2d');
    tempCtx.drawImage(previousImage, 0, 0, width, height);
    const previousImageData = tempCtx.getImageData(0, 0, width, height);

    // Create the delta image
    const deltaImageData = ctx.createImageData(width, height);
    const { data: currentData } = currentImageData;
    const { data: previousData } = previousImageData;
    const { data: deltaData } = deltaImageData;

    for (let i = 0; i < currentData.length; i += 4) {
        // Compare RGBA channels
        const isDifferent =
            currentData[i] !== previousData[i] ||
            currentData[i + 1] !== previousData[i + 1] ||
            currentData[i + 2] !== previousData[i + 2] ||
            currentData[i + 3] !== previousData[i + 3];

        if (isDifferent) {
            // Copy current image pixel
            deltaData[i] = currentData[i];       // Red
            deltaData[i + 1] = currentData[i + 1]; // Green
            deltaData[i + 2] = currentData[i + 2]; // Blue
            deltaData[i + 3] = currentData[i + 3]; // Alpha
        } else {
            // Set to transparent
            deltaData[i] = 0;
            deltaData[i + 1] = 0;
            deltaData[i + 2] = 0;
            deltaData[i + 3] = 0;
        }
    }

    // Put the delta image data back on the canvas
    ctx.putImageData(deltaImageData, 0, 0);

    // Return the delta image as a base64 string
    return canvas.toDataURL(); // Returns a base64 data URI
}

How to select the currently active / most recently focused video on a page with multiple videos?

On a page with multiple videos the currently active / most recently focused video reacts to standard keyboard actions like Space to play/pause the video or F to toggle full screen mode. This is already the case after initially selecting/clicking the respective video. But how to select this video to be able to perform custom keyboard actions like S to access the settings?

I tried an click event listener on the document body taking advantage of event delegation to select the closest video wrapper along with its containing video element. This, of course, only works if the video is clicked with the mouse and not if it is selected with the keyboard. But most importantly, it only works after the second click, because the first one apprently only activates the video in the first place.

So how does the browser do this and how can I mimic this behavior?

Understanding Javascript [closed]

I could really use some helpright now. I’m currently learning JavaScript . I started from HTML and CSS, and now I’m working my way through JS. Honestly, I spend over 4 hours a day studying, but it still feels overwhelming. I’m wondering will I ever truly get it? Is there a better way or technique to learn JavaScript effectively?

HTML5 Twitching Audio Progress Bar in Safari/Orion, Smooth in Chrome/Firefox

Problem Description:

About half a second after audio playback starts (in Safari and Orion), the progress bar “twitches”, jumping back towards the start momentarily before continuing to progress. The audio itself plays fine.

Below is a stripped down reproduction of the issue:

<head>
    <title>Audio Progress Twitch Test</title>
    <style>
        /* Basic body for display */
        body {
            font-family: sans-serif;
            padding: 20px;
        }

        /* Paragraph Container - simplified */
        .paragraph-group {
            padding: 10px;
            border: 1px solid #ccc;
            margin-bottom: 10px;
            position: relative;
        }

        /* Audio Controls - simplified positioning */
        .audio-controls {
            position: absolute;
            right: 10px;
            top: 15px;
        }

        /* Play Button - basic styling */
        .play-btn {
            width: 32px;
            height: 32px;
            border: 1px solid #aaa;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            background-color: #f0f0f0;
        }
        .play-btn.playing {
            /* Simple visual feedback for playing state */
            background-color: #ccf2ff;
        }

        /* Progress Bar - core styles */
        .progress-container {
            margin-top: 10px;
            height: 12px;
            cursor: pointer;
            background-color: #eee; /* Background for the container */
        }

        .progress-bar {
            /* This is the track */
            height: 4px;
            width: 100%;
            background: #d0d0d0;
            border-radius: 2px;
            position: relative; /* For the indicator */
            margin-top: 4px; /* To center it a bit in the 12px container */
        }

        .progress-indicator {
            /* This is the moving part */
            position: absolute;
            height: 100%;
            width: 0%; /* Initial state */
            background: #007bff;
            border-radius: 2px;
            transition: none !important; /* Explicitly disable CSS transitions */
        }
    </style>
</head>
<body>
    <div class="paragraph-group" data-audio="sample.m4a">
        <!-- Replace with your audio file -->
        <p>Sample Audio Track</p>
        <div class="audio-controls">
            <button class="play-btn">▶</button>
        </div>
        <div class="progress-container">
            <div class="progress-bar">
                <div class="progress-indicator"></div>
            </div>
        </div>
    </div>

    <script>
        let currentAudio = null;
        let currentParagraph = null; // The div containing the current audio
        let isPlaying = false;
        const audioElements = {};

        // Web Audio API setup (as in the original, potentially relevant)
        const audioContext = new (window.AudioContext ||
            window.webkitAudioContext)();

        // Initialize audio elements
        document
            .querySelectorAll(".paragraph-group")
            .forEach((paraGroupDiv) => {
                const audioSrc = paraGroupDiv.getAttribute("data-audio");
                if (audioSrc) {
                    const audio = new Audio(audioSrc);
                    audioElements[audioSrc] = audio;

                    // Connect to Web Audio API
                    try {
                        const source =
                            audioContext.createMediaElementSource(audio);
                        source.connect(audioContext.destination);
                    } catch (e) {
                        console.error("Error creating MediaElementSource: ", e);
                    }
                }
            });

        // Play button event listener
        document.querySelectorAll(".play-btn").forEach((btn) => {
            btn.addEventListener("click", function () {
                if (audioContext.state === "suspended") {
                    audioContext
                        .resume()
                        .catch((e) =>
                            console.error("AudioContext resume error:", e)
                        );
                }

                const paragraphDiv = this.closest(".paragraph-group");
                const audioSrc = paragraphDiv.getAttribute("data-audio");
                const audio = audioElements[audioSrc];

                if (!audio) return;

                if (
                    currentAudio &&
                    currentAudio !== audio &&
                    !currentAudio.paused
                ) {
                    currentAudio.pause();
                    if (currentParagraph) {
                        const prevPlayBtn =
                            currentParagraph.querySelector(".play-btn");
                        if (prevPlayBtn) {
                            prevPlayBtn.textContent = "▶";
                            prevPlayBtn.classList.remove("playing");
                        }
                    }
                }

                if (audio.paused) {
                    if (
                        audio.ended ||
                        (audio.duration > 0 &&
                            Math.abs(audio.currentTime - audio.duration) < 0.01)
                    ) {
                        audio.currentTime = 0;
                        const progressIndicator = paragraphDiv.querySelector(
                            ".progress-indicator"
                        );
                        if (progressIndicator) {
                            progressIndicator.style.transition = "none"; // Ensure no transition during reset
                            progressIndicator.style.width = "0%";
                        }
                    }

                    audio
                        .play()
                        .catch((e) => console.error("Audio play error:", e));
                    this.textContent = "⏸";
                    this.classList.add("playing");
                    currentAudio = audio;
                    currentParagraph = paragraphDiv;
                    isPlaying = true;
                    requestAnimationFrame(updateProgress);

                    audio.onended = () => {
                        isPlaying = false;
                        this.textContent = "▶";
                        this.classList.remove("playing");
                        const progressIndicator = paragraphDiv.querySelector(
                            ".progress-indicator"
                        );
                        if (progressIndicator)
                            progressIndicator.style.width = "0%";
                    };
                } else {
                    audio.pause();
                    isPlaying = false;
                    this.textContent = "▶";
                    this.classList.remove("playing");
                }
            });
        });

        // Progress bar seeking
        document
            .querySelectorAll(".progress-container")
            .forEach((container) => {
                container.addEventListener("click", function (e) {
                    const paragraphDiv = this.closest(".paragraph-group");
                    const audioSrc = paragraphDiv.getAttribute("data-audio");
                    // Fallback to currentAudio if the click is on the active player's bar but audio not found by src
                    const audio =
                        audioElements[audioSrc] ||
                        (currentParagraph === paragraphDiv
                            ? currentAudio
                            : null);

                    if (!audio || isNaN(audio.duration) || audio.duration <= 0)
                        return;

                    const rect = this.getBoundingClientRect(); // 'this' is the progress-container
                    const seekPos =
                        Math.min(
                            Math.max(0, e.clientX - rect.left),
                            rect.width
                        ) / rect.width;

                    audio.currentTime = seekPos * audio.duration;

                    const indicator = paragraphDiv.querySelector(
                        ".progress-indicator"
                    );
                    if (indicator) {
                        indicator.style.width = `${seekPos * 100}%`;
                    }
                    // If paused and seek, update immediately but don't start animation loop unless playing
                    if (!isPlaying && currentAudio === audio) {
                        // Manually call updateProgress once to show the new position if paused
                        const safeDuration = audio.duration || 0.1;
                        const progress =
                            (audio.currentTime / safeDuration) * 100;
                        if (indicator) {
                            indicator.style.transition = "none";
                            indicator.style.width = `${Math.min(
                                100,
                                Math.max(0, progress)
                            )}%`;
                        }
                    }
                });
            });

        // updateProgress function
        function updateProgress() {
            if (!isPlaying || !currentAudio || !currentParagraph) {
                return;
            }

            // This line is suspected to be problematic if currentAudio.duration is unstable initially
            const safeDuration = currentAudio.duration || 0.1;
            const progress = (currentAudio.currentTime / safeDuration) * 100;

            const indicator = currentParagraph.querySelector(
                ".progress-indicator"
            );

            if (indicator) {
                indicator.style.transition = "none"; // Reinforce no transitions
                indicator.style.width = `${Math.min(
                    100,
                    Math.max(0, progress)
                )}%`; // Clamp progress
            }

            if (isPlaying) {
                requestAnimationFrame(updateProgress);
            }
        }
    </script>
</body>

So essentially I’m looking for a way to fix this to be smooth and not jumpy for WebKit-based browsers. It works perfectly in firefox and chrome, so that seems to be the source of this debacle. Thank you in advance for any help!

Get the timestamp as unix timestamp (QuestDB)

Right now, when I make a query, I get the time column in this format 2021-06-08T15:45:45.123456Z.

But I need the time as unix timestamp (long – miliseconds).

I have in my mind 2 idea:

  1. Date.parse(time) – in javascript.
  2. SELECT time::long / 1000 – in the query

I don’t know which way is better as performances (I have no idea how to compare).

I query every 250ms with results between 1_000 and 100_000 of rows.

There can be a better way?

P.S.: I use javascript with postgresql to query from questdb