Feature Script Menu customization

philmasterplus

Active member
Currently, the Script Menu mirrors the layout of files and directories under scripts/. This poses a problem as we build large projects like autoscend that install many files and clutter the Script Menu. Users "solved" the problem by moving files into subdirectories. KoLmafia enabled this by (A) allowing scripts to be imported from anywhere and (B) intelligently updating files during svn update.

Recent events have shown that these solutions have flaws. (A) is not true in JavaScript.† (B) has caused issues and has been effectively disabled. The traditional solution no longer works.

require("foo.js") does not recursively search all subdirectories of scripts/ for foo.js.

To remedy this, I suggest making the Script Menu more flexible. Some ideas:
  1. Allow users to hide files from the Script Menu. KoLmafia could keep a "exclude list" in a settings file.
  2. Allow the Script Menu to be fully customizable and data-driven, decoupling them from the filesystem. The new Menu would be similar to bookmarks in web browsers, or icons in the Windows Start Menu/the macOS Dock.
Both ideas are mutually exclusive. Idea #1 would be easier, but #2 would be more flexible.

Edit: @taltamir suggested something similar to Idea #1:
I think it wold be great if there was a way to instruct mafia itself that a specific file should be excluded from the scripts dropdown menu?
like starting the first line with
#exclude_dropdown
and some way for include to handle it. (either only use it to exclude from dropdown if it is the first line. or have the include command skip that specific line)

or maybe
exclude_dropdown("scripts/autoscend/util.ash");

@gausie and @xKiv offered other suggestions, such as adding svn/*/scripts/ to the "search path", and allowing projects to declare endpoints/entrypoints. These are good ideas and should be investigated as well, but I feel they are orthogonal to my suggestions.
 
Last edited:

xKiv

Active member
Yes, I think this is orthogonal to where the scripts end up being found on the filesystem.
I still think svn-installed scripts should provide a manifest (like we have dependencies.txt now) with list of their "entrypoints" and their preferred location in script menu. (or anything not listed as an entrypoint would just get automatically blacklisted from menu).
Having a directive in a script file itself sounds like it could work too (in which case I would want the default to be "exclude"). I can then customize by editing the file under svn/... and let automatic merge handle the change until conflict happens (at which point I would probably want manual resolution anyway)

I have also just noticed that we might be talking about two different things when we say "script menu". I am talking about the one in mafia's gui itself, which is currenlt populated from all files under scripts/.
There's also a "script dropdown" in relay browser, generated from relay/relay_* ; I don't think there's a reason to add directives to exclude those. If a script is not supposed to go there ... it just shouldn't be named relay/relay_...
fronobulax said:
How about someone who knows Javascript write such a thing as a relay script?
Which "such a thing"? An interface for .. functionality that doesn't exist?

Or are you asking for a fully functional relay script that would somehow
  • store for each script if it should be included, and under what path in the menu
    • how would it even know which scripts are available?
  • generate a menu from this information (in browser)
  • when I click on a script in this generated menu, it would run the script in mafia
?
 

philmasterplus

Active member
@fronobulax
We could prototype such a relay script. But I expect it would be a mere prototype. It would be made redundant when changes are made the Script Menu itself.

Maybe I should create a mockup of the New Script Menu as a series of images.

@xKiv I am referring to the Script Menu in the Swing UI.
 

MCroft

Developer
Staff member
As for separating the menu from the directory structure, it seems like there are multiple approaches to consider:
  1. the current model of no mapping
  2. a bookmark/registry method where we required scripts to tell us their entry points and we put it in a menu item.
    This could even extend the dependencies model and make the dependencies file more of a manifest.
  3. The one I just thought of, which is a convention-based system, like we do with prefs.
    e.g. "Don't put it on the menu if it ends with .hideme.ash"
    or "new file extensions for relay, menu or hidden files" (.rash, .mash or .hash, but I'm just ridiculous that way).
It may be possible to blend our way from 1 to either 2 or 3. Although we'd need to assure that if we put in a feature, that scripts that adopted it didn't fail for users who haven't updated mafia...
 

fronobulax

Developer
Staff member
About 70% of the files in my script directory were created by me, never shared and neither updated not installed by SVN. Please don't make me do anything new to find my scripts, such as having to create a manifest or changing the script because the alternative is not finding it :)

My solution to a Script Choosing Gui being cluttered was to embrace the (alternative) Script MRU Menu. A script is only there if I actually ran it and I can navigate the filesystem if it is not there. Now that it can scroll I can see the 25 or so scripts I am most likely to use in one display and am happy. I should note I almost always launch scripts from a menu and not the gCLI so what works fine for me might not be the choice of a better typist.

In Ye Olden Days when I remembered design patterns and best practices this would have been a classic application of the Model-View-Controller pattern. Simplistically, there is the file system, the way the user wants to interact with it and the glue that keeps them in sync. I note that if we used a MVC type pattern to choose scripts for launching, the possibility of actually running the script from its SVN directory becomes much easier to implement.

My build one comment was to have a prototype script viewer and launcher that ran from the relay browser. If it is data driven just ask the user to provide the data now (as opposed to automatically deriving some of the date at some point in the future). My bookmarks menus expect me to organize them. So could this scripts prototype. It would be a proof of concept. I would expect that it would either: a) not work as well as we hope and be abandoned; b) work well enough to be an alternative to a Java based GUI; or c) be the requirements spec for the replacement java GUI.
 

MCroft

Developer
Staff member
About 70% of the files in my script directory were created by me, never shared and neither updated not installed by SVN. Please don't make me do anything new to find my scripts, such as having to create a manifest or changing the script because the alternative is not finding it :)
My sketch of a design included "leave stuff alone for people who had simple/personal/unlikely to change/unmaintained scripts" plus "add a way to declutter the menu for something large and modular, like autoscend or something that's never used by a user, like zlib." Does that not exactly meet your requirement?

Please don't make me run scripts from the relay browser, that's a requirement I'd like to add. And don't make me type it the first time. If I installed it, I want it, or the parts of it that should be exposed.

My solution to a Script Choosing Gui being cluttered was to embrace the (alternative) Script MRU Menu. A script is only there if I actually ran it and I can navigate the filesystem if it is not there. Now that it can scroll I can see the 25 or so scripts I am most likely to use in one display and am happy. I should note I almost always launch scripts from a menu and not the gCLI so what works fine for me might not be the choice of a better typist.

In Ye Olden Days when I remembered design patterns and best practices this would have been a classic application of the Model-View-Controller pattern. Simplistically, there is the file system, the way the user wants to interact with it and the glue that keeps them in sync. I note that if we used a MVC type pattern to choose scripts for launching, the possibility of actually running the script from its SVN directory becomes much easier to implement.

My build one comment was to have a prototype script viewer and launcher that ran from the relay browser. If it is data driven just ask the user to provide the data now (as opposed to automatically deriving some of the date at some point in the future). My bookmarks menus expect me to organize them. So could this scripts prototype. It would be a proof of concept. I would expect that it would either: a) not work as well as we hope and be abandoned; b) work well enough to be an alternative to a Java based GUI; or c) be the requirements spec for the replacement java GUI.
I am skeptical, but if someone else is writing it, I'm willing to look at it when it gets to the Java menu.
 

fronobulax

Developer
Staff member
"Please don't make me run scripts from the relay browser, that's a requirement I'd like to add. And don't make me type it the first time. If I installed it, I want it, or the parts of it that should be exposed."

Not sure I understand this requirement. In the dusty corners of my memory I seem to recall some issues with the ability to open a browser as opposed to using the already open browser and the inability to solve that which is why there is a scripts menu in the relay browser. Issues would be in terms of which version of which browser, whether a browser has been launched, or not, tabs vs. new window and OS differences. Note that the Mini-Browser was written to guarantee that KoLmafia always had a way to display HTLM pages, whether the user has specified a relay browser, or not. So there will be a class of scripts that require the user to have a properly configured relay browser.

Are you imagining something like the Android Play store where immediately after installing an app, there is a button to launch it? I'd rather find it again or trust the gCLI to find it but that is because many of the other people's scripts I use have prerequisites (turns left, access to NEP, etc.) that might not be met when I install.
 

MCroft

Developer
Staff member
"Please don't make me run scripts from the relay browser, that's a requirement I'd like to add. And don't make me type it the first time. If I installed it, I want it, or the parts of it that should be exposed."

Not sure I understand this requirement.
That was unclear.
"Please don't make me run scripts from the relay browser", that's a requirement I'd like to add.
And don't make me type it the first time. I am looking to avoid the chair swivel from the app to the browser to run a script, then come back to KoLmafia.

If I installed it, I want it, or the parts of it that should be exposed on the scripts menu."
What I meant here was that if I install a script, I want to have it on the scripts menu. I do not want it on the relay scripts menu. I do not want it on the scripts menu later, but only if I happen to run it.

That may not be possible, or it may not be a good idea, and even in the current state of things, the script menu needs to be refreshed if I want this in the same mafia execution. But right now, if I install a script, it will be on the scripts menu in mafia, from whence I am likely to select it and run it. I don't want to lose that, because that's how I use it. I know that's not everyone's preference, but it's what currently happens.
 
Last edited:

fronobulax

Developer
Staff member
You might just consider enabling the ScriptsMRU menu. Once you have run a script you will be able to find it there the next time you want it. Some hiccups because you can't just click on a planting script in that menu and have it run and the menu does not refresh automatically under all the circumstances you want. To the extent I understand your requirement I'd say it is at least a 50% solution that is there now.

I don't quite see your other requirement. You can run from the mafia GUI and you can get there in two clicks. I don't get the chair swivel because anything that appears in that other menu requires the relay browser so you will be swiveling no matter whether you swivel and launch or launch then swivel. Vocabulary issue?
 

MCroft

Developer
Staff member
You might just consider enabling the ScriptsMRU menu. Once you have run a script you will be able to find it there the next time you want it. Some hiccups because you can't just click on a planting script in that menu and have it run and the menu does not refresh automatically under all the circumstances you want. To the extent I understand your requirement I'd say it is at least a 50% solution that is there now.
I haven't used it and in general haven't seen a need to, since I almost never use the relay scripts menu (or, honestly, remember that it exists). I am familiar with the concept, because it's baked into the maximizer GUI (and my text editor, and other similar tools). I can look at it, but as I understand MRU for scripts, it's a relay browser feature. I am explicitly wanting a solution that does not require me to use the relay browser to get a script menu. I can look as a "it'll be like this, when we get it", but I don't see it as currently providing any support for my proposed requirements/use cases.

I don't quite see your other requirement. You can run from the mafia GUI and you can get there in two clicks. I don't get the chair swivel because anything that appears in that other menu requires the relay browser so you will be swiveling no matter whether you swivel and launch or launch then swivel. Vocabulary issue?

Possibly, so let me try to be clearer. In any system, if you have to leave one app to do a task and then return, there's a context switch. Sometimes you come back, sometimes you get side-tracked, in every case, leaving App A for App B and back is less efficient than staying in App A and having a way to complete the task. In industry jargon, this is called swivel chairing. I shouldn't have used that term.

But this may be an issue because we're not talking about the same "scripts" menu.

I am not talking about scripts that require the relay. I have not been considering scripts in the relay menu at all. The relay menu is already cleaner for something like autoscend, where there's one entry on the relay scripts menu, but the KoLmafia scripts menu has 86 items in 5 subfolders for that package, when it needs 1 or maybe 2. I can see why there is less concern about cleaning up the relay scripts menu, but we end up with 98.83% useless entries on the KoLmafia scripts menu.

If this is confusing because you only use gCLI and relay scripts menu and I only use gCLI and KoLmafia scripts menu, then we can work out which cases we're talking about.
 

philmasterplus

Active member
Here's a detailed Bookmarks-based proposal that I had in mind for a while.

  • Script Menu items are Bookmark-based.
  • Each script menu item has a name, a list of submenus, and a command.
  • Script Menu configuration is stored in settings/bookmarks.json. It has the format:
    JSON:
    {
      "bookmarks": [
        {
          // Name of the menu item
          "name": "Run foo",
          // gCLI command that is executed when the menu is clicked
          "command": "call foo.ash",
          // Array of submenu items
          // Leave empty to place the menu item at the top level
          "submenu": []
        },
        {
          "name": "Do something",
          "command": "call bar.ash",
          // This item is under two submenus: foo -> bar ->
          "submenu": ["foo", "bar"]
        },
        {
          "name": "Do breakfast",
          // Arbitrary gCLI commands are supported
          "command": "breakfast",
          "submenu": ["My Tasks"]
        }
      ]
    }

    This would create the following menu structure:
    Code:
    - Run foo
    - foo
      - bar
        - Do something
    - My Tasks
      - Do breakfast

    Note that each bookmark is a "leaf" node in the hierarchy of menu items and submenus. There is no setting to define an empty submenu item.
    Also, all menu items and submenus are sorted alphabetically. This is probably easier to implement than allowing arbitrary sorting.

Each bookmark executes a gCLI command when the user clicks it. This allows users to add not only scripts, but any task that can be performed via gCLI.

When a project is installed, KoLmafia will add all ASH and JS files in the scripts/ directory to the Bookmarks menu. This simulates what the old Script Menu does (show every file). Ofc you can edit or remove individual bookmarks. This might be changed later when we implement Manifests and Entry Points.

When a project is uninstalled, KoLmafia removes all bookmarks whose command matches call <file> for each file removed during the uninstall. Other bookmarks are untouched
 
Last edited:

MCroft

Developer
Staff member
Here's a detailed Bookmarks-based proposal that I had in mind for a while.

  • Script Menu items are Bookmark-based.
  • Each script menu item has a name, a list of submenus, and a command.
  • Script Menu configuration is stored in settings/bookmarks.json. It has the format:
    JSON:
    {
      "bookmarks": [
        {
          "name": "Run foo",
          "command": "call foo.ash",
          "submenu": []
        },
        {
          "name": "Do something",
          "command": "call bar.ash",
          "submenu": ["foo", "bar"]
        }
      ]
    }

    This would create the following menu structure:
    Code:
    - Run foo
    - foo
      - bar
        - Do something

Each bookmark executes a gCLI command when the user clicks it. This allows users to add not only scripts, but any task that can be performed via gCLI.

When a project is installed, KoLmafia will add all ASH and JS files in the scripts/ directory to the Bookmarks menu. This simulates what the old Script Menu does (show every file). Ofc you can edit or remove individual bookmarks. This might be changed later when we implement Manifests and Entry Points.

When a project is uninstalled, KoLmafia removes all bookmarks whose command matches call <file> for each file removed during the uninstall. Other bookmarks are untouched

I like it, generally.

Could we let a script edit the bookmark file or would that be opening a can of worms?

Are you thinking we'd recursively remove -bar and then -foo if they were empty following an uninstall?

Could you pass parameters on the command line to your script? It seems as if you could...

What happens when someone copies (or writes) a script into "scripts"? How do we simulate adding it to the menu if it comes from outside KoLmafia?

Do we do any sort of startup or on-demand (or on launching the Script Manager) validation (e.g. "is bar.ash still in /scripts?"). If so, what would it do if not found?

For managed scripts, we already have a data structure we use to pass extended information to ScriptManagerFrame (forumURL, long desc, etc). Could that be extended, for scripts that want it, to either point to a "post-install script", which would clean up the menus, or a foo.ash specific manifest json object, which would give us a way to not start with 86 objects and have to delete 85...

If that's too ambitious, we can start with duplicating the current actions, which at least won't get worse and lets me and my text editor fix things for me...
 

philmasterplus

Active member
The bookmarks file would be treated just like any other settings file. We don't stop you from modifying it with buffer_to_file(), but we don't support that use case.

Passing parameters would be possible. Set the command to script_file.ash foo bar, and the script will be invoked with foo bar as the argument string.

We shouldn't try too hard to synchronize the Bookmarks Menu with scripts/. It's supposed to reduce coupling between the UI and the filesystem. Files you add under scripts/ will not automatically appear under Bookmarks. Simple validation might be okay, though.

I'm not too fond of a postinstall script, but it would be orthogonal to this suggestion.
 

fronobulax

Developer
Staff member
"I almost never use the relay scripts menu"

Wherein the source of our confusion is established. The ScriptsMRU has nothing to do with the relay browser. When it is enabled it is invoked from the main KoL page under Scripts. The menu you get when doing that is either a list of scripts derived from the file system OR the most recently used scripts. The MRU is per character so, while the Scripts menu will do something reasonable when you are not logged in, it won't be any kind of MRU.

This is separate and distinct from the relay scripts menu which is not even available for use unless a relay browser is running. A script only appears in this menu if it is in /relay and the file name starts with "relay_". At one time such a script required a browser to render a portion of its output so running without the relay browser was not expected to work.
 

fronobulax

Developer
Staff member
Here's a detailed Bookmarks-based proposal that I had in mind for a while.

  • Script Menu items are Bookmark-based.
  • Each script menu item has a name, a list of submenus, and a command.
  • Script Menu configuration is stored in settings/bookmarks.json. It has the format:
    JSON:
    {
      "bookmarks": [
        {
          "name": "Run foo",
          "command": "call foo.ash",
          "submenu": []
        },
        {
          "name": "Do something",
          "command": "call bar.ash",
          "submenu": ["foo", "bar"]
        }
      ]
    }

    This would create the following menu structure:
    Code:
    - Run foo
    - foo
      - bar
        - Do something

Each bookmark executes a gCLI command when the user clicks it. This allows users to add not only scripts, but any task that can be performed via gCLI.

When a project is installed, KoLmafia will add all ASH and JS files in the scripts/ directory to the Bookmarks menu. This simulates what the old Script Menu does (show every file). Ofc you can edit or remove individual bookmarks. This might be changed later when we implement Manifests and Entry Points.

When a project is uninstalled, KoLmafia removes all bookmarks whose command matches call <file> for each file removed during the uninstall. Other bookmarks are untouched

I'm not quite understanding the relationship between your JSON and the hierarchy implied in the example.

If the menu is a tree structure and the only actually choices that do something are leaves in the structure then that is A Good Thing and I don't need to understand :)
 

MCroft

Developer
Staff member
"I almost never use the relay scripts menu"

Wherein the source of our confusion is established. The ScriptsMRU has nothing to do with the relay browser. When it is enabled it is invoked from the main KoL page under Scripts. The menu you get when doing that is either a list of scripts derived from the file system OR the most recently used scripts. The MRU is per character so, while the Scripts menu will do something reasonable when you are not logged in, it won't be any kind of MRU.
OK. How is it enabled? I didn't see a preference or a menu option to do so. I'll give it a try.

Edit: seems to be on. also full of my text cruft. It turns out that this is sorta the opposite of what I'm looking for, which is a way to keep junk from accumulating in that list, rather than a way to add just by running a script.
 
Last edited:

fronobulax

Developer
Staff member
The menu is always enabled because it is a core part of the mafia gui.

Setting the preference scriptMRULength to something greater than zero will build the MRU menu and use it instead of parsing the file system. A value greater than 25 is recommended because the scrolling works well enough.

Hard to find documentation at https://wiki.kolmafia.us/index.php?title=Hidden_Features

There are opportunities for improvement but most of them apply to the Scripts Menu in general and not specifically to the MRU.
 

philmasterplus

Active member
I'm not quite understanding the relationship between your JSON and the hierarchy implied in the example.

If the menu is a tree structure and the only actually choices that do something are leaves in the structure then that is A Good Thing and I don't need to understand :)

Yes, it's supposed to contain leaf nodes only. I don't see a need for empty submenus to exist. I updated reply #12 to hopefully make it a bit more clear.
 

fronobulax

Developer
Staff member
OK. How is it enabled? I didn't see a preference or a menu option to do so. I'll give it a try.

Edit: seems to be on. also full of my text cruft. It turns out that this is sorta the opposite of what I'm looking for, which is a way to keep junk from accumulating in that list, rather than a way to add just by running a script.
Now I don't understand.

If things are working as designed there will be nothing in that list that is not a script file and everything in that list was either initiated directly by user interaction or a call in a script.

You might consider inspecting or clearing scriptMRUList.

I would not use "text cruft" to describe the name of anything that was important enough to put in its own script file, but if you do then I guess I understand although some of your problems are of your own making :)
 
Top