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.