Why is node:http behaving unexpectedly in Deno?

My Deno app is using the node:http package to create a server. I have noticed that the behaviour of this package seems to differ between Deno and Node, and that it does not comply with the documentation. Specifically, the value of Request.rawHeaders is not as I expect it to be.

I used the following code to inspect the value of Request.rawHeaders.

import * as http from "node:http"
createServer((req, res) => {
  // My console.logs here
  res.end()
}).listen(80)

Similar code was used in Node to test the same value. After making a request to both servers, I observed the following results.

Value passed to console.log Console result in Node Console result in Deno
req.rawHeaders [‘Host’, ‘localhost’, ‘Connection’, ‘keep-alive’, …] result
req.rawHeaders.toString() Host,localhost,Connection,keep-alive, … [object Headers]
JSON.stringify(req.rawHeaders) [“Host”,”localhost”,”Connection”,”keep-alive”, …] {}
typeof req.rawHeaders object object
Object.keys(req.rawHeaders) [‘0’, ‘1’, ‘2’, ‘3’, …] []
Object.getOwnPropertyNames(req.rawHeaders) [‘0’, ‘1’, ‘2’, ‘3’, …] []
Array.isArray(req.rawHeaders) true false
Object.getPrototypeOf(req.rawHeaders) Object(0) [] Headers {}

According to the docs on rawHeaders, the value should be an array of strings. It is not in Deno. I can’t even understand what it is. A direct console.log on the value shows that it is an object with properties, but I can’t access them. I tried rawHeaders['host'] and rawHeaders.host. They both return undefined. How is this possible? What is console.log doing to access the values?

What am I doing wrong? Is this a bug?

Additional details

I discovered this while trying to use vhost in Deno. I noticed that it does not redirect to the correct handler based on the subdomain, presumably because it can’t read the host header. I’m using express instead of oak because oak requires too much boilerplate for my current requirements. Also, I don’t know if I can use vhost with oak.

morphing string to date in arktype

Im trying to to use the new Temporal date api in javascript and while trying to write a type using arktype library that takes in a string and morphs it into a Temporal zoned datetime.

Example:

import { Temporal } from '@js-temporal/polyfill';
import { type } from 'arktype';

const dateSchema = type("string").pipe((str: string) => Temporal.ZonedDateTime.from(str));

type DateParse = typeof dateSchema.infer;

const dateParse: DateParse = Temporal.Now.zonedDateTimeISO().toString();

Error:
Type 'string' is not assignable to type { readonly era: string | undefined; ...

The expected result is that the the dateSchema type would be string but it would morph it into a Temporal date.

How is WebKit / Safari garbage collecting JSON data in a React app?

We are experiencing random crashing of a relatively heavy webpage, which has lots of videos, and about 10 API endpoints polling every 3-5 seconds. Some of the endpoints are returning 1-3MB of JSON at a time, so there is a lot of churn of objects.

I haven’t yet figured out how to debug XCode WebView or dig into memory usage in Safari’s debugger. But I suspect that on iOS Safari, the reason why it’s crashing randomly is because the memory is growing too large, and Safari just cuts the page off and refreshes the page.

That’s my best guess so far on why the app is crashing.

So we are using Redux + React. We are doing some virtualization in React (only render what is visible), but what I’m wondering about for this question is what is happening to the churning JSON data as megabytes are coming in, react is running (recomputing large virtual DOM diffs, basically our whole UI is being recomputed every second due to redux selectors being used throughout React components, and that causes all components in the tree to re-compute).

So basically, React is generating a lot of objects. I don’t think there is any global variables and memory leaks. But just the data is building up with these 1-3MB JSON requests coming in (and converting to even larger JS objects), then presumably garbage collection is occurring, but WHEN.

How does garbage collection work roughly speaking in Safari / WebKit? Is it possible that the data is building up faster and the objects aren’t being cleared from the underlying memory system fast enough? Even though there’s probably no memory leaks (due to React’s nature)?

Web scraping in Python: data populated via javascript (I think)

I am trying to scrape the race result data from this link. However, the data is not in the HTML code if I just scrape that. The body seems to be generated with a javascript file:

<body id='rtrt' >
<div id='rt-app'></div><script type='text/javascript' src='https://track.rtrt.me/js/embed.js?appid=5f59147ca5f987352f8b4582&event=TLMR-CAMBRIDGE-HALF-2024&responsive=1'></script>
</body>

I looked at the script that it links too and it’s pretty confusing. My question is if it is even possible for me to scrape the result data, and if so how I should go about trying to do it.

Python using requests get html data after page has been altered by JS

I’ve tried searching for something like this online but haven’t actually found any solutions for my problem.

I’m trying to make a website to be a price tracker for the products they sell, since I’ve just started making this website I need to input all the products into my database for them to be tracked in the first place, but the issue is, their full product sitemap doesn’t seem to be up to date with their products so I can’t use that, so I’m using the regular products list page.

Now, the actual issue is that when you use a url with a parameter to pick a particular page it actually always gets the content for page 1, and then uses javascript to update the html to the actual correct content for the page number. I’m using requests and BeautifulSoup to get the page and parse through it.

Its not entirely relevant but here is my code:

class CategoryScraper():

    def __init__(self, url):
        self.url = url
        self.headers = {
            'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:131.0) Gecko/20100101 Firefox/131.0'
        }
        self.products = []
        self.html = None

    def get_data(self):
        self.products = []
        self.get_html()

        product_list = self.html.find('div', attrs={'class': 'productList'})
        product_containers = product_list.find_all('div', attrs={'class': 'itemContainer'})

        for product in product_containers:
            anchor = product.find('a')
            product_name = anchor.find('div', attrs={'class': 'itemTitle'}).get_text()
            product_price = anchor.find('div', attrs={'class': 'itemPrice'}).find('span').get_text().split('xa0')[1]
            product_url = anchor['href']
            self.products.append(
                {'product_name': product_name, 'product_price': product_price, 'product_url': product_url})

    def get_html(self):
        page = requests.get(self.url, headers=self.headers)
        self.html = BeautifulSoup(page.content, 'html5lib')

    def change_url(self, url):
        self.url = url
        self.get_html()

    def get(self):
        self.get_data()
        return self.products

I’m aware I might need to use a different library to wait for JavaScript to load and finish to get the page data, but I only started web scraping today so I don’t really know what libraries there are and their capabilities.

Base64 string bigger than ca 76456 bytes is not send via axios post request

I try to send base64 string via axios post request

  const sendData = async () => {
    if (newImg) {
      console.log(fileSizeInBytes(newImg));

      const result = await axios.post(
        `${process.env.REACT_APP_API}/uploadData`,
        {
          type: "image",
          data: newImg,
          userId: user._id,
        },
        {
          headers: {
            authtoken,
          },
        }
      );
      try {
        console.log("data", result.data);
        setImageUrl(result.data.url);
      } catch (error) {
        console.log("error", error);
      }
    }
}

Here I have a function which calculates base64 string size:

function fileSizeInBytes(base64) {
    const base64String = base64.replaceAll("=", "");
    const bytes = base64String.length * (3 / 4);

    return bytes;
  }

But the base64 string is sent only if it’s size is not bigger than ca 76456 bytes. The problem is clearly with the size of the string-if it’s smaller then it gets through.
Here is also my serverside setup

const express = require("express");
const app = express();
const dotenv = require("dotenv");
const mongoose = require("mongoose");
dotenv.config();
const bodyParser = require("body-parser");
const cors = require("cors");

const authRoutes = require("./routes/auth");
const userRoutes = require("./routes/user");
const cloudinaryRoutes = require("./routes/cloudinary");
const chatRoutes = require("./routes/chat");
const messageRoutes = require("./routes/message");

mongoose
  .connect(process.env.MONGO_URI)
  .then(() => console.log("DB connected!!"));

mongoose.connection.on("error", (err) => {
  console.log(`DB connection error: ${err.message}`);
});

app.use(bodyParser.json());
app.use(cors());

app.use("/api", authRoutes);
app.use("/api", userRoutes);
app.use("/api", cloudinaryRoutes);
app.use("/api", chatRoutes);
app.use("/api", messageRoutes);

app.use(function (err, req, res, next) {
  if (err.name === "UnauthorizedError") {
    res.status(401).json({
      error: "Unauthorized",
    });
  }
});

const port = process.env.PORT || 5000;

app.listen(port, () => {
  "Server is up and running on port " + port;
});

Is it an axios problem? What am I doing wrong here?

How to ignore inferred type errors from jsx files in the tsx files?

I have a TSX file that imports a JSX component in a large code base, sort of like this:

// Foo.tsx

import Bar from 'bar.jsx'

// ...

<Bar
  className="red"
>
  Hello, world!
</Bar>

// ...

and

const Bar = forwardRef((props, ref) => {
  return <div className={props.className}>{props.children}</div>
}

When running my application, I get a large amount of compilation errors like:
TS2322: Type '{ children: (string | Element)[]; className: string; }' is not assignable to type 'IntrinsicAttributes & RefAttributes<any>'. Property 'children' does not exist on type 'IntrinsicAttributes & RefAttributes<any>'.

Unfortunately, its not feasible for me to upgrade all of my jsx components to tsx at this time. So, is there an easy way to configure TS to stop trying to infer types from my unmigrated JSX files and prevent these errors?

How to determine if an element is CSS sticky to the top OR bottom?

I need to apply a CSS class to a position:sticky element when it has become stuck. I’ve got this working when I use a top position, but I’m having trouble figuring out how to determine this for a bottom position. I suppose I need to take the height into account somewhere, but I’m just not sure what the best thing would be here.

I also need this to work with offsets, just just a top or bottom position of 0. Here’s what I have so far

const stickyElements = [...document.querySelectorAll(".sticky")];
window.addEventListener("scroll", () => {
  stickyElements.forEach((el) => toggleClassIfStuck(el))
});
window.dispatchEvent(new Event('scroll')); //trigger initially

function toggleClassIfStuck(el){
  const computedStyles = getComputedStyle(el);

  if (this.canBeStuck(computedStyles)) {
    const hasTopPositionSet = computedStyles.top !== 'auto';
    const hasBottomPositionSet = computedStyles.bottom !== 'auto';

    if (hasTopPositionSet || hasBottomPositionSet) {
      el.classList.toggle('is-stuck', this.isStuck(el, computedStyles, hasBottomPositionSet))
    }
  }
}

function canBeStuck(computedStyles) {
  return computedStyles.display !== 'none' && computedStyles.position === 'sticky';
}

function isStuck(el, computedStyles, shouldUseBottomPosition) {
  const offsetParent = el.offsetParent; //the element which this element is relatively sticky to
  const rect = el.getBoundingClientRect();
  const parentRect = offsetParent.getBoundingClientRect();

  if (shouldUseBottomPosition) {
    //this isn't correct, but not sure what to check here!
    const elBottom = parseInt(computedStyles.bottom, 10);
    return rect.top - rect.bottom === elBottom;
  } else {
    const elTop = parseInt(computedStyles.top,10);
    return rect.top === elTop;
  }
}
.sticky               {
  position:sticky;
  background: #EEE;
  padding: .5rem;
  border: 1px solid #DDD;
  transition: all 200ms;
}
.sticky-top           { top:0; }
.sticky-top-offset    { top: 1rem;}
.sticky-bottom        { bottom: 0; }
.sticky-bottom-offset { bottom: 1rem; }

.is-stuck{
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.25);
  background: lightskyblue;
}

main{ display: flex; gap:.5rem;}
section{ height:120vh; width: 40%; }
<main>
  <section>
    <br>
    
    <div id="one" class="sticky sticky-top">Top</div>
    
    <br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br>
    
    <div class="sticky sticky-bottom">Bottom</div>
    
    <br>
  </section>
  
  <section>
    <br><br><br><br>
    
    <div class="sticky sticky-top-offset">Top with offset</div>
    
    <br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br>
    <br><br><br><br><br><br><br><br><br><br><br><br>
    
    <div class="sticky sticky-bottom-offset">Bottom with offset</div>
    
    <br><br><br><br>
  </section>
</main>

Chart JS donut with rounded edges in one direction and white border between the segements

Chart js Donut segment is not displayed in one direction. First and last segments are not working as expected enter image description here

Expectation : Need chart js donut rounded segments displayed in one direction with white border between the segments
enter image description here

Code

// Create a custom Doughnut type with rounded segments
Chart.defaults.RoundedDoughnut = Chart.helpers.clone(Chart.defaults.doughnut);
Chart.controllers.RoundedDoughnut = Chart.controllers.doughnut.extend({
  draw: function(ease) {
    var ctx = this.chart.ctx;
    var easingDecimal = ease || 1;
    var arcs = this.getMeta().data;
    var borderWidth = 20; // Width of the white border for space between segments
    Chart.helpers.each(arcs, function(arc, i) {
      var vm = arc._view;
      var startAngle = vm.startAngle;
      var endAngle = vm.endAngle;
      var radius = (vm.outerRadius + vm.innerRadius) / 2;
      var thickness = (vm.outerRadius - vm.innerRadius) / 2;
      ctx.save();
      ctx.translate(vm.x, vm.y);
      // Draw each arc segment with a white border to create spacing
      ctx.beginPath();
      ctx.arc(0, 0, radius, startAngle, endAngle);
      ctx.lineWidth = thickness * 2 + borderWidth; // Increase width to add border
      ctx.strokeStyle = '#FFFFFF'; // Set border color to white
      ctx.lineCap = 'round'; // Ensure all segments are rounded on both ends
      ctx.stroke();
      // Draw inner colored arc over the white border to make it look like a gap
      ctx.beginPath();
      ctx.arc(0, 0, radius, startAngle, endAngle);
      ctx.lineWidth = thickness * 2;
      ctx.strokeStyle = vm.backgroundColor; // Set segment color
      ctx.stroke();

      ctx.restore();
    });
  }
});
// Initialize the chart
window.onload = function() {
  new Chart(document.getElementById('usersChart'), {
    type: 'RoundedDoughnut',
    data: {
      datasets: [{
        data: [10, 10, 10, 10, 10, 10, 10, 10], // Adjust data values for even segments
        backgroundColor: [
          '#5da4e7', '#8fbbe7', '#addbf0', '#4b8de7',
          '#4da466', '#8ec486', '#b3dba8', '#63b571'
        ],
        borderWidth: 0
      }]
    },
    options: {
      cutoutPercentage: 70,
      tooltips: {
        enabled: false
      } // Optional: Disable tooltips to prevent hover issues
    }
  });
};
<canvas id="usersChart" width="400" height="400"></canvas>

Why does editing a task list create a new task instead of updating in To-do List (JS)? [closed]

I’m working on a project with multiple JavaScript files, and I’m stuck on an issue with updating task lists. When I try to edit a task, a new task list is created instead of updating the existing one.

the first task is the original, and the second one is created after attempting an update.

I added console logs and noticed that my form values appear correctly during task creation, but when editing the task and clicking ‘Add’, the values clear out before reaching the addTask function, treating it as a new task creation.

I suspect the issue may be due to form.reset() or how the ‘Add’ button is handling the edit as a new task rather than an update, but I’m not sure how to approach debugging this issue.

Expectations: I have a form where I can add tasks, and when I enter edit mode, I expect to update the existing task.

const allTask = () => {

  const newTaskBtn = document.querySelector('.newTask');
  const cancel = document.querySelector('.cancel');
  const dialog = document.querySelector('dialog');
  const form = document.querySelector('form');

  let isEditing = false;
  let editIndex = null;

  class TaskList {
    constructor(name, date, priority, note, completed = false) {
      this.name = name;
      this.date = date;
      this.priority = priority;
      this.note = note;
      this.completed = completed;
    }
  };

  function filterAndRenderMyTasks() {
    const myTasks = taskStore.tasks.filter(task => !task.completed && (!task.date || isTodayOrUpcoming(task.date)));
    renderTaskList(myTasks);
  }

  function isTodayOrUpcoming(date) {
    const today = new Date();
    const taskDate = new Date(date);
    return taskDate >= today;
  }

  filterAndRenderMyTasks();

  function addTask(name, date, priority, note) {

    console.log('Inside addTask:', {
      name,
      date,
      priority,
      note
    });

    if (!name || name.trim() === '') {
      console.error('Task name is required');
      return;
    }

    console.log('isEditing:', isEditing, 'editIndex:', editIndex);

    if (isEditing) {

      if (editIndex !== null && editIndex >= 0 && editIndex < taskStore.tasks.length) {
        taskStore.tasks[editIndex].name = name;
        taskStore.tasks[editIndex].date = date;
        taskStore.tasks[editIndex].priority = priority;
        taskStore.tasks[editIndex].note = note;

        console.log("Task updated at index:", editIndex, taskStore.tasks[editIndex]);
      } else {
        console.error("Invalid editIndex, task not updated");
      }

    } else {
      const newTask = new TaskList(name, date, priority, note);
      taskStore.tasks.push(newTask);
      console.log("Task added:", taskStore.tasks);
    }

    filterAndRenderMyTasks();

    console.log('Task list after rendering:', taskStore.tasks);
  };

  function setupEventListeners() {

    if (newTaskBtn) {
      console.log('newTaskBtn found, attaching listener');

      newTaskBtn.addEventListener('click', (e) => {
        e.preventDefault();
        console.log('Add button clicked');

        const taskFormName = document.querySelector('#taskName').value;
        const taskFormDate = document.querySelector('#dueDate').value;
        const taskFormPriority = document.querySelector('#priority').value;
        const taskFormDescription = document.querySelector('#description').value;

        console.log({
          taskFormName,
          taskFormDate,
          taskFormPriority,
          taskFormDescription
        });

        if (isEditing) {

          addTask(taskFormName, taskFormDate, taskFormPriority, taskFormDescription);

          isEditing = false;
          editIndex = null;

          newTaskBtn.textContent = 'Add';
        } else {
          addTask(taskFormName, taskFormDate, taskFormPriority, taskFormDescription);
        }

        form.reset();
        dialog.close();

      });
    } else {
      console.error('newTaskBtn not found!');
    }

    if (cancel) {
      cancel.addEventListener('click', (event) => {
        event.preventDefault();
        form.reset();
        dialog.close();
      });
    }
  }


  setupEventListeners();

  // Function to open the edit dialog with the selected task's data
  window.editTask = (task, index) => {
    isEditing = true;
    editIndex = index;

    newTaskBtn.textContent = 'Update';

    console.log('Editing task at index:', editIndex, task);

    console.log('isEditing is now:', isEditing);

    //Pre-fill form with existitng task data
    document.querySelector('#taskName').value = task.name;
    document.querySelector('#dueDate').value = task.date;
    document.querySelector('#priority').value = task.priority;
    document.querySelector('#description').value = task.note;

    dialog.showModal();
  };

}
<header>
  <nav>
    <button class="tab addTaskBtn">Add task</button>
    <button class="tab" id="myTasks">My tasks</button>
    <button class="tab" id="todayTask">Today</button>
    <button class="tab" id="upcomingTask">Upcoming </button>
    <button class="tab" id="completedTask">Completed</button>
  </nav>
  <div class="projects-container">
    <div class="projects">
      <p class="projects-title">Projects</p>
      <button class="projectsBtn">Add</button>
    </div>
    <div class="project-content"></div>
  </div>
</header>
<main id="content">
  <!--Tab switch, where task will be displayed-->
  <div id="task-container"></div>
</main>
<dialog>
  <form action="" method="get" class="form">
    <input type="text" name="usersTaskName" placeholder="Write a task name" class="form-content" id="taskName">
    <div class="form-content">
      <label for="dueDate">Due date</label>
      <button id="dueDate">calendar</button>
    </div>
    <div class="form-content">
      <label for="priority">Priority</label>
      <select name="priority" id="priority">
        <option value="none">None</option>
        <option value="high">High</option>
        <option value="medium">Medium</option>
        <option value="low">Low</option>
      </select>
    </div>
    <div class="form-content">
      <label for="description"></label>
      <textarea name="description" id="description" placeholder="What is this task about?"></textarea>
    </div>
    <div class='form-buttons'>
      <button class="newTask">Add</button>
      <button class="cancel" formmethod="dialog">Cancel</button>
    </div>
  </form>
</dialog>

Show Microphone and Camera Permission Pop-up in the Centre like Google Meet in Google Chrome

Google Meet has recently updated their UI (ref). As per the new UI, the microphone and camera permission pop-up (/ confirmation dialog) is prompted in the centre of the screen while dimming out the rest of the screen (image reference attached).

Google Meet Permission Pop-up

I would like to replicate the same, but I’m unable to find any documentation on the same.

How can I achieve that?

I tried using navigator.mediaDevices.getUserMedia() which opens the permission dialog in the top left corner.

Bot para dashskins [closed]

Critério de Busca: O bot precisa buscar por produtos acima de R$ 150, mas no código o filtro está para valores abaixo de R$ 200. Como posso ajustar isso?

Erros Encontrados: Estou recebendo alguns erros relacionados à API e ao uso de asyncio para o bot. Como posso depurar e corrigir esses problemas?

Integração de API: A resposta da API nem sempre vem no formato esperado, e isso causa erros. Qual é a melhor prática para tratar respostas inesperadas da API?

import asyncio
import os
import requests
from apscheduler.schedulers.background import BackgroundScheduler
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes

# URL da API
API_URL = 'https://dashskins.com.br/api/listing/prices'

# Definir as credenciais do Telegram
TOKEN = os.getenv('TELEGRAM_TOKEN', 'SEU_TOKEN_AQUI')  # Substitua pelo seu token

all_deals = []

# Inicializa a aplicação globalmente
application = ApplicationBuilder().token(TOKEN).build()

# Inicia o agendador globalmente
scheduler = BackgroundScheduler()
scheduler.start()

def fetch_all_deals_from_site():
    all_deals.clear()
    try:
        response = requests.get(API_URL)
        response.raise_for_status()
        deals_data = response.json()

        if not isinstance(deals_data, dict):
            print("Erro: O formato da resposta não é um dicionário.", deals_data)
            return

        for category, items in deals_data.items():
            if isinstance(items, dict):
                for name, price in items.items():
                    # Ajuste o critério de desconto aqui
                    if isinstance(price, (int, float)) and price < 200:  
                        all_deals.append(f"{name}: R${price:.2f}")
            else:
                print(f"Formato inesperado para a categoria '{category}': {items}")

    except requests.exceptions.HTTPError as http_err:
        print(f"Erro HTTP: {http_err}")
    except Exception as e:
        print(f"Erro durante a requisição à API: {e}")

async def send_message_via_telegram(deals, chat_id):
    if deals:
        message_chunk = ""
        for deal in deals:
            if len(message_chunk) + len(deal) + 2 > 4096:  # Limite de mensagem do Telegram
                await application.bot.send_message(chat_id=chat_id, text=message_chunk)
                message_chunk = ""
            message_chunk += deal + "n"
        if message_chunk:
            await application.bot.send_message(chat_id=chat_id, text=message_chunk)
    else:
        await application.bot.send_message(chat_id=chat_id, text="Nenhuma oferta disponível.")

async def fetch_and_send_deals(chat_id):
    fetch_all_deals_from_site()
    await send_message_via_telegram(all_deals, chat_id)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    chat_id = update.message.chat.id
    await update.message.reply_text("Bot iniciado! Você receberá ofertas com desconto a cada 10 minutos.")
    
    if scheduler.get_job(str(chat_id)) is None:
        scheduler.add_job(fetch_and_send_deals, 'interval', minutes=10, args=[chat_id], id=str(chat_id))

async def stop(update: Update, context: ContextTypes.DEFAULT_TYPE):
    chat_id = update.message.chat.id
    job = scheduler.get_job(str(chat_id))
    
    if job:
        scheduler.remove_job(str(chat_id))
        await update.message.reply_text("Bot parado! Não enviando mais ofertas.")
    else:
        await update.message.reply_text("Nenhum job agendado para este chat.")

async def refresh(update: Update, context: ContextTypes.DEFAULT_TYPE):
    chat_id = update.message.chat.id
    fetch_all_deals_from_site()
    await send_message_via_telegram(all_deals, chat_id)

async def main():
    application.add_handler(CommandHandler('start', start))
    application.add_handler(CommandHandler('stop', stop))
    application.add_handler(CommandHandler('refresh', refresh))

    async with application:  # Usando gerenciador de contexto para iniciar o bot
        await application.initialize()
        await application.run_polling()  # Inicia o bot

if __name__ == '__main__':
    try:
        # Check if the event loop is already running
        loop = asyncio.get_event_loop()
        if loop.is_running():
            print("Event loop already running. Starting the bot without asyncio.run()")
            asyncio.ensure_future(main())
        else:
            asyncio.run(main())
    except (KeyboardInterrupt, SystemExit):
        scheduler.shutdown()

How to make an auto-timestamped row in Google Sheets

I found a guide for a timestamp column that works great. But I’d like it as a header row, and cant get it to work. Attempting horozontal auto-timestamp header. Currently no effect. Would also like to figure out how to use a named range here. But just using row 1 would be good enough.

Here is my source

function onEdit(e){
const sh = e.source.getActiveSheet();
sh.getRange ('M' + e.range.rowStart)
.setValue (new Date())
.setNumberFormat ('MM/dd/yyyy HH:MMam/pm');
}

And here is my edit

function onEdit(e){
const sh = e.source.getActiveSheet();
sh.getRange ('1' + e.range.columnStart)
.setValue (new Date())
.setNumberFormat ('MM/dd/yyyy');
}

Currently does nothing. The goal is to populate row with corresponding column edit.

How to toggle button images between active and inactive states and adjust footer-bar styling in Angular?

I’m working on a footer component in Angular and having trouble with two main tasks:

Switching Button States: I want each button in my footer to switch between inactive and active images when clicked. Only one button should appear active at a time, resetting the others to their inactive states. I’m using src/assets for storing the images and have JavaScript set up to handle the switching, but the images don’t seem to toggle as expected.

Footer-Bar Styling Adjustments:

The left and right sections of the footer-bar have a triangle shape that should always be visible. When the page width changes, I’d like only the outer part of these sections to shrink, not the inner triangle part.
Additionally, the top part of the footer-bar should remain visible regardless of page size.

github: https://github.com/tostrauss/footer-cr/tree/main/footer-cr