I am new to node.js and while starting i learn that Nodejs is run time environment of Java script now, what actually is run time? what is environment? these words like environment, runtime ,dependencies, utilities these words are very common and frequently used and heard without knowing what actually these are ?
div scrolling with the page
I’m working on a writing sprint tool in HTML/CSS. It’s a timed writing session where the user selects a duration, hits “Start”, and writes until the timer ends. The issue, however, lies with the positioning, and not with the tool.
The layout has three main parts.
-
Left: #SPRINT-SETTINGS
A settings panel with dropdowns to choose a sprint duration, themes, and a start button. -
Right: #SPRINT-CARD
A large sprint card that displays a background image (selected from
the settings panel.), and a large countdown timer in the center.
Even though my #sprint-card is styled with position: fixed and has top and right values, it still moves down when I scroll down. I expected it to stay pinned in place like everything else.
Things I’ve checked and one:
- #sprint-card is absolutely using position: fixed
- No JavaScript is modifying its position
- The parent container uses display: flex (.sprint-layout)
- Other fixed elements like the .sprint-settings panel don’t have this issue,
only the card does
Any ideas on why this is happening or what else I should check?
Thanks in advance!
HTML:
<main class="sprint-page">
<h1>Writing Sprint</h1>
<p>Customize your sprint session below and press start when ready. You can adjust some settings during the sprint!</p>
<quote class="attribution">All background images were pulled from Freepik. They belong to the rightful owner.</quote>
</main>
<div class="sprint-layout">
<section class="sprint-settings">
<label for="duration">
Sprint Duration
<span class="tooltip" data-tooltip="Select the total sprint time.">?</span>
</label>
<select id="duration">
<option value="10">10 Minutes</option>
<option value="20">20 Minutes</option>
<option value="30">30 Minutes</option>
<option value="45">45 Minutes</option>
<option value="60">60 Minutes</option>
</select>
<label for="background">
Background Image
<span class="tooltip" data-tooltip="Select a background to set the mood while you write.">?</span>
</label>
<select id="background">
<option value="Images/sprints/dragonknight.jpg">Knight and Dragons</option>
<option value="Images/sprints/dragoncastle.jpg">Knight and Castle</option>
<option value="Images/sprints/castlecliff.jpg">Castle on Cliff</option>
<option value="Images/sprints/ancientruins.jpg">Ancient Ruins</option>
<option value="Images/sprints/sunkencity.jpg" selected>Sunken City</option>
<option value="Images/sprints/finalbattle.jpg">Final Battle</option>
</select>
<label for="ambience">
Ambience
<span class="tooltip" data-tooltip="Choose background ambience for your sprint session.">?</span>
</label>
<select id="ambience">
<option value="none">None</option>
<option value="Sound/Forest.mp3">Forest</option>
<option value="Sound/Night.mp3">Night</option>
<option value="Sound/Windhowl.mp3">Wind Howling</option>
<option value="Sound/Fireplace.mp3">Fireplace</option>
<option value="Sound/Birds.mp3">Birds</option>
</select>
<label for="volume-slider">
Volume
<span class="tooltip" data-tooltip="Adjust the volume of the selected ambience audio.">?</span>
</label>
<input type="range" id="volume-slider" min="0" max="100" value="50" />
<button id="start-btn" class="btn" onclick="toggleSprint()">Start Sprint</button>
<button class="btn" onclick="resetSprint()">Reset Sprint</button>
</section>
<section class="sprint-card" id="sprint-card">
<div class="sprint-info">
<h2 id="sprint-status">Ready to Sprint!</h2>
<p id="sprint-timer">00:00</p>
<div id="motivation-text" class="motivation-text">You’ve got this!</div>
</div>
</section>
</div>
<footer>
<div class="footer-container">
<div class="footer-column">
<h4>About</h4>
<a href="about.html" aria-label="About FantasyWriters">About Us</a>
<a href="contact.html" aria-label="Contact FantasyWriters">Contact Us</a>
<a href="contact.html" aria-label="Partnerships with FantasyWriters">Partnerships</a>
<a href="faq.html" aria-label="Frequently Asked Questions">Frequently Asked Questions</a>
</div>
<div class="footer-column">
<h4>Resources</h4>
<a href="writingsprint.html" aria-label="Writing Sprint Tool">Writing Sprint</a>
<a href="namegenerator.html" aria-label="Name Generator Tool">Name Generator</a>
</div>
<div class="footer-column">
<h4>Community</h4>
<a href="https://discord.com/invite/7nu2Zz8StN" target="_blank" rel="noopener" aria-label="Join our Discord community">
<img src="Images/Discord_Logo.png" alt="Discord logo" class="footer-icon" /> Discord
</a>
<a href="https://www.reddit.com/r/fantasywriters/" target="_blank" rel="noopener" aria-label="Join our Reddit community">
<img src="Images/Reddit_Logo.png" alt="Reddit logo" class="footer-icon" /> Reddit
</a>
</div>
</div>
<div class="footer-copy">
© 2025 FantasyWriters. All rights reserved.
</div>
</footer>
</body>
</html>
CSS:
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: white;
color: #333;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
main {
flex: 1;
padding: 3rem 1rem;
max-width: 700px;
margin: 0 auto;
text-align: center;
}
.sprint-page {
padding: 1rem 2rem;
text-align: left;
width: 20%;
margin: 0;
position: relative;
top: 0;
z-index: 5;
background-color: #e2e1e1;
box-shadow: rgba(85, 84, 85, 0.041) 3px 0px 0px;
}
.sprint-layout {
display: flex;
justify-content: space-between;
width: 100%;
padding: 0 2rem;
box-sizing: border-box;
background-color: rgb(21, 255, 0);
}
.sprint-settings {
position: fixed;
left: 2rem;
top: 250px;
width: 250px;
display: flex;
flex-direction: column;
gap: 1rem;
z-index: 10;
}
.sprint-card {
position: fixed;
right: 3rem;
top: 150px;
width: 1300px;
height: 600px;
background-image: url("../Images/sprints/dragonknight.jpg");
background-size: cover;
background-position: center;
color: white;
padding: 2rem;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
z-index: 0;
}
.sprint-info {
background-color: rgba(0, 0, 0, 0.6);
padding: 2rem;
border-radius: 10px;
text-align: center;
width: 50%;
}
.sprint-info h2 {
margin: 0;
font-size: 2.5rem;
}
#sprint-timer {
font-size: 4rem;
margin-top: 1rem;
font-weight: bold;
}
.sprint-settings label {
font-weight: bold;
color: #333;
}
.sprint-settings select,
.sprint-settings button {
padding: 0.5rem;
font-size: 1rem;
border-radius: 5px;
border: 1px solid #ccc;
width: 100%;
box-sizing: border-box;
}
.sprint-settings button.btn {
margin-top: 1rem;
cursor: pointer;
background-color: #77946e;
color: white;
border: none;
}
.sprint-settings button.btn:hover {
background-color: #45a049;
}
.attribution {
display: block;
font-style: italic;
color: #555;
background-color: #ffffff5d;
padding: 10px 20px;
margin: 600px 0;
font-size: 0.9em;
}
.motivation-text {
text-align: center;
margin-top: auto;
font-size: 1.1rem;
font-weight: bold;
color: #ffffff;
opacity: 0;
transition: opacity 1s ease-in-out;
}
@keyframes glow {
0%, 100% { text-shadow: 0 0 10px #ffd700; }
50% { text-shadow: 0 0 20px #ff4500; }
}
.sprint-complete {
font-size: 2rem;
color: #ffd700;
animation: glow 1.5s infinite;
text-align: center;
margin-top: 1rem;
}
@media (max-width: 768px) {
.sprint-page {
width: 30%;
padding: 0.5rem;
}
.sprint-page h1 {
font-size: 20px;
}
.sprint-page p {
font-size: 14px;
}
.sprint-layout {
display: flex;
flex-direction: row;
overflow-x: auto;
padding: 0 1rem;
}
.sprint-settings {
position: fixed;
top: 240px;
left: 0.1rem;
width: 200px;
gap: 0.8rem;
padding: 0.5rem;
font-size: 0.9rem;
z-index: 10;
}
.sprint-card {
position: fixed;
top: 200px;
right: 0.4rem;
width: 480px;
height: 300px;
padding: 1rem;
border-radius: 8px;
background-size: cover;
background-position: center;
color: white;
}
.sprint-info {
width: 100%;
padding: 1rem;
font-size: 0.9rem;
}
.sprint-info h2 {
font-size: 1.5rem;
}
#sprint-timer {
font-size: 2rem;
}
.motivation-text {
font-size: 0.9rem;
}
}
.tooltip {
display: inline-block;
margin-left: 5px;
color: #414141;
background-color: #c7baba;
border-radius: 50%;
width: 18px;
height: 18px;
text-align: center;
font-size: 12px;
line-height: 18px;
font-weight: bold;
cursor: pointer;
position: relative;
z-index: 1000;
}
.tooltip:hover::after {
content: attr(data-tooltip);
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 125%;
background-color: #333;
color: #fff;
padding: 6px 10px;
border-radius: 5px;
white-space: normal;
width: 200px;
font-size: 0.85rem;
z-index: 1000;
opacity: 1;
transition: opacity 0.3s;
}
.tooltip:hover::before {
content: "";
position: absolute;
bottom: 110%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #333;
z-index: 1001;
}
googles direct dynamic script loading no longer works for Google’s new auto complete
I am migrating to googles new places Auto complete. I am trying to load the script using Google recommended method for loading. They says there are 3 ways to load the map
-
Use dynamic library import.
-
NPM js-api-loader package.
I am focused on the first two.
I followed the guide and was able to load the script with dynamic library import (option 1).
However the 2nd option: direct script loading tag does not works. It used to work under the legacy script (not sure if its been ported over to the new places API).
I need to use option two because I want to dynamically load the script only when its being used.
This is example of script for option 1 – dynamic library import:
<script>
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "YOUR_API_KEY",
v: "weekly",
// Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
// Add other bootstrap parameters as needed, using camel case.
});
</script>
this is example of option 2 Direct loading:
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&loading=async&libraries=places&callback=initMap">
</script>
And this is full example of how i was able to load using dynamic library import (option one):
HTML:
<!doctype html>
<html>
<head>
<title>Place Autocomplete element</title>
<link rel="stylesheet" type="text/css" href="./style.css" />
<script type="module" src="./index.js"></script>
</head>
<body>
<div class="place-autocomplete-card" id="place-autocomplete-card">
<p>Search for a place here:</p>
</div>
<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
({key: "MYKEY", v: "weekly"});</script>
</body>
</html>
JS
async function initMap() {
await google.maps.importLibrary("places");
const country = 'uk'
const placeAutocomplete = new google.maps.places.PlaceAutocompleteElement();
const card = document.getElementById('place-autocomplete-card');
card.appendChild(placeAutocomplete);
document.body.appendChild(selectedPlaceInfo);
placeAutocomplete.addEventListener('gmp-select', async ({ placePrediction }) => {
const place = placePrediction.toPlace();
console.log('places return', place);
await place.fetchFields({ fields: ['displayName', 'formattedAddress', 'location','addressComponents'] });
});
}
initMap();
when I used the exact same script above but change the script to option two i.e
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&loading=async&libraries=places&callback=initMap">
</script>
I got the following error messages:
- Uncaught (in promise) ReferenceError: google is not defined
- Uncaught (in promise) InvalidValueError: initMap is not a function
WordPress Taxonomy check box to radio button
In wordpress I am recently working on the custom post type with a custom taxonomy for the post type. In the post edit page i need to convert my taxonomy check box to radio button because the user need to select only one term for one post and i am using gutenberg editor. Help me to figure out this problem.
Does JavaScript (V8) optimize concatenation of const strings like Java does?
I’m learning about string concatenation in JavaScript and how it compares to Java.
In Java, if you write:
final String LET = "a";
final String OOO = "aaaaa";
final String LET_OOO = LET + OOO;
The compiler combines these constant strings at compile time, so NO new string is created at runtime.
Can JavaScript do the same? For example:
const a = "a";
const b = "bbb";
const c = a + b;
I expected that since both are constants, JavaScript might optimize and not create a new string in memory—similar to how Java combines final strings at compile time.
Does JavaScript (like V8) concatenate these without creating a new string in memory? Or does it always create a new string?
Ant Design Dropdown with contextMenu trigger closes instantly when hovering — how to keep it open?
I’m using Ant Design’s Dropdown component in a React app. The dropdown is triggered via right-click using trigger={[“contextMenu”]}.
However, there’s an issue:
When I right-click on the element, the dropdown appears — but as soon as I try to move my mouse toward the menu to click “Start”, “Logs”, or “Delete”, the menu disappears instantly.
What I’ve Tried:
Set a controlled open state using useState(false)
Used onOpenChange={(open) => setDropdownOpen(open)} along with open={dropdownOpen}
Moved my logic to set status inside onOpenChange when open === true
Added mouseLeaveDelay={0.3}
Used getPopupContainer={(triggerNode) => triggerNode.parentNode}
Ensured my menu={{ items }} is correct and stable
Wrapped the trigger element in a with a fixed height and width
But nothing worked — the dropdown still vanishes as soon as I try to hover over the menu options.
`
const [dropdownOpen, setDropdownOpen] = useState(false);
const items = [
{
key: "start",
label: "Start",
},
{
key: "logs",
label: "Logs",
},
{
key: "delete",
label: "Delete",
},
];
<Dropdown
open={dropdownOpen}
onOpenChange={(open) => {
setDropdownOpen(open);
if (open && node?.id) {
// setting status based on componentData
}
}}
menu={{ items }}
trigger={["contextMenu"]}
mouseLeaveDelay={0.3}
getPopupContainer={(trigger) => trigger.parentNode}
>
<div style={{ width: 150, height: 100, background: "#f0f0f0" }}>
Right-click me
</div>
</Dropdown>
`
MediaStream metadata not being loaded in react
Im making a mutli user video chatting app using webrtc but the remote streams that are recieved with the peer connections dont play .
this is my peerManager class :
import { v4 as uuidv4 } from "uuid";
export class PeerService {
private ws: WebSocket;
private peerList: Map<string, RTCPeerConnection>;
private localStream: MediaStream | null = null;
public remoteStreams: Map<string, MediaStream> = new Map();
constructor(soc: WebSocket, localStream?: MediaStream) {
this.peerList = new Map<string, RTCPeerConnection>();
this.ws = soc;
this.localStream = localStream || null;
}
// Add local stream to be shared with peers
async addLocalStream(stream: MediaStream) {
console.log("local stream");
this.localStream = stream;
// Add tracks to all existing peer connections
this.peerList.forEach((pc) => {
stream.getTracks().forEach((track) => {
pc.addTrack(track, stream);
});
});
}
// Remove local stream
removeLocalStream() {
if (this.localStream) {
this.peerList.forEach((pc) => {
const senders = pc.getSenders();
senders.forEach((sender) => {
if (
sender.track &&
this.localStream?.getTracks().includes(sender.track)
) {
pc.removeTrack(sender);
}
});
});
this.localStream = null;
}
}
// Get remote stream for a specific peer
getRemoteStream(peerID: string): MediaStream | null {
return this.remoteStreams.get(peerID) || null;
}
// Get all remote streams
getAllRemoteStreams(): Map<string, MediaStream> {
return this.remoteStreams;
}
private setupTrackHandlers(pc: RTCPeerConnection, peerID: string) {
// Handle incoming remote tracks
pc.ontrack = (event: RTCTrackEvent) => {
console.log("tracks adde");
const remoteStream = event.streams[0];
console.log(event.streams);
if (remoteStream) {
this.remoteStreams.set(peerID, remoteStream);
// console.log(this.remoteStreams)
window.dispatchEvent(
new CustomEvent("remoteStreamAdded", {
detail: { peerID, stream: remoteStream },
})
);
}
};
if (this.localStream) {
console.log("localllllllllll");
this.localStream.getTracks().forEach((track) => {
pc.addTrack(track, this.localStream!);
});
}
}
async addPeer() {
console.log(
"adddd poeererererewrawgdsfg hdsfg jsfoghsdfogndsofngdsangakjb"
);
const peerID = uuidv4();
const pc = new RTCPeerConnection();
console.log("add peer");
this.setupTrackHandlers(pc, peerID);
pc.onicecandidate = (event: any) => {
// console.log("ws in PEER MANAGER", this.ws);
if (event.candidate && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(
JSON.stringify({
type: "ice-candidate",
payload: {
candidate: event.candidate,
peerID,
},
})
);
}
};
let offer = await pc.createOffer();
await pc.setLocalDescription(new RTCSessionDescription(offer));
console.log(pc.signalingState);
// console.log(`during offer peer :${peerID}`,pc)
if (this.ws) {
this.ws.send(
JSON.stringify({
type: "offer",
payload: {
peerID,
sdp: offer,
},
})
);
}
this.peerList.set(peerID, pc);
}
async handleSignal(message: any) {
console.log(message);
console.log(this.peerList);
let message_type = message.type;
/* let pc;
if (message.type !== "offer"){
console.log(message)
pc = this.peerList.get(message.payload.peerID);} */
//console.log(pc );
//console.log(message)
/* if (!pc) {
console.log("peer connection not found ");
return;
} */
console.log(message_type);
switch (message_type) {
case "offer": {
const peerID = message.payload.peerID;
const peerConnection = new RTCPeerConnection();
// Optional: Monitor ICE state
peerConnection.oniceconnectionstatechange = () => {
console.log(
"ICE state for",
peerID,
"→",
peerConnection.iceConnectionState
);
};
// ✅ Add local tracks BEFORE setting remote description
this.setupTrackHandlers(peerConnection, peerID); // This must add tracks if localStream exists
// ✅ Set remote description
await peerConnection.setRemoteDescription(
new RTCSessionDescription(message.payload.sdp)
);
// ✅ Store the peer connection immediately under the correct key
this.peerList.set(peerID, peerConnection);
// ✅ Create and send answer
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(
JSON.stringify({
type: "answer",
payload: {
peerID,
sdp: answer,
},
})
);
}
break;
}
case "answer":
console.log("answer");
const pc = this.peerList.get(message.payload.peerID);
console.log(pc);
if (!pc) return;
// if(pc.connectionState === )
// console.log(pc.signalingState);
// console.log(`during asnwer peer :${message.payload.peerID}`,pc)
await pc?.setRemoteDescription(
new RTCSessionDescription(message.payload.sdp)
);
break;
case "ice-candidate": {
const pc = this.peerList.get(message.payload.peerID);
if (!pc) return;
await pc?.addIceCandidate(
new RTCIceCandidate(message.payload.candidate)
);
break;
}
}
}
closePeer(peerID: string) {
const pc = this.peerList.get(peerID);
pc?.close();
this.peerList.delete(peerID);
this.remoteStreams.delete(peerID);
window.dispatchEvent(
new CustomEvent("remoteStreamRemoved", {
detail: { peerID },
})
);
}
closeAll() {
this.peerList.forEach((pc, peerId) => {
pc.close();
});
this.peerList.clear();
this.remoteStreams.clear();
this.removeLocalStream();
}
}
export default PeerService;
VidConference.tsx
import React, { useState, useEffect, useRef } from "react";
import { useNavigate, useParams } from "react-router-dom";
import {
Mic,
MicOff,
Video,
VideoOff,
PhoneOff,
Users,
MessageSquare,
Share,
Settings,
} from "lucide-react";
import Button from "../ui/Button";
import { useMeetings } from "../../contexts/MeetingsContext";
import { useAuth } from "../../contexts/AuthContext";
import ParticipantGrid from "./ParticipantGrid";
import { Participant } from "../../types";
import { useSoc } from "../../hooks/usesoc";
import { PeerService } from "../../utils/peer";
const VideoConference: React.FC = () => {
const { getCurrentMeeting, leaveMeeting } = useMeetings();
const { user } = useAuth();
const meeting = getCurrentMeeting();
const [isMuted, setIsMuted] = useState(false);
const [isVideoOn, setIsVideoOn] = useState(true);
const [showParticipants, setShowParticipants] = useState(false);
const [showChat, setShowChat] = useState(false);
const [participants, setParticipants] = useState<Participant[]>([]);
const localVid = useRef<HTMLVideoElement | null>(null);
const [stat, setStat] = useState<boolean>(false);
const remoteVid = useRef<HTMLVideoElement | null>(null);
const { roomid } = useParams();
const peerManager = useRef<PeerService | null>(null);
useEffect(() => {
const mockParticipants: Participant[] = [
{
id: user?.id || "1",
name: user?.name || "You",
email: user?.email || "",
avatar: user?.avatar,
isMuted: true,
isVideoOn: true,
isHost: true,
mediaStream: localVid,
},
];
setParticipants(mockParticipants);
}, [user]);
const soc = useSoc();
async function playVideoFromCamera() {
try {
const constraints: MediaStreamConstraints = {
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
frameRate: { ideal: 30 },
facingMode: "user",
},
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 44100,
},
};
console.log("Requesting user media with constraints:", constraints);
const stream = await navigator.mediaDevices.getUserMedia(constraints);
console.log("Media stream obtained:", stream);
console.log("Video tracks:", stream.getVideoTracks());
console.log("Audio tracks:", stream.getAudioTracks());
return stream;
} catch (error) {
console.error("Error opening video camera.", error);
return null;
}
}
function changeParticipants(s: Participant) {
setParticipants((prev: any) => {
console.log("remote stream", s.mediaStream);
return [...prev, s];
});
}
function populateRemoteStreams() {
peerManager.current?.remoteStreams.forEach((stream, peerId) => {
const alreadyExists = participants.some((p) => p.id === peerId);
if (alreadyExists) return;
console.log("pouplate");
const newRef = React.createRef<HTMLVideoElement>();
changeParticipants({
id: peerId,
name: "yo",
avatar: "isudgfius",
isMuted: true,
isVideoOn: true,
isHost: false,
mediaStream: newRef,
stream: stream,
});
});
}
// Set up video independently of WebSocket
useEffect(() => {
if (peerManager.current == null) {
playVideoFromCamera().then(async (stream) => {
if (stream && localVid.current !== null) {
localVid.current.srcObject = stream;
// Force the video to play
localVid.current
.play()
.catch((e) => console.error("Error playing video:", e));
}
if (stream && stat) {
peerManager.current = new PeerService(
soc.current as WebSocket,
stream
);
peerManager.current.addPeer().then(async () => {
// await peerManager.current?.addLocalStream(stream as MediaStream);
});
// const stream = await playVideoFromCamera()
}
});
}
}, [stat]);
useEffect(() => {
let check = peerManager.current?.remoteStreams.size;
function checkRemoteStreams() {
if (peerManager.current) {
// console.log("Remote streams:", peerManager.current.remoteStreams);
if (check != peerManager.current?.remoteStreams.size) {
populateRemoteStreams();
check = peerManager.current.remoteStreams.size;
}
}
}
const intervalId = setInterval(checkRemoteStreams, 1000);
return () => clearInterval(intervalId);
}, []);
useEffect(() => {
if (soc.current)
soc.current.onopen = () => {
setStat(true);
};
if (!soc.current || soc.current.readyState !== WebSocket.OPEN) {
return;
}
if (soc.current)
soc.current.send(
JSON.stringify({
type: "create-room",
room_id: roomid,
})
);
}, [roomid, stat]);
// WebSocket message handling
useEffect(() => {
if (!soc.current) return;
const socket = soc.current;
socket.onmessage = (m) => {
if (peerManager.current)
peerManager.current.handleSignal(JSON.parse(m.data));
};
}, [soc.current?.readyState, stat]);
// Update local participant when video stream is available
useEffect(() => {
if (localVid.current && localVid.current.srcObject) {
setParticipants((prev) =>
prev.map((p) =>
p.id === user?.id
? { ...p, isVideoOn: true, mediaStream: localVid }
: p
)
);
}
}, [localVid.current?.srcObject, user?.id]);
const handleLeaveMeeting = () => {
leaveMeeting();
// navigate("/dashboard");
};
const toggleMute = () => {
setIsMuted(!isMuted);
// Update participant state
setParticipants((prev) =>
prev.map((p) => (p.id === user?.id ? { ...p, isMuted: !isMuted } : p))
);
};
const toggleVideo = () => {
setIsVideoOn(!isVideoOn);
// Update participant state
setParticipants((prev) =>
prev.map((p) => (p.id === user?.id ? { ...p, isVideoOn: !isVideoOn } : p))
);
};
const handleShare = () => {
// Copy meeting link to clipboard
const meetingLink = `${window.location.origin}/video-conference/${roomid}`;
navigator.clipboard.writeText(meetingLink).then(() => {
// Could add a toast notification here
console.log("Meeting link copied to clipboard");
});
};
const handleSettings = () => {
// Open settings modal or panel
console.log("Settings clicked");
};
return (
<div className="h-screen bg-gray-900 flex flex-col">
{/* Header */}
<header className="bg-gray-800/50 backdrop-blur-sm border-b border-gray-700 px-6 py-4">
<div className="flex items-center justify-between">
<div>
<h1 className="text-lg font-semibold text-white">
{meeting?.title || `Room: ${roomid}`}
</h1>
<div className="flex items-center space-x-4 text-sm text-gray-400">
<span>Meeting ID: {meeting?.meetingCode || roomid}</span>
<span>•</span>
<span>{participants.length} participants</span>
</div>
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => setShowParticipants(!showParticipants)}
leftIcon={<Users className="w-4 h-4" />}
>
<span className="hidden sm:inline">Participants</span>
<span className="sm:hidden">{participants.length}</span>
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setShowChat(!showChat)}
leftIcon={<MessageSquare className="w-4 h-4" />}
>
<span className="hidden sm:inline">Chat</span>
</Button>
</div>
</div>
</header>
{/* Main Content */}
<div className="flex-1 flex">
{/* Video Grid */}
<div className="flex-1 p-4">
<ParticipantGrid participants={participants} />
</div>
{/* Sidebar */}
{(showParticipants || showChat) && (
<div className="w-80 bg-gray-800/50 backdrop-blur-sm border-l border-gray-700 p-4">
{showParticipants && (
<div className="mb-6">
<h3 className="text-lg font-semibold text-white mb-4">
Participants ({participants.length})
</h3>
<div className="space-y-2">
{participants.map((participant) => (
<div
key={participant.id}
className="flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-700/50"
>
{!participant.isVideoOn ? (
<img
src={
participant.avatar ||
`https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&w=50&h=50&dpr=2`
}
alt={participant.name}
className="w-8 h-8 rounded-full object-cover"
/>
) : (
<></>
)}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-white truncate">
{participant.name}
{participant.isHost && (
<span className="ml-2 text-xs text-blue-400">
(Host)
</span>
)}
</p>
</div>
<div className="flex items-center space-x-1">
{participant.isMuted ? (
<MicOff className="w-4 h-4 text-red-400" />
) : (
<Mic className="w-4 h-4 text-green-400" />
)}
{participant.isVideoOn ? (
<Video className="w-4 h-4 text-green-400" />
) : (
<VideoOff className="w-4 h-4 text-red-400" />
)}
</div>
</div>
))}
</div>
</div>
)}
{showChat && (
<div>
<h3 className="text-lg font-semibold text-white mb-4">Chat</h3>
<div className="bg-gray-700/50 rounded-lg p-4 text-gray-400 text-sm text-center">
Chat feature coming soon...
</div>
</div>
)}
</div>
)}
</div>
{/* Controls */}
<div className="bg-gray-800/50 backdrop-blur-sm border-t border-gray-700 px-6 py-4">
<div className="flex items-center justify-center space-x-4">
<Button
variant={isMuted ? "danger" : "secondary"}
size="lg"
onClick={toggleMute}
className="w-12 h-12 rounded-full p-0 flex items-center justify-center"
title={isMuted ? "Unmute" : "Mute"}
>
{isMuted ? (
<MicOff className="w-5 h-5" />
) : (
<Mic className="w-5 h-5" />
)}
</Button>
<Button
variant={!isVideoOn ? "danger" : "secondary"}
size="lg"
onClick={toggleVideo}
className="w-12 h-12 rounded-full p-0 flex items-center justify-center"
title={isVideoOn ? "Turn off camera" : "Turn on camera"}
>
{isVideoOn ? (
<Video className="w-5 h-5" />
) : (
<VideoOff className="w-5 h-5" />
)}
</Button>
<Button
variant="secondary"
size="lg"
onClick={handleShare}
className="w-12 h-12 rounded-full p-0 flex items-center justify-center"
title="Share meeting"
>
<Share className="w-5 h-5" />
</Button>
<Button
variant="secondary"
size="lg"
onClick={handleSettings}
className="w-12 h-12 rounded-full p-0 flex items-center justify-center"
title="Settings"
>
<Settings className="w-5 h-5" />
</Button>
<Button
variant="danger"
size="lg"
onClick={handleLeaveMeeting}
className="w-12 h-12 rounded-full p-0 flex items-center justify-center"
title="Leave meeting"
>
<PhoneOff className="w-5 h-5" />
</Button>
</div>
</div>
</div>
);
};
export default VideoConference;
Participantgrid.tsx
import React, { useEffect, useRef } from "react";
import { Mic, MicOff, Video, VideoOff, Crown } from "lucide-react";
import { Participant } from "../../types";
interface ParticipantGridProps {
participants: Participant[];
}
const ParticipantGrid: React.FC<ParticipantGridProps> = ({ participants }) => {
const videoRefs = useRef<Record<string, HTMLVideoElement | null>>({});
useEffect(() => {
participants.forEach((participant) => {
const videoEl = videoRefs.current[participant.id];
if (
participant.isVideoOn &&
videoEl &&
participant.stream &&
participant.stream.getTracks().length > 0 &&
videoEl.srcObject !== participant.stream
) {
videoEl.srcObject = null;
videoEl.srcObject = participant.stream;
videoEl
.play()
.then(() => console.log("Playing:", participant.id))
.catch((error) => {
console.error("Error playing video for", participant.id, error);
});
videoEl.onloadedmetadata = () => {
videoEl.play().catch((error) => {
console.error("Error playing video for", participant.id, error);
});
};
}
});
}, [participants]);
const getGridCols = () => {
const count = participants.length;
if (count === 1) return "grid-cols-1";
if (count === 2) return "grid-cols-2";
if (count <= 4) return "grid-cols-2";
if (count <= 6) return "grid-cols-3";
return "grid-cols-4";
};
return (
<div className={`grid ${getGridCols()} gap-4 h-full`}>
{participants.map((participant) => (
<div
key={participant.id}
className="relative bg-gray-800 rounded-lg overflow-hidden group"
>
{/* Video/Avatar */}
<div className="w-full h-full flex items-center justify-center vid_area">
{participant.isVideoOn && !participant.isHost ? (
participant.mediaStream ? (
<>
<video
key={participant.id}
ref={(el) => {
videoRefs.current[participant.id] = el;
}}
autoPlay
muted={participant.isMuted}
playsInline
className="w-full h-full object-cover transform scale-x-[-1]"
onLoadedMetadata={(e) => {
console.log("Loaded metadata for", participant.id);
}}
onError={(e) =>
console.error("Video error for", participant.id, e)
}
/>
{/* <canvas ref={canvasRef} className="..." /> */}
</>
) : (
<div className="w-full h-full bg-gradient-to-br from-blue-500/20 to-purple-500/20 flex items-center justify-center">
<div className="text-6xl font-bold text-white/20">
{participant.name?.charAt(0).toUpperCase()}
</div>
</div>
)
) : (
<div className="flex flex-col items-center justify-center space-y-4">
<img
src={
participant.avatar ||
`https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&w=200&h=200&dpr=2`
}
alt={participant.name}
className="w-20 h-20 rounded-full object-cover border-4 border-gray-600"
/>
<div className="flex items-center space-x-2 px-3 py-1 bg-gray-700/80 rounded-full">
<VideoOff className="w-4 h-4 text-red-400" />
<span className="text-sm text-gray-300">Camera off</span>
</div>
</div>
)}
</div>
{/* Participant Info */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<span className="text-white font-medium text-sm">
{participant.name}
</span>
{participant.isHost && (
<Crown className="w-4 h-4 text-yellow-400" />
)}
</div>
<div className="flex items-center space-x-1">
<div
className={`p-1.5 rounded-full ${
participant.isMuted ? "bg-red-500" : "bg-green-500"
}`}
>
{participant.isMuted ? (
<MicOff className="w-3 h-3 text-white" />
) : (
<Mic className="w-3 h-3 text-white" />
)}
</div>
</div>
</div>
</div>
{/* Speaking Indicator */}
{!participant.isMuted && (
<div className="absolute top-4 left-4 w-3 h-3 bg-green-400 rounded-full animate-pulse" />
)}
</div>
))}
</div>
);
};
export default ParticipantGrid;
when i try to tlog the remote streams they get logged but cant be played
when i checked the readystate of the stream was 0 and the network state was 2
Change iframe src attribute before iframe loads for consent management
I have a WordPress site that uses some plugin which can display iFrames.
Now I want to use a consent manager (Usercentrics, fyi) that requires to change the src attribute to data-src in order to prevent the iframe from loading before consent has been given. The plugin mentioned above cannot do this.
Now my first approach was to change the src attribute upon DOMContentLoaded using JavaScript, like this:
<script>
document.addEventListener("DOMContentLoaded", function(){
var iframeSrcValue = document.getElementById("partnerIFrame").src;
document.getElementById('partnerIFrame').removeAttribute("src");
document.getElementById('partnerIFrame').setAttribute("data-src", iframeSrcValue);
});
</script>
I would insert this script into the head tag of the WordPress site using a plugin.
However, that seems to be not working well, as the iframe starts loading for a fraction of a second, before the script takes effect – which is not nice and also doesn’t fullfill the requirement of a consent management solution to only load third party ressource after consent.
What would be the best approach to achieve this goal?
Any hints are appreciated!
webpack file doesnt refer node_modules present in the imported path , but refer for it its node_modules
i am webpacking a module from pathA , node_modules are ignored
i import the pathA index.js in pathB node index.js dynamically which has the required node_modules
but when i run pathB node index.js , i get the following error
“error”: “Cannot find module ‘@playwright/test’nRequire stack:n- D:PersonalpackAdistindex.jsn- D:Personalpackbindex.js”
pathA/
- dist/index.js # Webpacked bundle (externals: Playwright)
- src/index.ts
- webpack.config.js # With externals config
pathB/
- node_modules/ # Contains @playwright/test
- index.js # Imports pathA/dist/index.js
I will attach my config
const path = require("path");
const nodeExternals = require("webpack-node-externals");
module.exports = {
entry: "./src/index.ts",
target: "node",
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
library: {
name: 'test-api',
type: 'umd',
},
clean: true,
},
mode: "development",
resolve: {
extensions: [".ts", ".js" ],
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /.ts$/,
use: "ts-loader",
exclude: /node_modules/,
},
{
test: /(LICENSE|.d.ts|.md|.apk|.png|.sh|.ps1|.dmg|.css|.html|.exe|.svg|.ttf)$/,
use: ["null-loader"],
},
{
test: /.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
targets: { node: "current" },
},
],
],
},
},
},
],
},
devtool: "source-map",
};
How to mutate a nested object in the beforeChange hook in a collection?
I’m trying to mutate a nested object in the beforeChange hook of a Payload CMS collection definition:
const newContent = structuredClone(props.data.content);
const newData = await props.req.payload({...}) // making an internal call to get the new data
newContent.prop.prop.prop.prop.prop1 = newData.prop1
newContent.prop.prop.prop.prop.prop2 = newData.prop2
console.log(newContent)
return {
...props.data,
content: newContent
}
When I execute console.log(newContent), I get the updated object, but when I console.log(data) in the afterChange hook, the unmodified object is printed. Moreover, I can see in MongoDB Compass that the document was not updated with my new object.
I’m using Payload v3. This is an oversimplified version of my code. The original code has 2 for of loops inside which the payload calls are made. However, I thought this should not impact the result, since the console.log at the end of beforeChange is displaying the correct data. What am I missing?
jsPDF adds image at incorrect size and position when using dimensions from getBoundingClientRect and clientHeight/clientWidth
I am trying to add an HTMLElement to a PDF after converting it to an image using "toPng" from "html-to-image".
The HTMLElement exists inside another, that plays the role of the page (used for visualizing the pages before rendering. set to the same dimensions as the pages for the PDF). I calculate the offsets of the image (of the component) using the distance between the bounding boxes of the element and it’s parent (the “page”):
// page header is the element to render!
const { parentElement } = pageHeader;
if (!parentElement) {
return;
}
const parentRect = parentElement.getBoundingClientRect();
const headerRect = pageHeader.getBoundingClientRect();
const xOffset = headerRect.left - parentRect.left;
const yOffset = headerRect.top - parentRect.top;
For the image size i use clientHeight/clientWidth:
const imgHeight = pageHeader.clientHeight;
const imgWidth = pageHeader.clientWidth;
Here is how i create the image of the element and add it to the file:
const headerImgData = await toPng(pageHeader as HTMLElement, {
cacheBust: true,
pixelRatio: 2,
});
pdf.addImage(headerImgData, 'PNG', xOffset, yOffset, imgWidth, imgHeight);
When creating the PDF object, i use ‘px’ as the unit:
const pdf = new jsPDF({ orientation: 'p', unit: 'px', format: 'a4' });
The actual image rendered in the file is way bigger than expected, the same goes for the offsets.
I wanted the image to look the same way the HTMLElement looks in the DOM.
I tried logging the dimensions and they look fine (the same as in the DOM). I also tried other units and converting but the same the problem persists.
p-limit – Javascript modules ES6
On a webpage, I am not using Node.js.
I wanted to use p-limit like this:
<script src="
https://cdn.jsdelivr.net/npm/[email protected]/index.min.js
"></script>
But this doesn’t work.
Cannot use import statement outside a module
Naively, I tried:
<script type="module" src="
https://cdn.jsdelivr.net/npm/[email protected]/index.min.js
"></script>
Which errors out:
Failed to resolve module specifier "yocto-queue".
I eventually realized there are “commonJS” and “ES6”. So I tried:
<script type="module">
import pLimit from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
</script>
Which doesn’t errors out until:
<script type="module">
const l = pLimit(10);
</script>
where it says:
pLimit is not defined
Eventually, this works:
<script type="module">
import pLimit from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
const l = pLimit(10);
</script>
I don’t understand why it needs to be in the same block.
If I modify the code to remove the import/export, I can use it in <script src="changed_plimit.js"></script> (of course, I don’t really want to do that).
Ultimately, I’d like to know if it can be used <script src="https://cdn..."></script>.
Obviously my understanding is limited. Am I completely off?
How to display msg received in API response on page in ReactJs
i have one function which checks for user authorization
export const authUser = (rideId,action) => {
return (dispatch, getState) => {
const postData = {
consumer: "Rapi",
classId: rideId,
action: action,
};
return Api.post(
'/api/xx/yy/auth',
postData,
)
.then((response) => {
const authorized = response && response.authorized === true;
if (authorized) {
console.log("User is authorized");
return true;
} else {
console.warn("User is NOT authorized");
return false;
}
})
.catch((error) => {
console.error(" Authorization failed:", error);
return false;
});
};
};
if user is authorized to access this particular ride in that case we are getting this below response from API
{"authorized":true}
if that particular RideId is not available or he is not authorized to access that ride we are getting below response from API
{
"message": "Ride not found. rideId: RD23435OPSMSK76772",
"errorCode": "60000",
"timestamp": "2025-07-02T08:34:57.241+00:00"
}
as of now i am displaying a common message for user not authorized and ride not found error .
But i want to display this particular message which we are receiving from API response.
so when ride Id is not found i am getting above response and catch block is executing.
We are getting response in ride not found case. so then block should get execute.
Please Help. Thanks in Advance
Input checkboxes change value=”wording” dependent on checked unchecked [closed]
I have multiple inputs:
<input type="checkbox" name="t2" id="t2" value="">
<input type="checkbox" name="t3" id="t3" value="">
<input type="checkbox" name="t4" id="t4" value="">
I would like help with the JavaScript code so that when a user checks the input, the individual input values (value=””) become the word “show”, unchecked, it becomes the word “hide”.
Is it possible to manually reject files in a FileUpload before the form gets submitted?
In a Filament form I have the following field:
FileUpload::make('Files')
->acceptedFileTypes(self::$filetypes)
->disk('local')
->moveFiles()
->multiple(true)
Now before the form is submitted I want to validate the uploaded files based on their (original) names.
I have tried using ->rules() but that only takes effekt after submitting the form.
I have also tried using ->afterStateUpdated() like this:
->afterStateUpdated(function($state) {
foreach($state as $idx => $file) {
$filename = $file->getClientOriginalName();
if($filename == 'test.csv') {
$file->delete();
unset($state[$idx]);
}
}
})
This works for deleting the files and probably would not submit them (I have not tried that)
but it does not update the field state to show that the file has been rejected.
Basically I’d want a $file->reject() method so that my field looks like this. The file in the image was rejected due to its size, so there must be a way to validate the files before submission.