Poderia tentar interpretar esse script e gerar um outro que quando estiver com um texto selecionado cria um slider control para controlar o número de caracteres máximos por linha

{
"captioneerExpLib": (function () {
/**
* This library may only be used with the JavaScript expression engine.
* Set the engine under File > Project Settings > Expressions > Expression Engine.
*
* CHANGE LOG
*
* 20240225
* Added + organized functions
*
* 20240215
* Initial version
*/


    /**
     * TEXT LAYER specific functions
     */
    TEXT_numLines = function (targetLayer = thisLayer, sampleTime = 0) {
        const txt = targetLayer.text.sourceText.valueAtTime(sampleTime);
        return Math.max(txt.split(/[^\r\n\3]*/gm).length - 1, 0);
    }

    TEXT_numWords = function (targetLayer = thisLayer, sampleTime = 0, locale) {
        const txt = targetLayer.text.sourceText.valueAtTime(sampleTime);

        if(typeof locale !== 'undefined') {

            const en = new Intl.Segmenter(locale, {granularity: "word"});
            const numWords = [...en.segment(txt)].filter((segment) => segment.isWordLike).length;
            return Math.max(0, numWords)

        } else {

            const whiteSpace = txt.split(/\s/gm).length;
            return Math.max(0, whiteSpace);
            
        }
    }

    TEXT_addLineBreaks = function (string, maxCharacters) {
        const ch = Math.max(Math.floor(maxCharacters), 1);
        const splitRegex = new RegExp((?![^\\n]{1,${ch}}$)([^\\n]{1,${ch}})\\s, 'g');

        return string.replace("\r", "\n").replace(splitRegex, '$1\n');
    }

    TEXT_cloneStyle = function (targetLayer = thisLayer) {
        const txt = targetLayer.text.sourceText;
        return txt.style
    }

    TEXT_cloneTextStyle = function (targetLayer = thisLayer) {
        const txt = targetLayer.text.sourceText;
        return txt.style.setText(txt)
    }

    TEXT_centerAP = function (targetLayer = thisLayer, sampleTime = 0, extraLineSpacing = 0) {
        const txt = targetLayer.text.sourceText;
        const numLines = TEXT_numLines(targetLayer, sampleTime);
        const { left, width } = targetLayer.sourceRectAtTime(sampleTime);
        const { fontSize, leading, autoLeading } = txt.style;
        const xHeight = fontSize * 0.50;

        const getLeading = (!autoLeading) ? leading : fontSize * 1.2;
        const lastBaseline = (getLeading + extraLineSpacing) * (numLines - 1);

        return [left + (width * 0.50), (-xHeight + lastBaseline) * 0.50]
    }

    TEXT_baselines = function (targetLayer = thisLayer, numLines = 1, extraLineSpacing = 0) {
        const txt = targetLayer.text.sourceText;
        const { fontSize, leading, autoLeading } = txt.style;
        const getLeading = (!autoLeading) ? leading : fontSize * 1.2;

        const baseline = (getLeading + extraLineSpacing) * (numLines - 1);

        return [0, baseline]
    }

    TEXT_lastBaseline = function (targetLayer = thisLayer, sampleTime = 0, extraLineSpacing = 0) {
        const txt = targetLayer.text.sourceText;
        const { fontSize, leading, autoLeading } = txt.style;
        const getLeading = (!autoLeading) ? leading : fontSize * 1.2;
        const lastBaseline = (getLeading + extraLineSpacing) * (TEXT_numLines(targetLayer, sampleTime) - 1);

        return lastBaseline
    }

    TEXT_pins_AnchorXY = function (targetLayer = thisLayer, guideLayer = thisLayer, sampleTime = 0, pinX = 50, pinY = 50, extraLineSpacing = 0) {

        let textOrigin
        if (thisLayer.hasParent) {
            textOrigin = thisLayer.parent.fromWorld(targetLayer.toWorld([0, 0, 0], sampleTime));
        } else {
            textOrigin = targetLayer.toWorld([0,0,0], sampleTime);
        };

        const left = targetLayer.sourceRectAtTime(sampleTime).left;
        
        const botCenter = TEXT_lastBaseline(targetLayer, sampleTime, extraLineSpacing);

        const textTop = guideLayer.sourceRectAtTime(sampleTime).top;

        const x = thisLayer.linear(pinX, 0, 100, left, -left);
        const y = thisLayer.linear(pinY, 0, 100, textTop, botCenter);

        return thisLayer.add(textOrigin, [x, y])
    }

    /**
     * General LAYER functions
     */

    centerX = function (targetLayer = thisLayer, sampleTime = 0) {

        const {left, width} = targetLayer.sourceRectAtTime(sampleTime);
        const x = left + (width * 0.50);

        return [x, 0]
    }

    centerY = function (targetLayer = thisLayer, sampleTime = 0) {

        const {top, height} = targetLayer.sourceRectAtTime(sampleTime);
        const y = top + (height * 0.50);

        return [0, y]
    }

    centerAP = function (targetLayer = thisLayer, sampleTime = 0) {
        const { top, left, width, height } = targetLayer.sourceRectAtTime(sampleTime);

        return [left + (width * 0.50), top + (height * 0.50)]
    }
    
    descenderlessBox = function (targetLayer = thisLayer, guideLayer = thisLayer, sampleTime = 0, extraLineSpacing = 0) {

        const width = targetLayer.sourceRectAtTime(sampleTime).width;

        const textTop = guideLayer.sourceRectAtTime(sampleTime).top;
        const lastBaseline = TEXT_lastBaseline(targetLayer, sampleTime, extraLineSpacing);

        const x = width;
        const y = (-textTop) + lastBaseline;

        return [x,y]
    }


    pins_AnchorXY = function (targetLayer = thisLayer,  sampleTime = 0, pinX = 50, pinY = 50) {

        const origin = targetLayer.toComp([0,0], sampleTime);
        const {left, top, width, height} = targetLayer.sourceRectAtTime(sampleTime);

        const x = thisLayer.linear(pinX, 0, 100, left, left + width);
        const y = thisLayer.linear(pinY, 0, 100, top, top + height);

        return thisLayer.add(origin, [x, y])
    }


})()
}