Daz3D script need help (auto bone weight filling)

I have a hair figure (with bones), and the bone names are something like “J_Sec_Hair1_02”, “J_Sec_Hair2_02”, “J_Sec_Hair3_02”, and so on.

Currently, these bones don’t have weights, and I want to assign weights to all of them.

My process is as follows:

Select “J_Sec_Hair1_02” (bone).
Go to Geometry Selection > Select By > Face Groups > “J_Sec_Hair1_02” (Face Groups).
Then, go to Weight Editing > Fill Selected.

This process successfully assigns a weight to the “J_Sec_Hair1_02” bone.

The issue is there are many bones, and I want to repeat this process for each one until all bones have weights.

I need a script that automatically detects all bones under the currently selected figure and performs the above steps, assigning weights to each bone based on its corresponding face group.

This is chatgpt’s version log error and the script

2024-11-17 11:55:28.662 [WARNING] :: Script Error: Line 48

2024-11-17 11:55:28.662 [WARNING] :: TypeError: Result of expression ‘geometry.selectFaceGroup’ [undefined] is not a function.

// Function to assign weights to bones based on matching face groups
function assignWeightsToBones() {
    // Get the selected figure in the scene
    var selectedNode = Scene.getPrimarySelection();
    if (!selectedNode) {
        print("No figure selected. Please select a figure.");
        return;
    }

    // Check if the selected node has a skeleton
    var skeleton = selectedNode.getSkeleton();
    if (!skeleton) {
        print("Selected figure has no skeleton.");
        return;
    }

    // Get all bones in the skeleton
    var bones = skeleton.getAllBones();
    if (!bones || bones.length === 0) {
        print("No bones found in the selected figure.");
        return;
    }

    print("Found " + bones.length + " bones. Processing...");

    // Iterate through each bone
    for (var i = 0; i < bones.length; i++) {
        var bone = bones[i];
        var boneName = bone.getName();
        print("Processing Bone: " + boneName);

        // Select the bone
        bone.select(true);

        // Use Geometry Selection to select the corresponding face group
        var geometry = selectedNode.getObject().getCurrentShape().getGeometry();
        if (!geometry) {
            print("No geometry found for the selected figure.");
            return;
        }

        // Select faces by the face group matching the bone name
        var faceGroupIndex = geometry.findFaceGroup(boneName);
        if (faceGroupIndex === -1) {
            print("Face group not found for bone: " + boneName);
            continue;
        }
        geometry.selectFaceGroup(faceGroupIndex, true);

        // Fill the selected faces with the bone's weight
        geometry.fillWeights(bone, 1.0); // 1.0 represents full weight
        print("Assigned weight to bone: " + boneName);

        // Deselect the face group to prepare for the next bone
        geometry.clearSelection();
    }

    print("Weight assignment complete for all bones.");
}

// Run the function
assignWeightsToBones();

This is a script that could save my life. I have no understanding of Daz script (or JavaScript), and since ChatGPT couldn’t fulfill my request (it keeps making errors), I have no choice but to shamelessly come here to seek help.

  [1]: https://i.sstatic.net/82v6yMzT.jpg
  [2]: https://i.sstatic.net/eANGJPXv.jpg
  [3]: https://i.sstatic.net/BRIs3bzu.jpg
  [4]: https://i.sstatic.net/c8e9PagY.jpg

When saving to localstorage using JSON.stringify my value bececomes empty

I am developing an web app where the user clicks on a row in a table and it turns green then saves it into localstorage so that every time they access the web page it will save which row they have clicked on. I have this weird problem when I try to save my value to React localstorage the value empties. I have to use JSON.stringify to be able to save it to localstorage. Below is my code

var store = JSON.parse(localStorage.getItem("name"));

if (store === null) {
    store = new Set();
} else {
    store = new Set(store);
}

function saveStore() {
    localStorage.setItem("name", JSON.stringify([...store]));
}


const handleRowClicked = row => {
        const updatedData = data.map(item => {
            if (row.uid !== item.uid) {
                return item;
            }
            console.log(item.uid)
            if(store.has(item.uid)) {
                store.delete(item.uid);
            } else {
                store.add(item.uid);
            }

            saveStore();

            return {
                ...item,
                toggleSelected: !item.toggleSelected
            };
        });

        setData(updatedData);
  };

Ive tried printing the value of the stringify method and it always comes out exactly what I want it to but when I check the localstorage using the dev console it is always empty. I have also printed in the if cases so I know that code is executing the correct statement. It should add it to store then go the saveStore() method which will stringify the store set and save it into localstorage. Can someone help me?

Error populating dropdown: TypeError: Cannot read properties of undefined (reading ‘trim’)

I’m using Tom Select for one of my dropdowns in the form. The form has an option to dynamically add new rows to the form. The Tom Select works perfectly fine for the default row of the form, but it breaks when I add a new row by clicking on the button. additionally, it shows the error below.

Error populating dropdown: TypeError: Cannot read properties of
undefined (reading ‘trim’)

Here is my script. could you have any solution for this. I’m new to Javascript & Jquery.

$(document).ready(function () {
    // Initialize Tom Select for the default product dropdown
    initializeTomSelect('.product_id', true);

    // Add new row functionality
    $('#add_expense_row').click(function (e) {
        e.preventDefault();

        // Clone the row template
        const newRow = $('#aod_line').clone();
        newRow.removeAttr('id'); // Remove the ID to avoid duplication
        newRow.find('input[type="number"], input[type="text"]').val(''); // Clear input values
        newRow.find('.product_id').empty(); // Clear existing options
        newRow.find('.product_id').append('<option value="" disabled selected>Select a product</option>'); // Add placeholder
        newRow.find('#remove-line').prop('disabled', false); // Enable remove button

        // Append the new row
        $('#submit_div').before(newRow);

        // Initialize Tom Select for the newly added dropdown after populating options
        const productDropdown = newRow.find('.product_id');
        initializeTomSelect(productDropdown, false);
    });

    // Remove row functionality
    $(document).on('click', '#remove-line', function (e) {
        e.preventDefault();
        $(this).closest('.row').remove();
    });

    // Function to initialize Tom Select
    function initializeTomSelect(selector, isStatic = false) {
        $(selector).each(function () {
            const dropdown = $(this);

            // Populate options first before initializing Tom Select
            populateProductDropdown(dropdown).then(() => {
                console.log('Dropdown after population:', dropdown.html());
                
                if (!dropdown[0].tomselect) { // Check if Tom Select is already initialized
                    new TomSelect(dropdown[0], {
                        placeholder: 'Select a product',
                        allowEmptyOption: true, // Allow empty dropdowns to prevent errors
                    });
                }
            }).catch((error) => {
                console.error('Error populating dropdown:', error);
            });
        });
    }

    // Function to populate product dropdown with products
    function populateProductDropdown(dropdown) {
        return new Promise((resolve, reject) => {
            $.ajax({
                url: '../controller/operations_controller.php?status=get-products',
                method: 'GET',
                dataType: 'json',
                success: function (data) {
                    const select = dropdown[0].tomselect;

                    if (select) {
                        // If Tom Select is already initialized
                        select.clearOptions(); // Clear existing options
                        select.addOption({ value: '', text: 'Select a product', disabled: true }); // Add placeholder

                        data.forEach((product) => {
                            select.addOption({ value: product.product_id, text: product.product_name });
                        });
                        select.refreshOptions(false); // Refresh without opening
                    } else {
                        // If Tom Select is not initialized yet
                        dropdown.empty();
                        dropdown.append('<option value="" disabled selected>Select a product</option>'); // Add placeholder

                        data.forEach((product) => {
                            dropdown.append(`<option value="${product.product_id}">${product.product_name}</option>`);
                        });
                    }

                    resolve(); // Resolve the promise after population
                },
                error: function (xhr, status, error) {
                    console.error('AJAX error:', xhr.responseText || error);
                    alert('Failed to load products. Please try again later.');
                    reject(error); // Reject the promise on error
                }
            });
        });
    }
});

Here are two screenshots of the first row and the second row which was added dynamically.
https://imgur.com/a/EbTM3Wa

puppeteer detach CDP sessions safely for page.close()

I have this code snipper I use with puppeteer to create CDP session.

let browser = await puppeteer.launch(options);
const page = await browser.newPage();
const session = await page.target().createCDPSession();

await session.send('Input.synthesizeScrollGesture', { 
                            x: 100,
                            y: 200,
                            yDistance: -150
                        });

when I use page.close() as the scroll is happening, the whole program crashes with

node_modulespuppeteer-corelibcjspuppeteercommonCallbackRegistry.js:73
            this._reject(callback, new Errors_js_1.TargetCloseError('Target closed'));
                                   ^

TargetCloseError: Protocol error (Input.synthesizeScrollGesture): Target closed
    at CallbackRegistry.clear 

or this error

cdpCDPSession.js:64
            return Promise.reject(new Errors_js_1.TargetCloseError(`Protocol error (${method}): Session closed. Most likely the ${this.#targetType} has been closed.`));
                                  ^

TargetCloseError: Protocol error (Network.getCookies): Session closed. Most likely the page has been closed.
    at CdpCDPSession.send (

I have no way to prevent this, please help. I want to detach safely and close the page safely.

Indeed the real question is how to page.close() safely without crash ?

Can’t run a simple example with await

I tried to run the following code from the command prompt:

const read = require('read').read;

const password = await read({
  prompt: "Password: ",
  silent: true,
  replace: "*" //optional, will print out an asterisk for every typed character 
});
console.log("Your password: " + password);

with the command node read.js and got:

SyntaxError: await is only valid in async functions and the top level bodies of modules

What should I do to make this work?

Should I add exports or something like this?

I simply want it to read the password from the command line and print it.

Image from a toBlob interpolates edges

I am trying to scale a 1x image up to an arbitrary size using a canvas. However, the image always has some interpolation at the edges of each pixel. I expect the image provided to match how the look canvas exactly. No combination of different scales etc. has worked for me. Even right click -> save image on the canvas produces the same image.

I have also tried using toDataURL on the canvas but I get the same result.

// This function is unimportant; it is used as an example for this question.
function generateImage(width, height, digitOffset) {
  const pixelsCount = width * height
  const imageData = new ImageData(width, height)
  const digits = "0123456789"
    .repeat(30)
    .substring(digitOffset, digitOffset + pixelsCount)

  for (const [index, digit] of [...digits].entries()) {
    const colours = {
      0: [0x00, 0x12, 0x19],
      1: [0x00, 0x5f, 0x73],
      2: [0x0a, 0x93, 0x96],
      3: [0x94, 0xd2, 0xbd],
      4: [0xe9, 0xd8, 0xa6],
      5: [0xee, 0x9b, 0x00],
      6: [0xca, 0x67, 0x02],
      7: [0xbb, 0x3e, 0x03],
      8: [0xae, 0x20, 0x12],
      9: [0x9b, 0x22, 0x26],
    }
    const dataOffset = index * 4

    const color = colours[digit]
    imageData.data[dataOffset] = color[0]
    imageData.data[dataOffset + 1] = color[1]
    imageData.data[dataOffset + 2] = color[2]
    imageData.data[dataOffset + 3] = 0xff
  }

  return imageData
}

/** The width/height of the 1x image. */
const size = 16
const imageData = generateImage(size, size, 0)
const scale = 10
/** The canvas containing the 1x image. */
const unscaledCanvas = document.getElementById("unscaledCanvas")
unscaledCanvas.width = size
unscaledCanvas.height = size
unscaledCanvas.style.width = `${size}px`
unscaledCanvas.style.height = `${size}px`
const unscaledContext = unscaledCanvas.getContext("2d")
unscaledContext.putImageData(imageData, 0, 0)

const scaledCanvas = document.getElementById("increasedScale")
// Allow a pixel multiplier > 1 to not be blurry.
scaledCanvas.imageSmoothingEnabled = false
scaledCanvas.mozImageSmoothingEnabled = false
scaledCanvas.webkitImageSmoothingEnabled = false
scaledCanvas.style.imageRendering = "pixelated"
scaledCanvas.width = size * scale
scaledCanvas.height = size * scale
scaledCanvas.style.width = `${size * scale}px`
scaledCanvas.style.height = `${size * scale}px`
const scaledContext = scaledCanvas.getContext("2d")
scaledContext.imageSmoothingEnabled = false
scaledContext.drawImage(unscaledCanvas, 0, 0, size * scale, size * scale)

const image = document.getElementById("output")
image.width = size * scale
image.height = size * scale
scaledCanvas.toBlob((blob) => {
  const dataURL = URL.createObjectURL(blob)
  image.onload = () => {
    URL.revokeObjectURL(dataURL)
  }
  image.src = dataURL
})
<canvas id="unscaledCanvas"></canvas>
<canvas id="increasedScale"></canvas>
<img id="output" />

Sorting a Array of Object based on the value of another array

I have a basic array of objects that I want to reorder based on the value of another array.

This is something similar to what I want to do but is based purely on an array

items = [
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

/* This works fine */
sorting = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
result = [];

sorting.forEach(function(key) {
    var found = false;
    items = items.filter(function(item) {
        if(!found && item[1] == key) {
            result.push(item);
            found = true;
            return false;
        } else
            return true;
    })
})

result.forEach(function(item) {
    console.log(item[0]) /// Bob Jason Henry Thomas Andrew
})

The code I have is:

driverArray = [
   {label: "Driver 1",  pos: 1},
   {label: "Driver 2",  pos: 2},
   {label: "Driver 3",  pos: 3},
   {label: "Driver 4",  pos: 4},
   {label: "Driver 5",  pos: 5},
   {label: "Driver 6",  pos: 6},
   {label: "Driver 7",  pos: 7},
   {label: "Driver 8",  pos: 8},
   {label: "Driver 9",  pos: 9},
   {label: "Driver 10", pos:10}
];

newIndexes = [1,2,3,7,4,8,5,9,6,10); // These match the pos key;

I then want to sort the driverArray in the order of the newIndexes array. The newIndexes array match the Object Key ‘pos’;

Calendly Api integration to get events data

I need to get booking data from a client in Calendly. Here is the API documentation But I got a 401 Errors on my code. I can’t understand what exactly I make wrong.

My code is look like

window.addEventListener("message", function (event) {

    if (event.origin.includes("calendly.com")) {
        const data = event.data;
        if (data.event && data.event === "calendly.event_scheduled") {
            console.log("Event scheduled:", data.payload);


            const eventUri = data.payload.event.uri;
            const inviteeUri = data.payload.invitee.uri;

            // Getting event details via API
            fetchCalendlyEventDetails(eventUri, inviteeUri);
        }
    }
});

async function fetchCalendlyEventDetails(eventUri, inviteeUri) {
    try {
        const eventId = eventUri.split("/").pop();
        const inviteeId = inviteeUri.split("/").pop();

        // USER KEY
        const token = 'IzLmPwQHQDKPyoBrnT3Fxy6DFbv3wWwLcqskWrgLcsc';

        // const options = {
        //     method: 'GET',
        //     headers: {'Content-Type': 'application/json', Authorization: token}
        // };
        //
        // fetch('https://api.calendly.com/scheduled_events/event_uuid/invitees/invitee_uuid', options)
        //     .then(response => response.json())
        //     .then(response => console.log(response))
        //     .catch(err => console.error(err));
        //

        const headers = {
            'Content-Type': 'application/json',
            'Authorization': token,

        };

        const eventResponse = await fetch(`https://api.calendly.com/scheduled_events/${eventId}`, headers );
        const inviteeResponse = await fetch(`https://api.calendly.com/scheduled_events/${eventId}/invitees/${inviteeId}`, headers );

        const eventData = await eventResponse.json();
        const inviteeData = await inviteeResponse.json();

        // Checking data
        if (!inviteeData || !inviteeData.resource || !inviteeData.resource.scheduled_at) {
            console.error("Incorrect meeting details:", inviteeData);
            return;
        }

        const scheduledAt = inviteeData.resource.scheduled_at;
        const startTime = new Date(scheduledAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
        const startDate = new Date(scheduledAt).toLocaleDateString([], { day: '2-digit', month: 'short', year: 'numeric' });

        console.log(`Date event: ${startDate}`);
        console.log(`Time event: ${startTime}`);

        document.querySelector('.date-box_item').textContent = `${startDate}`;
        document.querySelector('.date-box_item + .date-box_item').textContent = `${startTime}`;

    } catch (error) {
        console.error("Error when retrieving appointment data:", error);
    }
}

I don’t understand what the problem. I used local tested site….

Api for creating some credentials for Calendly

I also tried create some token here but it don’t help me too. In any case I got a 401 error. I tried also with prefix

const token = 'Bearer eyJraWQiOiIxY2UxZTEzNjE3ZGNmNzY2YjNjZWJjY2Y4ZGM1YmFmYThhNjVlNjg0MDIzZjdjMzJiZTgzNDliMjM4MDEzNWI0IiwidHlwIjoiUEFUIiwiYWxnIjoiRVMyNTYifQ.eyJpc3MiOiJodHRwczovL2F1dGguY2FsZW5kbHkuY29tIiwiaWF0IjoxNzMxNzkxNTkwLCJqdGkiOiJkZWYyMDUxYi1jNzk0LTRjOWQtYWY0OC1kNjI2OGFmYWMzMGQiLCJ1c2VyX3V1aWQiOiIxMTg0MGNlZC0xZTEyLTQ1Mjct';

Calculating percent coverage of 1000m raster by 30m raster in google earth engine

I have two rasters, one of restoration opportunity at 1000m and one of deforestation from 2019-2023 at 30m (Global Forest Watch data). At the end of this, I want to determine how many pixels of restoration opportunity have over 50% coverage by deforestation in this time period. Right now, I am tackling “how do I calculate percent coverage of a coarser resolution raster by a finer resolution raster?”. See below for what I have tried which has resulted in two issues/questions.

  1. The reported coverage percents per polygon are plausible but strike me as a little small. How can I go about verifying these numbers are correct? Does this method seem sound?
  2. A more overarching issue is that adjacent pixels are aggregated together at the vectorizing stage. Ideally, I would calculate percent coverage per 1km pixel. Advice on how to achieve that in GEE?

General overview: I converted the 1km raster to polygons, got a pixel area raster of deforestation, created a function that sums up pixel area raster per polygon then divides that by polygon area, and map that over all the polygons. Function shown here, full code is available here

## Function to calculate fractional coverage for each polygon
var calculateCoverage = function(feature) {
  // Clip raster to current polygon
  var clippedImage = lossYear_2019_2023_area.clip(feature);
  
  // Mask raster to include only the pixels inside the polygon
  var imageMasked = clippedImage.updateMask(clippedImage.mask());

  // Calculate the area of the polygon (in square meters)
  var polygonArea = feature.area({maxError: 1e6}); // in square meters
  
  // Sum up the pixels (area of deforestation) for the polygon
  var coverageArea = imageMasked.reduceRegion({
    reducer: ee.Reducer.sum(), // Sum of 30m area pixels that are in polygon
    geometry: feature.geometry(),
    scale: scale, // The resolution of the image (~30m per pixel)
    maxPixels: 1e13
  });
  
  // Calculate the fractional coverage as a percentage
  var fractionalCoverage = ee.Number(coverageArea.get('lossyear')).divide(polygonArea).multiply(100);

  // Add the fractional coverage as a new property to the feature
  return feature.set('fractionalCoverage', fractionalCoverage);
};

// Apply the function to each polygon in the FeatureCollection
var polygonsWithCoverage = opportunity_vectors.map(calculateCoverage);

// Print the result to check
print('Polygons with fractional coverage:', polygonsWithCoverage.first());

How to change page title even if user is in another website [duplicate]

I’m trying to make a stopwatch, and I need it to appear as the page title, it works good if I’m in that specific tab but the second I change to another tab/website it stays paused.
Since it’s a stopwatch I want it to always be updating so that I can just look at that tab to know how long I’ve been working on (it’s just a personal project that I don’t plan on sharing so I don’t care too much about performance).

This is the code I’m using to change the title:

  useEffect(() => {
    document.title = `${hours}h ${minutes
      .toString()
      .padStart(2, "0")}m ${seconds.toString().padStart(2, "0")}s`;
  }, [seconds]);

Here is the website in case you want to look at it: https://stopwatch-and-earnings.netlify.app/

Why dosnt my n-segment pendulum conserve energy

In response to @maciejmatyka on YouTube, I was attempting to create an n-segment pendulum that can conserve energy.
Obviously, numerical issues would cause discrepancies but I assumed those would be small and predictable, so my goal was to make a good code to single that out, then fix that accordingly.
Here is my pendulum class in JS:

class Pendulum {
    constructor({
        n = 20,
        g = -10,
        dt = 0.002,
        thetas = Array(n).fill(0.5 * Math.PI),
        thetaDots = Array(n).fill(0)
    } = {}) {
        this.n = n;                 // Number of pendulums (constant)
        this.g = g;                 // Gravitational acceleration (constant)
        this.dt = dt;               // Time step (short for delta-time)
        this.thetas = thetas;       // Angles of the pendulums (polar)
        this.thetaDots = thetaDots; // Angular velocities (polar)
    }
    
    matrixA() {
        const { n, thetas } = this;
        return Array.from({ length: n }, (_, i) =>
            Array.from({ length: n }, (_, j) =>
                (n - Math.max(i, j)) * Math.cos(thetas[i] - thetas[j])
            )
        );
    }
    
    vectorB() {
        const { n, thetas, thetaDots, g } = this;
        return thetas.map((theta, i) => {
            let b_i = 0;
            for (let j = 0; j < n; j++) {
                const weight = n - Math.max(i, j);
                const delta = thetas[i] - thetas[j];
                b_i -= weight * Math.sin(delta) * thetaDots[j] ** 2;
            }
            b_i -= g * (n - i) * Math.sin(theta);
            return b_i;
        });
    }
    
    accelerations() {
        const A = this.matrixA();
        const b = this.vectorB();
        return math.lusolve(A, b).flat();
    }
    
    leapfrogStep() {
        const { thetas, thetaDots, dt } = this;
        const acc = this.accelerations();

        // Half-step for velocities
        const halfThetaDots = thetaDots.map((dot, i) => dot + acc[i] * dt / 2);

        // Full step for positions
        this.thetas = thetas.map((theta, i) =>
            ((theta + halfThetaDots[i] * dt) + Math.PI) % (2 * Math.PI) - Math.PI
        );

        // Full step for velocities
        const newAcc = this.accelerations();
        this.thetaDots = halfThetaDots.map((dot, i) => dot + newAcc[i] * dt / 2);
    }
    
    tick() {
        // This is where I would correct velocities to match the energy
        this.leapfrogStep();
        // Check energy
        // Apply difference
    }
    
    kineticEnergy() {
        const { thetas, thetaDots } = this;
        
        // Sum of 1/2 * ((xDot_j)^2 + (yDot_j)^2) for j in n
        return thetas.reduce((T, _, i) => {
            let xDot = 0, yDot = 0;
            for (let j = 0; j <= i; j++) {
                xDot += thetaDots[j] * Math.cos(thetas[j]);
                yDot += thetaDots[j] * Math.sin(thetas[j]);
            }
            return T + 0.5 * (xDot*xDot + yDot*yDot);
        }, 0);
    }
    
    potentialEnergy() {
        const { thetas, n, g } = this;
        
        // Sum of cos(theta)+1's up to i for i in n
        return thetas.reduce(
            (V, theta, i) => V - g * (n - i) * (Math.cos(theta)+1),
            0
        );
    }
    
    totalEnergy() {
        return this.kineticEnergy() + this.potentialEnergy();
    }

    get coordinates() {
        let x = 0, y = 0;
        return this.thetas.map(theta => {
            x += Math.sin(theta);
            y += Math.cos(theta);
            return { x, y };
        });
    }
}

I have gone through a couple of iterations of formulas. For some reason, potential energy looks too high, and kinetic energy gets even higher at the lowest part of its swing. I’m not the most veteran coder, and I’m not even sure if this is the right site, but I’m getting really cross-eyed by this, and I don’t know what’s causing it to fail so dramatically.

I was expecting a pendulum to stay stable for at least a few hundred iterations, but it seems to be constantly losing energy, which I can’t explain unless something is fundamentally wrong with my code.

I thought to try adjusting the velocity dependent on the difference in energy from an initial constant, but that created some weird effects. Speeding up the pendulum far more than is realistic led me to assume my energy calculations were written wrong.

How to make element fixed number of characters using monospace font?

I’m trying to add --cols CSS variable to jQuery Terminal, the problem I had before was not able to get the size of the scrollbar but got it in JavaScript.

The problem I have now is that when I set --cols: 80 the size is 79 characters. The question is: why this is happening?

const term = $('#term').terminal();

const wrapper = term.find('.terminal-wrapper');
const padding = term[0].style.getPropertyValue('--padding') || 10;
const wrapper_rect = wrapper[0].getBoundingClientRect();
const container_rect = term[0].getBoundingClientRect();
const content_width = wrapper_rect.width + (padding * 2);
const scrollbar = container_rect.width - content_width;
term[0].style.setProperty('--terminal-scrollbar', scrollbar);

term.echo(() => {
    const cols = term.cols();
    const rows = term.rows();
    return `${cols}x${rows}`;
})
#term {
    --cols: 80;
    --rows: 10;
    width: calc((var(--cols) * 1ch) + (var(--padding, 10) * 2px) + (var(--terminal-scrollbar, 10) * 1px));
}
<script src="https://cdn.jsdelivr.net/npm/jquery"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery.terminal/js/jquery.terminal.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/jquery.terminal/css/jquery.terminal.min.css" rel="stylesheet"/>
<div id="term"></div>

.terminal-wrapper is an element that is inside scrollbar, so it’s just the content that contain text. I tested the cols() and it returns correct value.

I can fix my issue by adding:

((var(--cols) + 1) * 1ch)

But I want to know why this off by one error is happening. I’m trying to understand and not add a fix blindly.

I’ve tried to set:

.terminal-wrapper {
    width: 80ch;
}

Just to see if I will get 80 characters, but I still get 79 characters.

The way I calculate the number of characters is using this function:

    function get_char_size(term) {
        var result;
        if (terminal_ready(term)) {
            var $prompt = term.find('.cmd-prompt').clone().css({
                visiblity: 'hidden',
                position: 'absolute'
            });
            $prompt.appendTo(term.find('.cmd'))
                .html('&nbsp;')
                .wrap('<div class="cmd-wrapper"/>');
            result = {
                width: $prompt.width(),
                height: $prompt.height()
            };
            $prompt.parent().remove();
        } else {
            var temp = $('<div class="terminal terminal-temp"><div class="terminal-' +
                         'wrapper"><div class="terminal-output"><div><div class="te' +
                         'rminal-line" style="float: left"><span>&nbsp;</span></div' +
                         '></div></div><div class="terminal-pixel"></div></div>')
                .appendTo('body');
            temp.addClass(term.attr('class')).attr('id', term.attr('id'));
            if (term) {
                var style = term.attr('style');
                if (style) {
                    style = style.split(/s*;s*/).filter(function(s) {
                        return !s.match(/displays*:s*none/i);
                    }).join(';');
                    temp.attr('style', style);
                }
            }
            var node = temp.find('.terminal-line');
            result = {
                width: node.width(),
                height: node.height()
            };
            temp.remove();
        }
        return result;
    }
    function get_num_chars(terminal, char_size) {
        var width = terminal.find('.terminal-fill').width();
        var result = Math.floor(width / char_size.width);
        // random number to not get NaN in node.js but big enough to
        // not wrap exception
        return result || 1000;
    }

It was proven to work correctly.

I thought that it may be a browser bug related to scrollbar-gutter, but I was not able to reproduce the issue, here is a code that do work: https://codepen.io/jcubic/pen/ExqJwMN. I think that I did exactly the same.

I was checking if the font-size is correctly set on the root element and it have the same font and size as content of the terminal.

Why is my website adding a span with id PING_IFRAME_FORM_DETECTION to the bottom of the page?

I have a React/Node website created using Vite. On pages that have a form, when any of the inputs are put into focus for the first time, the following element is added to the bottom of the page. I can’t find this id anywhere in any dependency, in my project, and can’t find any information about it when searching the internet. I’m completely lost here.

<span id="PING_IFRAME_FORM_DETECTION" style="display: none;"></span>