SkillAgentSearch skills...

Tw.gl.repl

A multi-line texteditor in the Max Jitter OpenGL window for interaction with your patch in a Livecoding-like style.

Install / Use

/learn @twhiston/Tw.gl.repl
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

GLRepl

Test

About

GLRepl is a Max Repl (Read/Execute/Print/Loop) environment based on the excellent th.gl.texteditor. It consists of two objects [tw.gl.repl] and [tw.gl.repl.dynamic-size-helper].

At it's core this is the same idea as th.gl.texteditor but the way in which functions can be attached to keys now significantly extends what it is possible to do. There is a fundamental philosophical difference between the idea of having a text buffer repl which performs actions on run/execute and a program where additionally every key press triggers a specific function. It means there are subtle differences between this and th.gl.texteditor which are important to be aware of. For example when you load a text file into th.gl.texteditor it just fills the buffer, in tw.gl.repl it replays the keystrokes back through the input processing. This means that any function which is attached to the individual keypress will be executed again. In it's usual configuration this means that the text is added to the text buffer, but it does not necessarily hold that this is true in every possible configuration. It would be possible to attach functions to keypresses which maintain state for other parts of an application, or which trigger messages to be output immediately etc. This means you should think about where you put your functionality, does it need to be in the repl itself, ie should it be triggered every time the keypresses are played back? or does it need to be some routing and handling in max? Further to this the repl introduces the concept of output formatters, these can be attached to the repl and then used in the configuration file to alter the output in some way. This allows you to format text easily for whatever you are hooking the repl up for, for example concatenating the output into a single line, or checking that it has balanced brances, or ensuring whitespace is in a regular format. However it also means that it's possible to, for example, have a short dsl for the repl, which is expanded to a full DSL of the thing you wish to interface with. This is useful if you need to interact with a verbose javascript but don't want to do a lot of typing.

TLDR not only is it possible to output the contents of the repl buffer for processing in max, but it's possible to attach any function to a keypress in the repl, which can in turn do things including generate messages for output. The text you input can also be mutated on run/execute so that something different is output from the repl.

Simple use cases for the repl can be handled entirely in configuration, and more complex use cases can be easily managed by including a user-repl.js file inside your project in which you can further customize behaviour by attaching your own custom functions to keypresses or your own custom formatters for output message handling. Read on for more about this.

See the patch in the extras menu for a few examples of how you might use the repl.

Install

You should install this inside your Max packages directory, in a folder called GLRepl, it should then be available in max after a restart. See help files for some ideas on what you might do with it!

Download zip

1. download a release from the github release page for this project
2. unzip and place in Max Searchpath (eg. MacOS ~/Documents/Max 8/Packages)
3. restart Max8

Git clone

If you want to git clone the repo you will need to have npm and tsc installed as the compiled sources are not included in the repo.

cd ~/Documents/Max\ 8/Packages
git clone https://github.com/twhiston/tw.gl.repl.git
cd GLRepl/javascript
npm install && npm compile
//start Max8
4. Go to the extras menu and open the "GLRepl Overview" patch

All source files loaded by max are in the dist folder and the typescript which it is compiled from is found in src. Unless you have a more complex project in mind you probably don't need to care about this and can use the config file and user-repl.js to extend the functionality of the repl.

Execute/Run functionality

By default executing the code in the repl will run a series of formatters and output the resulting text from outlet 0. This allows you to write livecoding style commands in the repl, ensure they are formatted as needed, and then output them for further routing and processing in max.

Scaling

It's undeniably the most useful to have a repl that you can dynamically resize and to this end a helper object is included. See the help file for information on how to connect this, or hover the inlets and outlets in max. The scaling fits some window sizes better than others, and sometimes it might unavoidably break a boundary, you should just resize the window in a way that sorts this out. You can also send a scale 1. value to the object, which the current scaling will be multiplied by. No guarantee that the current scaling works super well with every font either!

Config

Basic configuration of your repl can be achieved by loading a replkeys.json file to reconfigure it. This file is an object

The config is in the following form:

{
    "settings":{
        "keypressProcessor": {
            "overrideAlphaNum": true
        }
    }
    "bindings": [
        {
            "id": "execute",
            "asciiCode": 2044,
            "functions": [
                "return 'run'"
            ]
        },
        {
            "id": "backspace",
            "asciiCode": -7,
            "functions": [
                "ctx.backSpace()"
            ]
        },
        {
            "id": "customSpace",
            "asciiCode": -2,
            "functions": [
                "myCustomFunction"
            ]
        }
    ]
}

Settings

Settings allow you to set the value of some repl settings instead of settings them through messages or in code. All currently available settings are as follows:

"settings": {
        "repl": {
            "INDENTATION": 4,
            "CMNT": "//"
        },
        "keypressProcessor": {
            "overrideAlphaNum": true
        },
        "textbuffer": {
            "formatters": [
                "whitespace",
                "bracebalanced",
                "singleline",
                "commentremover"
                //can also include your own custom formatters here
            ]
        }
}

Bindings

Bindings are an array of of objects which bind a key number to a function. In contrast to th.gl.editor there are no internal functions, so everything is defined in this file and the user can override anything. As you can see there are a number of ways to define the functions that are called, and it is possible to call multiple functions with a single key. Functions can be defined as a function body in text (which will be wrapped new Function('k', 'ctx', funcString)), it can be a function from whatever context is passed in (in the case of this application it is an instance of REPLManager), or it can be a reference to a custom function.

There is one "special" keycode which is not defined in config, this is the binding for 'ignore_keys'. This is hardcoded to option+d which is keycode 8706. This needs to be handled outside of the javascript because you want to be able to re-enable the keys. You can change this binding by sending the message ignore_keys_id and the keycode id that you want.

Binding a simple function

You can create a simple key binding to output a message when a key is pressed with the following configuration

{
    "id": "execute",
    "asciiCode": 2044,
    "functions": [
        "return 'run'"
    ]
}

Each of the entries in functions will be wrapped in a new Function('k', 'ctx', funcString) and will be executed on keypress. This allows us to perform simple actions such as returning custom messages which we can process further in max easily.

Context based functions

Because the functions called have the signature ('k', 'ctx') functions we create in config will always contain the value of the key that was pressed in k. The ctx parameter however will contain an instance of REPLManager, which means that its functions and all the functions of the subclasses are available here. This allows you to create very complex functionality in just the config file. The shortkey to replace a line of text in the buffer with the pastebin is an exaxple of this

{
    "id": "replaceLine-alt-p",
    "asciiCode": 960,
    "functions": [
        "var pb = ctx.tb.pasteBinGet(); var startLine = ctx.c.line(); ctx.deleteLine(); if(ctx.c.line() < ctx.tb.length()-1){ctx.jumpLine(-1);ctx.jumpTo(1);} if(startLine === 0){ctx.jumpTo(0); ctx.newLine(); ctx.jumpTo(2); }else { ctx.newLine(); } for(var i = 0; i < pb.length; i++){for (var a = 0; a < pb[i].length; a++) {var char = pb[i].charCodeAt(a); ctx.keyPress(char)}}"
            ]
        }

Including custom functions

One of the ways to extend the repl further is to attach or preload your own functions so you can tie them to a key in the config. To make this easier the package tries to load a file called user-repl.js, max should load this fine if it's in your patch folder. Inside it you have access to glrepl.renderer and glrepl.manager, you also have access to a Dict of replkeys.json in sKeys. Which will be stringified and passed into the repl on init()

Most basic usage will be something like:

//Typescript signature is actually
//const functionOne = (k: number, ctx: {}) => {
const functionOne = (k, ctx) => {
    return `some message`;
};
glrepl.manager.kp.preloadFunction('doSomething', functionOne);

You can then use this in your replkeys.json app config by binding it to a key

{
    "bindings": [
        {
            "id": "pushSpace",
        
View on GitHub
GitHub Stars29
CategoryDevelopment
Updated4mo ago
Forks0

Languages

Max

Security Score

87/100

Audited on Nov 26, 2025

No findings