Text clipped at 37kb when trying to make blob or arraybuffer and upload to sharepoint

I am trying to upload json data as a text file to SharePoint with javascripts stingify method using either blob or array buffer, but I keep getting only the first 37kb of the content saved. This is similar to the browsers dev console using JSON.stringify(json) as shown in the attached image. The text is only 100-200 kb. I can see the Blob and arraybuffers are only 37kb, and if I click the show more in the dev console, it is all there but I don’t have that luxury in the code. WTH, how do I fix this? screenshot

let data = hot.getSourceData()[0]
    console.log(data)

    let json = JSON.stringify(data)
    const file = new Blob([json], { type: 'text/plain' });
  
    sprLib.folder('myfolder').upload({
      name: "textdata.json",
      data: file,
      overwrite: true, //always true
    })

Possible to add code blocks to tags, without using libraries?

Is it possible to obtain code blocks, like these ones that have designated background and font, inside a <textarea>? To visualize, the Notion note-taking app allows users to type plain texts and code blocks at the same time (although they didn’t do it as <textarea> tags):
enter image description here

Is it possible to realize this without using libraries like tinymce, done in vanilla JavaScript or React? I understand the form data submitted form <textarea> is of String type, which makes it difficult to transform into other types.

Thank you!

Type Error with Optional Fields in React Hook Form and Yup Schema

I’m encountering a problem while using React Hook Form with Yup for validation. I have a schema where some fields are optional, but I’m getting a type error related to the resolver.

Here’s my Yup schema:

const yupSchema = yup.object({
  name: yup.string()
    .typeError('Name must be a string')
    .required('Name is required')
    .min(3, 'Name must be at least 3 characters long'),

  surname: yup.string()
    .matches(/^[a-zA-Z]+$/, 'Surname must contain only letters'),

  age: yup.number()
    .min(0, 'Age must be greater than or equal to 0')
    .max(120, 'Age must be less than or equal to 120'),
});

type YupFormData = yup.InferType<typeof yupSchema>;

In this schema, age and surname are optional fields. However, I am receiving the following error when using the resolver:

Types of parameters options and options are incompatible.
Type 'ResolverOptions<{ surname?: string | undefined; age?: number | undefined; name: string; }>' is not assignable to type 'ResolverOptions<{ name: string; surname: string | undefined; age: number | undefined; }>'.
Property 'surname' is optional in type
{
    surname?: string | undefined;
    age?: number | undefined;
    name: string;
}
but required in type
{
    name: string;
    surname: string | undefined;
    age: number | undefined;
}

Here’s how I’m using the useForm hook:

const {
  control,
  handleSubmit,
  formState: {
    errors, 
  }, 
} = useForm<YupFormData>({
  resolver: yupResolver(yupSchema),
  mode: 'onBlur',
});

I would appreciate any help in resolving this issue. Thank you!

What I tried:

I created a Yup validation schema with optional fields for surname and age. I then set up the useForm hook from React Hook Form, integrating the Yup schema using the yupResolver. I expected the form to validate correctly without errors, even when the optional fields were left blank.

What I was expecting:

I expected that the optional fields (surname and age) would not cause any type errors during validation, allowing the form to submit successfully as long as the required field (name) was filled out correctly. Instead, I encountered a type error related to the resolver, indicating a mismatch in the expected types for the form data.


EDIT:

I’m also encountering an error related to the control when using the following components:

<InputController
  name="name"
  control={control}
  error={errors.name?.message as string}
  label="Name"
/>
<InputController
  name="surname"
  control={control}
  error={errors.surname?.message as string}
  label="Surname"
/>
<InputNumberController
  name="age"
  control={control}
  error={errors.age?.message as string}
  label="Age"
/>

The error message I receive is:

TS2322: Type
Control<{
    name: string;
    surname: string | undefined;
    age: number | undefined;
}, unknown, {
    surname?: string | undefined;
    age?: number | undefined;
    name: string;
}>
is not assignable to type
Control<{
    surname?: string | undefined;
    age?: number | undefined;
    name: string;
}>
The types of _options.resolver are incompatible between these types.
Type 'Resolver<{ name: string; surname: string | undefined; age: number | undefined; }, unknown, { surname?: string | undefined; age?: number | undefined; name: string; }> | undefined' is not assignable to type 'Resolver<{ surname?: string | undefined; age?: number | undefined; name: string; }, any, { surname?: string | undefined; age?: number | undefined; name: string; }> | undefined'.
Type 'Resolver<{ name: string; surname: string | undefined; age: number | undefined; }, unknown, { surname?: string | undefined; age?: number | undefined; name: string; }>' is not assignable to type 'Resolver<{ surname?: string | undefined; age?: number | undefined; name: string; }, any, { surname?: string | undefined; age?: number | undefined; name: string; }>'.
Types of parameters options and options are incompatible.
Type 'ResolverOptions<{ surname?: string | undefined; age?: number | undefined; name: string; }>' is not assignable to type 'ResolverOptions<{ name: string; surname: string | undefined; age: number | undefined; }>'.
Type '{ surname?: string | undefined; age?: number | undefined; name: string; }' is not assignable to type '{ name: string; surname: string | undefined; age: number | undefined; }'.
Property surname is optional in type
{
    surname?: string | undefined;
    age?: number | undefined;
    name: string;
}
but required in type
{
    name: string;
    surname: string | undefined;
    age: number | undefined;
}

This error with control exists even before removing <YupFormData> from the useForm hook. While removing it resolves the resolver error, the control type mismatch persists. Any guidance on how to resolve this would be greatly appreciated!


EDIT: Here is my implementation of the InputController:

type InputControllerProps<T extends FieldValues> = {
  name: Path<T>;
  control: Control<T>;
  rules?: Omit<RegisterOptions<T, Path<T>>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
  error?: string;
  label: string;
};

function InputController<T extends FieldValues>({
  name, control, rules, error, label,
}: InputControllerProps<T>) {
  return (
    <Controller
      name={name}
      control={control}
      rules={rules}
      render={({ field }) => (
        <input
          {...field}
          aria-label={label}
          id={name}
          style={{ borderColor: error ? 'red' : 'default' }} // Example of error handling
          placeholder={label}
        />
      )}
    />
  );
}

Encountered illegal break in a for loop [duplicate]

Can anyone help me with this? I’m doing leetcode and I just want to break my for loop inside an if-else statement but I have an illegal error.

var longestCommonPrefix = function(strs) {
    let commonPrefix = [];
    let firstStringArr = strs[0].split("");
    counter = 0;
    
    for (i = 0; i < firstStringArr.length; i++) {
        strs.forEach(string => {
            if (string.includes(firstStringArr[i])) { 
                counter++; 
            };

            if (counter === strs.length && strs.length > 0) {
            commonPrefix.push(firstStringArr[i]);
            } else {
                break;
            }
        })
        counter = 0;
    }
    return commonPrefix.join('');
};

Error message is:

Line 21 in solution.js
                break;
                ^^^^^
SyntaxError: Illegal break statement

How to create a custom WordPress plugin for adding a shortcode?

I’m trying to create a custom WordPress plugin that registers a shortcode so I can use it in posts and pages. I want to know the steps and code needed to make this work from scratch.

What I want:
Create a plugin inside the wp-content/plugins folder.

Add a shortcode (for example, [greet name=”John”]) that outputs something like:

Copy
Edit
Hello, John!
Ability to pass attributes to the shortcode (like name).

What I have tried:
I created a folder custom-shortcode-plugin inside wp-content/plugins.

Added a file custom-shortcode-plugin.php with only a header comment:

Difficulties with responsiveness

I’m having trouble with the responsiveness of a landing page. It doesn’t adapt well to mobile devices. I’ll forward my Style.css and HTML:

Problems: Slide and Forms

I’m working on a responsive navigation menu using HTML and CSS, where the menu should collapse into a hamburger button on smaller screens.

I’ve already implemented the following:

Created a checkbox-based toggle system (.close-menu) to show/hide the menu with a hamburger (☰) and close (×) icon.

Styled the menu with position: fixed, height: 100vh, and flex-direction: column to make it appear as a full-screen overlay.

Used :checked pseudo-class to toggle .menu visibility when clicking the label.

Set display: none and display: block correctly to toggle visibility.

Ensured the menu content centers properly using flex and justify-content: center.

However, I’m still facing issues with how the menu behaves on mobile devices:

Sometimes the hamburger icon appears behind other elements or disappears.

The menu content shifts unexpectedly or doesn’t center vertically.

Overflow issues or layout bugs depending on the screen size.

I’ve checked z-index, position types, and even adjusted padding and margin, but can’t seem to fix the final layout issue.

Here’s the relevant CSS part for the mobile menu:

style.css:
.intro-content {
    position: relative;
    /* top: -8rem; */
    display: grid;
    grid-template-columns: 1fr 1.5fr;
    gap: var(--gap);
    min-height: 100vh;
}

.intro-text-content,
.intro-img {
    display: flex;
    flex-flow: column wrap;
    justify-content: center;

}

.intro-img img,
.intro-img svg {
    max-width: 100%;
    height: auto;
}

.top3-content {
    max-width: 64rem;
    display: flex;
    /* flex-flow: column nowrap; */
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: center;
    min-height: 100vh;
    text-align: center;
}

.grid-one-content {
    display: flex;
    flex-flow: column wrap;
    justify-content: center;
    min-height: 100vh;
}

.grid-main-heading {
    margin-bottom: 1rem;
}

.grid-description {
    padding-bottom: 5rem;
}

.grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--gap);
    counter-reset: grid-counter;
}

.grid h3 {
    font-size: 3rem;
    position: relative;
    padding-left: 5rem;
    padding-bottom: 2rem;
}

.grid h3::before {
    counter-increment: grid-counter;
    content: counter(grid-counter);
    position: absolute;
    font-size: 8rem;
    font-style: italic;
    top: -4rem;
    left: -2rem;
    transform: rotate(5deg);
}

.gallery-img {
    width: 100%;
    max-width: 36rem;
    max-height: 36rem;
    overflow: hidden;
}

.gallery-img img {
    transition: all 300ms ease-in-out;
}

.gallery-img img:hover {
    transform: translate(-3%, 3%) scale(1.2) rotate(5deg);
}

.contact-form {
    grid-column: span 2;
}

.contact-form .form-grid {
    border: none;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--gap);
}

.form-grid legend {
    font-style: italic;
    font-size: 1.6rem;
    margin-bottom: 3rem;
}

.form-group {
    min-width: 32rem;
    flex: 1 1 auto;
}

.form-group label {
    display: block;
    margin-bottom: 1rem;
}

.form-group input,
.form-group textarea {
    border: none;
    background: var(--white-color);
    padding: 1.5rem 2rem;
    width: 100%;
    font-size: 3rem;
    outline: none;
}

.form-group input:focus,
.form-group textarea:focus {
    box-shadow: 0 0 10px 2px var(--light-gray-color);
}

.form-group button {
    border: 0.5rem solid var(--white-color);
    background: var(--primary-color);
    color: var(--white-color);
    padding: 1.5rem 2rem;
    font-size: 3rem;
    cursor: pointer;
    transition: all 300ms ease-in-out;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    border-radius: 0.5rem;
}

.form-group button:hover {
    border: 0.5rem solid var(--white-color);
    background: var(--primary-color-checked);
    color: var(--white-color);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
    transform: scale(1.05);
}

.form-group ::placeholder {
    color: var(--light-gray-color);
}

.footer {
    text-align: center;
    font-size: 1.6rem;
}

.footer a {
    color: var(--primary-color);
}

.footer p {
    margin: 0;
    padding: 3rem;
}


.close-menu {
    display: none;
}

.back-to-top {
    position: fixed;
    bottom: 2rem;
    right: 2rem;
    background: var(--white-color);
    width: 5rem;
    height: 5rem;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    color: var(--primary-color);
    transform: rotate(-90deg);
    border: 0.1rem solid var(--primary-color);
}

a:visited {
    color: inherit;
    /* Define a cor do link visitado */
    text-decoration: none;
    /* Remove a sublinha do link visitado, se necessário */
}

a:hover {
    color: var(--menu-underline-color);
}

a:active {
    color: blue;
}

/* table */

.responsive-table table {
    width: 100%;
    border-collapse: collapse;
    font-family: inherit;
    color: var(--white-color);
    background-color: var(--primary-color);
}

.responsive-table th {
    background-color: var(--primary-color-checked);
    color: var(--white-color);
    padding: 0.75rem 1rem;
    text-align: left;
    font-weight: bold;
}

.responsive-table td {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--primary-color-checked);
}

.responsive-table .text-end {
    color: var(--primary-color);
    text-align: right;
    padding-right: 1rem;
    background-color: var(--white-color);
}

.responsive-table caption {
    color: var(--white-color);
    font-weight: bold;
    font-size: 1.2rem;
    padding: 0.75rem 0;
}


/* mobile */
@media (max-width: 800px) {



    .intro-content,
    .grid {
        grid-template-columns: 1fr;
    }

    .gallery-img {
        width: 100%;
        max-width: 100%;
        max-height: 100%;
    }

    .gallery-img img {
        width: 100%;
    }

    .grid-one-content {
        display: block;
    }

    .menu {
        bottom: 0;
        text-align: center;
    }

    .menu-content,
    .menu-content ul {
        flex-direction: column;
        justify-content: center;
    }

    .menu-content {
        height: 100vh;
    }

    .menu {
        display: none;
    }


    .close-menu-label::after {
        content: '☰';
        position: fixed;
        z-index: 2;
        top: 2rem;
        right: 2rem;
        background: var(--primary-color);
        color: var(--white-color);
        font-size: 3rem;
        line-height: 3rem;
        width: 3rem;
        height: 3rem;
        text-align: center;
        padding: 0.5rem;
        cursor: pointer;
    }

    .close-menu:checked~.menu {
        display: block;
    }

    .close-menu:checked~.close-menu-label::after {
        display: block;
        content: '×';
    }

    .menu-spacing {
        display: none;
    }

    h1 {
        font-size: 4rem;
    }

    h2 {
        font-size: 3.6rem;
    }

    h3 {
        font-size: 3.4rem;
    }

    h4 {
        font-size: 3.2rem;
    }

    h5 {
        font-size: 3rem;
    }

    h6 {
        font-size: 2.8rem;
    }
}

/* fechando o bloco mobile */


/* SLIDE SHOW */
.slider{
    margin: 0 auto;
        width: 800px;
        height: 400px;
        overflow: hidden;
}

.slider {
    border: 0px solid var(--white-color);
    box-shadow: 0px 0px 15px 5px var(--white-color);
    transition: 0.5s ease-in-out, border 0.5s ease-in-out; /* Especifica a transição para a sombra e borda */
}

.slider:hover {
    box-shadow: 0px 0px 25px 10px var(--white-color); /* Aumenta o brilho da sombra no hover */
}

.slides {
    width: 400%;
    height: 400px;
    display: flex;;
}
.slides input {
    display: none;
}
.slide{
    width: 25%;
    position: relative;
    transition: 2s
}
.slide img{
    width: 800px;
}
.manual-navigation{
    position: absolute;
    width: 800px;
    margin-top: -40px;
    display: flex;
    justify-content: center;
}
.manual-btn{
    border: 2px solid var(--menu-underline-color);
    padding: 5px;
    border-radius: 10px;
    cursor: pointer;
    transition: 0.8s;
    background-color: var(--white-color);
}
.manual-btn:not(:last-child) {
    margin-right: 40px;
}
.manual-btn:hover{
    background: var(--primary-color);
}
#radio1:checked ~ .first{
    margin-left: 0;
}
#radio2:checked ~ .first{
    margin-left: -25%;
}
#radio3:checked ~ .first{
    margin-left: -50%;
}
#radio4:checked ~ .first{
    margin-left: -75%;
}
#radio5:checked ~ .first{
    margin-left: -100%;
}
#radio6:checked ~ .first{
    margin-left: -125%;
}
.navigation-auto div{
    border: 2px solid var(--menu-underline-color);
    padding: 5px;
    border-radius: 10px;
    cursor: pointer;
    transition: 1s;
}
.navigation-auto{
    position: absolute;
    width: 800px;
    margin-top: 360px;
    display: flex;
    justify-content: center;
}
.navigation-auto div:not(:last-child){
    margin-right: 40px;
}
#radio1:checked ~ .navigation-auto .auto-btn1{
    background: var(--white-color);
}
#radio2:checked ~ .navigation-auto .auto-btn2{
    background: var(--white-color);
}
#radio3:checked ~ .navigation-auto .auto-btn3{
    background: var(--white-color);
}
#radio4:checked ~ .navigation-auto .auto-btn4{
    background: var(--white-color);
}
#radio5:checked ~ .navigation-auto .auto-btn5{
    background: var(--white-color);
}
#radio6:checked ~ .navigation-auto .auto-btn6{
    background: var(--white-color);
}


/* Para telas pequenas, como tablets */
@media (max-width: 768px) {
    .slider {
        width: 100%;
        height: auto;
        overflow: hidden;
    }

    .slides {
        display: flex;
        width: 600%;
        height: auto;
        transition: margin-left 0.5s ease-in-out;
    }

    .slide {
        width: 16.6667%; /* Cada slide ocupa 1/6 do total */
        flex-shrink: 0;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .slide img {
        width: 100%;
        height: auto;
        object-fit: cover;
    }

    .manual-navigation,
    .navigation-auto {
        width: 100%;
        position: relative;
    }

    .manual-btn {
        padding: 6px;
        margin-right: 10px;
    }

    #radio1:checked ~ .first {
        margin-left: 0;
    }
    
    #radio2:checked ~ .first {
        margin-left: -16.7%;
    }
    
    #radio3:checked ~ .first {
        margin-left: -33.4%;
    }
    
    #radio4:checked ~ .first {
        margin-left: -50%;
    }
    
    #radio5:checked ~ .first {
        margin-left: -66.7%;
    }
    
    #radio6:checked ~ .first {
        margin-left: -83.3%;
    }
}

/* Para celulares menores que 480px */
@media (max-width: 480px) {
    .slider {
        width: 100%;
        height: auto;
        overflow: hidden;
    }

    .slides {
        display: flex;
        width: 600%; /* ou 400% dependendo do número de slides */
        transition: margin-left 0.8s ease-in-out;
      }
      

    .slide {
        width: 50%; /* Ajustado para 2 slides por vez em telas menores */
        flex-shrink: 0;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .slide img {
        width: 100%;
        height: auto;
        object-fit: contain;
        border-radius: 0.5rem;
      }

    .slider,
    .slide img {
    max-width: 100%;
    overflow: hidden;
}


    .slider-container {
        max-width: 800px;
        width: 100%;
        margin: 0 auto;
        position: relative;
        aspect-ratio: 16 / 9; /* Mantém proporção em qualquer tela */
      }

    .manual-navigation,
    .navigation-auto {
        width: 100%;
        position: relative;
    }

    .navigation-auto div {
        opacity: 0.5;
        background: transparent;
      }
      
      #radio1:checked ~ .navigation-auto .auto-btn1,
      #radio2:checked ~ .navigation-auto .auto-btn2,
      #radio3:checked ~ .navigation-auto .auto-btn3,
      #radio4:checked ~ .navigation-auto .auto-btn4,
      #radio5:checked ~ .navigation-auto .auto-btn5,
      #radio6:checked ~ .navigation-auto .auto-btn6 {
        background: var(--menu-underline-color);
        opacity: 1;
      }      

    .manual-btn {
        border: 2px solid var(--menu-underline-color);
        padding: 6px;
        border-radius: 50%;
        cursor: pointer;
        background: transparent;
        transition: background-color 0.3s ease-in-out, transform 0.2s ease-in-out;
      }
      
      .manual-btn:hover {
        background-color: var(--menu-underline-color);
        transform: scale(1.1);
      }
      

    #radio1:checked ~ .first {
        margin-left: 0;
    }

    #radio2:checked ~ .first {
        margin-left: -50%;
    }

    #radio3:checked ~ .first {
        margin-left: -100%;
    }

    #radio4:checked ~ .first {
        margin-left: -150%;
    }

    #radio5:checked ~ .first {
        margin-left: -200%;
    }

    #radio6:checked ~ .first {
        margin-left: -250%;
    }
}

/* Para celulares muito pequenos, como 320px */
@media (max-width: 320px) {
    .slider {
        width: 100%;
        height: auto;
        overflow: hidden;
    }

    .slides {
        display: flex;
        width: 600%;
        height: auto;
        transition: margin-left 0.5s ease-in-out;
    }

    .slide {
        width: 100%; /* Apenas 1 slide por vez */
        flex-shrink: 0;
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .slide img {
        width: 100%;
        height: auto;
        object-fit: cover;
    }

    .manual-navigation,
    .navigation-auto {
        width: 100%;
        position: relative;
    }

    .manual-btn {
        padding: 4px;
        margin-right: 3px;
    }

    #radio1:checked ~ .first {
        margin-left: 0;
    }

    #radio2:checked ~ .first {
        margin-left: -100%;
    }

    #radio3:checked ~ .first {
        margin-left: -200%;
    }

    #radio4:checked ~ .first {
        margin-left: -300%;
    }

    #radio5:checked ~ .first {
        margin-left: -400%;
    }

    #radio6:checked ~ .first {
        margin-left: -500%;
    }
}
html:
<!DOCTYPE html>
<html lang="pt-BR">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Language" content="pt-br">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Portfólio de Marcelo Magalhães - Desenvolvedor Full Stack com projetos em C#, JS, PHP e mais.">
    <meta name="author" content="Marcelo Magalhães">
    <meta name="keywords" content="portfólio, desenvolvedor, fullstack, C#, JavaScript, PHP, sistemas, API, landing page">

    <title>Marcelo Magalhães </></title>
    <link rel="icon" type="image/png" href="assets/img/Brazil.png" />
    <link rel="shortcut icon" href="assets/img/Brazil.png" type="image/png">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Montserrat:[email protected]&family=Open+Sans:[email protected]&display=swap" rel="stylesheet">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">

    <link rel="stylesheet" href="assets/css/style.css">
    <link rel="stylesheet" href="assets/css/variables.css">
    <link rel="stylesheet" href="assets/css/elements.css">
    <link rel="stylesheet" href="assets/css/classes.css">
    <link rel="stylesheet" href="assets/css/menu.css">

    <script src="assets/js/script.js" defer></script>

</head>

<body>
    <input id="close-menu" class="close-menu" type="checkbox" aria-label="Close menu" role="button">
    <label class="close-menu-label" for="close-menu" title="close menu"></label>
    <aside class="menu white-bg">
        <div class="main-content menu-content">
            <h1 onclick="getElementById('close-menu').checked = false">
                <a href="#home" title="Início" aria-label="Ir para a seção inicial">
                    <i class="fas fa-home"></i>
                </a>
            </h1>
            <nav>
                <ul onclick="getElementById('close-menu').checked = false">
                    <li><a href="#home">Sobre mim</a></li>
                    <li><a href="#grid-one">Projetos</a></li>
                    <li><a href="#grid-three">Projetos Casuais</a></li>
                    <li><a href="#slideshow">Slide Show</a></li>
                    <li><a href="#meus-estudos">Meus Estudos</a></li>
                    <li><a href="#contact">Contato</a></li>
                </ul>
            </nav>
        </div>
    </aside>

    <section id="home" class="intro main-bg section">
        <div class="main-content intro-content">
            <div class="intro-text-content">
                <h2>Olá,</h2>
                <p> Me chamo Marcelo Magalhães, sou 
                    formado em Análise e Desenvolvimento de Sistemas,
                    Pós Graduado em MBA - Inteligência Artificial e Big Data,
                    e também possuo Pós Graduação em Desenvolvimento Web Full 
                    Stack. Também sou estudante de Engenharia de Software 5º 
                    Semestre.</p>
            </div>
            <div class="intro-img">
                <img src="assets/img/javascript.svg" alt="Logo de HTML, CSS e JS.">
            </div>
        </div>
    </section>
..........."
    <footer id="footer" class="footer white-bg">
        <p><a rel="nofollow" target="_blank" href="https://www.linkedin.com/in/marcelo-magalhães-513b6b283/">Feito com
                muita dedicação por: Marcelo Magalhães</a></p>
    </footer>
    <a class="back-to-top" href="#" aria-label="Voltar ao topo">
        <i class="fas fa-arrow-up"></i>
    </a>
</body>



</html>

I tried to add @media and it didn’t work. I think the images are too big for the slide, but I wanted them to adapt.

How to handle async/await inside Express middleware without blocking the request-response cycle?

I’m working on a Node.js project with Express and using async/await inside middleware to validate an auth token with an external API.

Here’s my middleware:

const authMiddleware = async (req, res, next) => {
    const token = req.headers.authorization;

    try {
        const user = await fetchUserFromToken(token); // external API call
        req.user = user;
        next();
    } catch (err) {
        res.status(401).json({ error: 'Invalid token' });
    }
};

How can I return an img tag with src parameter?

Jest transformers are written by module.exports-ing process, which returns code.

Extract from the docs:

export type TransformedSource =
  | {code: string; map?: FixedRawSourceMap | string | null}
  | string;

A transformer looks like this:

module.exports = {
  process(src, filename) {
    return { code: `module.exports = 'img';` };
  }
}

If I write the transformer as above the transformer works as expected, whatever it transforms will become an img tag.

However, If I write code: `module.exports = 'img src=""';, I get the following error:

The above error occurred in the <img src=""> component:

at img src=""
    
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

InvalidCharacterError: "img src=""" did not match the Name production: Unexpected syntax in top

It looks like the whole expression is trying to be evaluated to a tag.

Question:

How can I successfully transform the input into an img tag with src information?

What is the best way to download large files in JavaScript where the file is not public? [closed]

I’m looking for an approach that works on most modern browsers, provides a good user experience, and can handle large (~50 GB) files.


Existing Implementation

The code that I am working on downloads a file by making a request for the file, reading the file as a blob and then using createObjectURL to make a link to the file and download it. It’s currently implemented inside an RTK Query endpoint:

getTempStorageDocumentBlobUrl: builder.query<string, string>({
    query: documentId => ({
        url: documentId,
        responseHandler: async (response) => await response.blob()
    }),
    transformResponse: (blob: Blob) => URL.createObjectURL(blob)
})

This is for the most part working okay but has a couple big disadvantages:

  1. It seems to struggle with larger file sizes. I had seen mention on here as well as other sources that browsers would optimise await response.blob() to avoid loading the entire file into memory, but it’s choking with files over 1 GB.
  2. The user receives absolutely no feedback until the entire file is downloaded, at which point it pops up in their download bar. With a fast internet connection and small files it’s not that noticeable, but with large files it’s not a good experience.

The upside is that the file downloads like most other files on the internet, and goes straight into their downloads folder or lets them choose a save location based on their browser settings.


Implementation without RTK Query

I wondered if the problem with the existing implementation struggling with large files was due to something RTK Query was doing, so I tried a stripped back version that uses the most basic demo I’d seen online.

const filePath = '';
const token = '';

const response = await fetch(filePath, {
    headers: { Authorization: `Bearer ${token}` }
});

if (!response.ok) {
    return;
}

const blob = await response.blob();

const url = URL.createObjectURL(blob);

const a = document.createElement('a');
a.href = url;
a.download = 'test.txt';

a.click();

URL.revokeObjectURL(url);

The problem is that this still has the second disadvantage of the existing implementation: the user doesn’t see the download progress until the file is fully loaded.

I’m also still unsure if this will support large files, as I’ve seen posts saying the browser will save the blob to disk if it’s large enough, and others saying it this approach isn’t optimal for files over ~2 GB.


Streaming

The next thing I tried is streaming the file and managing the progress all in code.

const handle = await window.showSaveFilePicker({ startIn: 'downloads', suggestedName: 'test.txt' });

const writable = await handle.createWritable();

const filePath = '';
const token = '';

const response = await fetch(filePath, {
    headers: { Authorization: `Bearer ${token}` }
});

if (!response.ok) {
    return;
}

const reader = response.body.getReader();

const readable = new ReadableStream({
    start: controller => {
        const read = async () => {
            const { done, value } = await reader.read();

            if (done) {
                controller.close();
                return;
            }

            controller.enqueue(value);
            read();
        };

        read();
    }
});

let bytesRead = 0;
const label = document.getElementById('fileStatusLabel');
label.textContent = 'Starting download...';
const finalSize = 104857699922; // For testing, would normally get this from API.

let lastTime = Date.now();
let dBytes = 0;
let byteRate = 0;

const track = new TransformStream({
    transform: (chunk, controller) => {
        controller.enqueue(chunk);

        bytesRead += chunk.byteLength;
        dBytes += chunk.byteLength;

        const progress = Math.ceil(bytesRead * 100 / finalSize);

        if (Date.now() - lastTime > 1000) {
            const dTime = Date.now() - lastTime;
            byteRate = dBytes * 0.001 / dTime;
            dBytes = 0;
            lastTime = Date.now();
        }

        label.textContent = `Download ${progress} complete (${byteRate.toFixed(2)} MB/s) - ${bytesRead}/${finalSize} bytes downloaded.`;
    }
});

readable.pipeThrough(track).pipeTo(writable);

Aside from the scuffed code for tracking download speed, this is pretty close to working how I want it… with a couple big downsides.

  1. It doesn’t work on Firefox because window.showSaveFilePicker isn’t defined. I looked it up and apparently Firefox doesn’t agree with the file save API so it isn’t implemented, and the post I saw recommended using the second approach with createObjectURL instead.
  2. It still can’t download really large files. In my testing with a ~100 GB file it got to between 5-20 GB (on screen) before I got an error in the console:

Uncaught (in promise) RangeError: Array buffer allocation failed

It shows this error as having come from the read() method inside ReadableStream.start, and the progress eventually stops. It generally stops at around 21% e.g. ~21 GB.

  1. The download happens completely inside the app, so the user doesn’t get the file in their downloads folder – they have to pick a location on disk to save the file to.

I imagine this approach would also need a lot more testing and error handling e.g. what if they pick a restricted directory? Can I create a link to open the folder containing the file once the download is complete? It’s my first time using this method so I have no idea what the possible pitfalls are, and generally it feels like there must be a simpler way to do this.


Don’t require an access token?

I’ve seen this done (and done it myself in the past) where the access token is passed as a query parameter instead of with a header, allowing you to just open a new tab containing the file path and the browser downloads the file as it should. I can’t make this change as I can’t risk exposing a user’s access token. Security is very important here, these are not public files.

Something I did consider was creating a cookie or something that will allow the browser to access the file. I’ve never done that before, though, and don’t really know how/if it will work or if it is a recommended/secure approach.


Any advice on this would be much appreciated as I’m otherwise at a bit of a loss.

LangGraph course_title and course_description mismatch

over the past few days I’ve been working with LangGraph, and I’m still new to it. While learning, I created a LangGraph agent that generates a course based on the user_thought and targeted_audience. Everything works fine until the evaluation step. However, whenever it reaches the save_course step, the title and description are different from what was used in the course_reviewer.

Could anyone help me understand the reason for this? Am I doing something wrong?

import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { Annotation, MemorySaver, StateGraph } from '@langchain/langgraph';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { Injectable } from '@nestjs/common';
import z from 'zod';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';

type courseData = {
user_thought: string;
targeted_audience: string;

enable_knowledge_store: boolean;
knowledge_store_id: string;

course_structure_id?: string;
};

@Injectable()
export class LangCourseService {
private readonly app;

constructor() {
  this.app = this.buildflow();
}

private buildflow() {
  const CourseMetadataAgentAnnotation = Annotation.Root({
    messages: Annotation<BaseMessage[]>,
    userInput: Annotation<string>,
    courseInput: Annotation<courseData>,
    courseScore: Annotation<number>,
    courseOutput: Annotation<{
      course_title: string;
      short_Desc: string;
      detailed_desc: string;
      tags: string[];
    }>,
  });

  const course_reviewer = new DynamicStructuredTool({
    name: 'course_reviewer',
    description: ``,
    schema: z.object({
      course_title: z.string().describe('Title of the course'),
      course_description: z.string().describe('Description of the course'),
    }),
    func: async ({ course_title, course_description }) => {

      console.log("Course Title", course_title)
      console.log("course description", course_description)

      const prompt = `Evaluate the following course based on clarity, relevance, and detail.
          Return a score between 1 and 10, and provide a short comment.

          Course Title: ${course_title}
          Course Description: ${course_description}

          Format:
          Score: <number>
          Comment: <text>
          `
       
          const agents = await model.invoke([new HumanMessage(prompt)]);
      
          const text = agents.content as string;
          const match = text.match(/Score:s*(d+)/i);
          const commentMatch = text.match(/Comment:s*(.*)/i);

          console.log("Score",match ? parseInt(match[1]) : 0);
          
          return {
              score: match ? parseInt(match[1]) : 0,
              comments: commentMatch ? commentMatch[1] : 'No comment',
          };
    },
  });

  const save_course = new DynamicStructuredTool({
    name: 'save_course',
    description: `Saves the course details to the database.
          * PURPOSE: Saves the course details to the database.
          * INPUT: A data object containing course details.
          * OUTPUT: An object with the course ID and a success message.
          * EXAMPLES:
          - "Save the course details to the database."
          - "Store the course information in the system."`,
    schema: z.object({
      course_title: z.string().describe('Title of the course'),
      course_description: z.string().describe('Description of the course'),
    }),
    func: async ({ course_title, course_description }) => {
      console.log('Saving course:', { course_title, course_description });

      return {
        course_id: 'course-12345',
        message: 'Course saved successfully',
      };
    },
  });

  const shouldContinue = ({
    messages,
  }: typeof CourseMetadataAgentAnnotation.State) => {
    const lastMessage = messages[messages.length - 1] as AIMessage;
    return lastMessage.tool_calls?.length ? 'tools' : '__end__';
  };

  const tools = [course_reviewer, save_course];

  const tool = new ToolNode(tools);

  const model = new ChatGoogleGenerativeAI({
    apiKey: 'AIzaSyBs1GoNE5PScHx8_U6ErN2Y2CdNVutsIjQ',
    model: 'models/gemini-2.5-flash',
  });

  const chatModel = async (
    state: typeof CourseMetadataAgentAnnotation.State,
  ) => {
    const messages: BaseMessage[] = [new AIMessage(`You are a helpful course generation assistant.

          Your job is to:
          1. Generate a course based on the user's course idea and targeted audience.
          2. Use the "course_reviewer" tool to evaluate the generated course.
          3. If the score is less than or equal to 5, regenerate a new course and re-evaluate.
          4. Repeat until the score is greater than 5.
          5. Once a good-quality course is achieved (score > 5), call the "save_course" tool with the course details.

          Always ensure:
          - The course is well-structured, relevant to the audience, and clearly described.
          - You follow the tool call format properly and wait for score feedback before proceeding.- Always use the same course content that was evaluated.`),
      ...state.messages];

    const response = await model.bindTools(tools).invoke(messages);
    

    return {
      ...state,
      messages: [...state.messages, response],
    };
  };

  const checkpointSaver = new MemorySaver();

  return new StateGraph(CourseMetadataAgentAnnotation)
    .addNode('chatModel', chatModel)
    .addNode('tools', tool)
    .addEdge('__start__', 'chatModel')
    .addEdge('tools', 'chatModel')
    .addConditionalEdges('chatModel', shouldContinue)
    .compile({
      checkpointer: checkpointSaver,
    });
}

async interact(thread_id: string,user_thought: string, targeted_audience: string) {
  const result = await this.app.invoke({
    messages: [
      new HumanMessage(
        `Create a course title and description based on the following user idea:
          ${user_thought}

          Ensure the course is suitable for the target audience:
          ${targeted_audience}`,
      ),
    ],
  },{
      configurable: {
          thread_id,
      }
  });

  return result.messages[result.messages.length - 1];
}
}

How to deal with the printer using Node.js

I have a lot of problems when I try to deal with the printer using Node.js.

I have tried a lot of libraries like :

  • pdf-to-printer – it is good, but when I use the fit option, it makes the printing unclear, and also puts a huge margin on the top of the paper.

  • node-thermal-printer & node-printer. I encountered some issues with these libraries and was unable to install them at all. I tried many solutions, but they could not solve the problems.

  • I also use Electron in my project, and I tried to use the print method of the window, but it has some issues with silent printing and page size.

I want a library that could give me the ability to send a PDF file to the printer and print it properly.

This is the code that I am using right now:

const fs = require('fs');
const { print } = require("pdf-to-printer");

async function generate_and_print_invoice(event, html, width_mm = 80) {
    const win = new BrowserWindow({ show: false, webPreferences: { offscreen: true } });
    try {
        await win.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`);

        const height_px = await win.webContents.executeJavaScript('document.body.scrollHeight');
        const pdf_buffer = await win.webContents.printToPDF({
            printBackground: true,
            pageSize: { width: Math.round(width_mm * 0.0393701), height: Math.round(height_px * 0.01045) },
            margins: { top: 0, right: 0, bottom: 0, left: 0 }
        });

        const file_path = path.join(process.cwd(), 'invoice.pdf');
        fs.writeFileSync(file_path, pdf_buffer);

        const response = await print(file_path);

        try {
            fs.unlinkSync(file_path);
        } catch (err) {
            console.log(err);
        }

        win.close();
        return { success: true };
    } catch (error) {
        win.close();
        return { success: false, error: error.message };
}

Bookmarklets and Javascript URI scheme on .onion sites in Chrome

I set up my regular Chrome to proxy .onion addresses through a Tor socks proxy. I don’t want or need to use the Tor browser, because I don’t care about anonymity, but there’re a couple of sites that are easier to access in their .onion version than the official URL for me. Some online libraries, etc.

On these websites, I use bookmarklets to sort / export data, and I noticed that the bookmarklets don’t work in .onion sites.

Out of curiosity, I put the line:

38.25.12.8 testing.onion testing.somesite

(I used the IP of a server I control.)

into my hosts file, and sure enough, bookmarklets and javascript:(function()); work on .somesite and don’t on the .onion, so clearly this is a hardcoded restriction. It’s worthwhile to mention this isn’t a CORS/CSP issue, since these sites have neither.

Intrigued, I went to look at the source code for Chromium, and there’s nothing–nada–bupkis–that would limit Javascript URI specifically on .onions in the source code that I could find.

My question is, which Chrome setting/flag controls this behavior and how can it be adjusted? What prevents Javascript from execution in Chrome on .onion sites?

Using properties in router-link to field

I have a list of items each with a name and numerical ID. Each one I want to render as a component with a link to a page for that item. This requires me to have a <router-link> tag. I cannot work out how to make the to property of the tag have a value basedon the properties of the component.

<script setup>
import { defineProps } from 'vue'

defineProps({
  id: {
    type: Number,
    required: true
  }
})
</script>

<template>
  <div class="item">
    <router-link to="/entry/{{id}}">
      <slot name="name"></slot>
    </router-link>
  </div>
</template>

I need the <router-link to="/entry/{{id}}"> to have the actual id property and cannot work out how

How can I implement a live search input with DataviewJS in Obsidian?

I’m trying to build an interactive search UI inside Obsidian using the Dataview plugin — specifically DataviewJS.

What I want to achieve is:

A simple input box where users can type in a query, and the DataviewJS result list updates immediately as they type (without reloading the note manually).

Here’s a simplified version of what I’m attempting:

const container = dv.el("div");
const input = container.createEl("input", { type: "text", placeholder: "Search..." });
const results = container.createEl("div");

function render(query) {
  results.innerHTML = "";
  const pages = dv.pages().where(p => p.title.includes(query));
  for (let p of pages) {
    results.createEl("div", { text: p.file.name });
  }
}

input.addEventListener("input", (e) => {
  render(e.target.value);
});

The input box renders correctly, but the dv.pages() call seems to be static — it does not dynamically re-evaluate the query on input changes.

What I’ve tried:

  • Using DataviewJS to render a dynamic list based on a keyword
  • Wrapping dv.pages() call in a function to trigger re-render
  • Manipulating DOM directly using createEl

My questions:

  • Is it possible to re-run a DataviewJS query when user input changes?
  • If not, is there a workaround — like forcing a re-evaluation or calling DataviewAPI manually?
  • Would building a custom plugin be the only solution if I want true “live search”?
  • Any pointers or examples would be greatly appreciated.

Thanks in advance!