Skip to content

Add TypeScript mixxx controls definitions#15441

Merged
acolombier merged 4 commits intomixxxdj:2.6from
Serveny:control-types
Mar 5, 2026
Merged

Add TypeScript mixxx controls definitions#15441
acolombier merged 4 commits intomixxxdj:2.6from
Serveny:control-types

Conversation

@Serveny
Copy link
Contributor

@Serveny Serveny commented Oct 1, 2025

Exact TypeScript type definitions for the Mixxx controls available in JavaScript/TypeScript for full autocompletion and type-checking in your IDE. The editor shows you exactly which controls and groups are available and validates which controls are allowed within a group.

Forum post

Demonstration:
demo

@Serveny
Copy link
Contributor Author

Serveny commented Oct 1, 2025

I attempted to add the deprecated controls with a note, but currently, JSDoc deprecation warnings do not display for union types. Therefore, I believe it is best to only document the up-to-date types by this.

Comment on lines +92 to +94
| 'PeakIndicator'
| 'PeakIndicatorL'
| 'PeakIndicatorR'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI these and VuMeter controls have been moved/renamed to the [Main] group.
mixxxdj/manual#798

@Swiftb0y
Copy link
Member

Swiftb0y commented Oct 1, 2025

I appreciate the contribution, but I'm a little skeptical that this improves the developer experience enough to warrant the additional maintenance overhead this causes. Wdyt @JoergAtGithub

@JoergAtGithub
Copy link
Member

I personaly like it. But maintaining two parallel descriptions is of course additional maintenance effort.
If we find a way do generate the controls description in the manual from this type definition, we might have the best of both worlds. I tried something similar on the POC PR mixxxdj/manual#562 long time ago.
I wonder if we could check for the use of undefined controls during pre-commit using this type definitions. Such cases slipped several times through our reviews.

@Serveny
Copy link
Contributor Author

Serveny commented Oct 1, 2025

I also think that it's only worthwhile if it one of the parts can be generated.

Without knowing too much about C++ or the Mixxx code base itself, is it also possible to generate the types straight from the Mixxx-Code? The controls must surely be registered somewhere. If, for example, they could be read out as a dictionary with group and control, the path to Typescript types could not be that long.

@Serveny
Copy link
Contributor Author

Serveny commented Oct 1, 2025

I personaly like it. But maintaining two parallel descriptions is of course additional maintenance effort. If we find a way do generate the controls description in the manual from this type definition, we might have the best of both worlds. I tried something similar on the POC PR mixxxdj/manual#562 long time ago. I wonder if we could check for the use of undefined controls during pre-commit using this type definitions. Such cases slipped several times through our reviews.

Checking for undefined controls should be possible for JS and TS files via TS compiler prehook, at least as long as “as any” has not been passed by the developer in TS. In JS, you could at least check this with the tsconfig setting “checkJS”: true if the string was written directly into the function call in JS. I could test that too.

@JoergAtGithub
Copy link
Member

@Serveny This PR is your first contribution to Mixxx:
As a first-time contributor we need you to sign the Mixxx Contributor Agreement and comment here when you have done so. It gives us permission to distribute your contribution under the GPL v2 or later license and the Apple Mac App Store. It is also helpful for us to have contact information for contributors in case we may need it in the future

@JoergAtGithub
Copy link
Member

I noticed this case where the third argument (cthe callback) is of wrong type, and the whole function type overload seems to be no longer detected correctly:
grafik

@JoergAtGithub
Copy link
Member

The behaviour depends on how you concatenate strings:
grafik

@Serveny
Copy link
Contributor Author

Serveny commented Oct 6, 2025

The behaviour depends on how you concatenate strings

Oh, that could be a show-stopper. The Typescript compiler can't recognize compound strings; it simply interprets them as string.

image

The only solution I can think of off the top of my head is to make string checking optional, so that if only string is passed, it won't be checked.

I'll take a closer look at this and the other issues.

@Serveny
Copy link
Contributor Author

Serveny commented Oct 6, 2025

@Serveny This PR is your first contribution to Mixxx: As a first-time contributor we need you to sign the Mixxx Contributor Agreement and comment here when you have done so. It gives us permission to distribute your contribution under the GPL v2 or later license and the Apple Mac App Store. It is also helpful for us to have contact information for contributors in case we may need it in the future

Done! :)

@JoergAtGithub JoergAtGithub added controller backend developer experience Issues, bugs and PRs related to the development process, development environment & developer docs control objects Issues and bugs specifically in regard to mixxx `ControlObjects` and removed controller mappings labels Oct 6, 2025
@Serveny
Copy link
Contributor Author

Serveny commented Oct 6, 2025

The behaviour depends on how you concatenate strings

Fixed that, now group/control type check is disabled if group is just string.

But I have one more question:

The d.ts formatting seems inconsistent so far, for example, only sometimes using a semicolon and with an unknown tab or maximum line width. What are the usual code formatting guidelines for TS/JS? I found a .prettierrc.yaml file in the project. Is that intended for auto formatting the JS/TS code with prettier?

tabWidth: 2
singleQuote: false

@Serveny
Copy link
Contributor Author

Serveny commented Oct 10, 2025

I noticed this case where the third argument (cthe callback) is of wrong type, and the whole function type overload seems to be no longer detected correctly: grafik

I think the reason for this was the "Too complex type" error, appearing if the group was any and the typescript compiler had to check too many possibilities. If fixed it now by disabling the check if the group is any.

@JoergAtGithub
Copy link
Member

This is working in most cases, but also the control name can be a concatenated string:
grafik
grafik

@JoergAtGithub
Copy link
Member

I think not all variants of the ControlPotMeter controls are represented: https://manual.mixxx.org/2.6/en/chapters/appendix/mixxx_controls.html#controlpotmeter-controls

@JoergAtGithub
Copy link
Member

What do you think about seperated type groups for writeable and readonly controls? To hide the read-only COs from setValue etc.

@Serveny
Copy link
Contributor Author

Serveny commented Oct 12, 2025

@JoergAtGithub I think I was able to resolve your points:

  • Disabled control name check if name is just string
  • Excluded read-only controls for set functions
  • Added ControlPotMeter control suffixes

Known limitation:

  • VSCode cannot suggest groups with infinite possibilities (f.i. [ChannelN]). However, it recognizes them when they are written down and can suggest controls for the group.
  • The type structure with union types is a little bit complex and because of that maybe hard to maintain. As a next step, I would like to test a type structure where you have a kind of registry and assign tags to each control, which you can then use to filter

@JoergAtGithub
Copy link
Member

If I use the auto-completion in VS-Code and select a control from the suggestion list, it place the closing quotation mark before the cursor. Do you have an idea, if this can be fixed:

Aufzeichnung.2025-10-12.200742.mp4

@Serveny
Copy link
Contributor Author

Serveny commented Oct 12, 2025

If I use the auto-completion in VS-Code and select a control from the suggestion list, it place the closing quotation mark before the cursor. Do you have an idea, if this can be fixed

Cursor inside string after suggestion is the default vscode behaivior, but there is (just vscode things 😄) an extension for tab to jump out of the string or brackets: https://marketplace.visualstudio.com/items?itemName=albert.TabOut

Alternatively, you can also set a script to Enter, then it will be exactly as you described. To activate, copy this in your keybinding.json (Strg + Shift + P and search for "keyboard shortcuts json"):

   {
        "key": "enter",
        "command": "runCommands",
        "args": {
            "commands": [
                "acceptSelectedSuggestion",
                "cursorRight"
            ]
        },
        "when": "suggestWidgetVisible && textInputFocus"
    }

EDIT:
I tested a little bit more. The shortcut script has side effects like jumping in next line . It looks that I can't restrict it to run only inside strings without writing an vscode extension for it.

@JoergAtGithub
Copy link
Member

This is LGTM now! But what are your ideas to prevent double maintenance of the list of COs?

@Serveny
Copy link
Contributor Author

Serveny commented Oct 13, 2025

This is LGTM now! But what are your ideas to prevent double maintenance of the list of COs?

Cool :) Hmm, there are various good ways to achieve that:

  1. In my opinion the best way would be to generate the TS types directly out of the C++ code base. So, nothing needs to be changed on the current documentation workflow and the types are always in sync with the C++ code base. But I could only do that together with someone who is familiar with C++ or the Mixxx code base.
  2. The second good option is to generate the controls documentation from TS types with TS doc comments. I could write a python script for that.

Conclusion: If we can read out the controls informations about groups, control name, read-only & isDeprecated I would prefer option 1, if not option 2.

@ronso0
Copy link
Member

ronso0 commented Oct 13, 2025

I'd go with 1. generate from c++ code base (though that's not trivial afaict) and maintain the manual by hand.

Because 2. generate the controls documentation from TS types with TS doc comments is not really a time safer.
a) it requires cross-repo actions (mixxxdj/mixxx -> mixxxdj/manual)
b) controls are not just listed, often we need add hints, references to other controls, to other manual sections etc.

@Serveny
Copy link
Contributor Author

Serveny commented Oct 17, 2025

I created a little Python prototype how we could generate the types:

  1. Analyze the C++ code with clang package (for testing purposes I used regex)
  2. Create a dict with structure:
{
  group: { 
    controlname: {
      is_legacy: bool,
      is_read_only: bool,
    }
  }
}
  1. Generate the TypeScript file from contols dict (first look)

@ronso0 Do you think this is the right path, or is there a better way than extracting the controls from the C++ code via Python and clang package?

EDIT: I tried this with Python and clang, but it was very slow and I couldn't find the right tokens (probably because the imports still need to be linked somehow, idk).

@Serveny Serveny changed the base branch from main to 2.6 January 24, 2026 08:25
@Serveny
Copy link
Contributor Author

Serveny commented Jan 25, 2026

I built a workflow like the changelog.yml, but I don't know if it works or how to test it.

What it should do:

  1. Pull the Mixxx repo
  2. Pull the manual repo into a subfolder
  3. Build the manual with make html (I need the build/.doctrees/chapters/appendix/mixxx_controls.doctree file)
  4. Run the tools/ts_export_docs_controls.py
  5. Push res/controllers/mixxx-types.d.ts into the Mixxx repo

The trigger inside the manual repo could look like this:

name: Generate TypeScript Control Types
on:
  push:
    branches:
      - '**'
    paths:
      - 'source/chapters/appendix/mixxx_controls.rst'
permissions: {}
jobs:
  trigger-ts-control-types-update:
    name: Trigger mixxx-types.ts update on Mixxx repository
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Start workflow run on Mixxx repository
        uses: peter-evans/repository-dispatch@v4
        if: env.MIXXXBOT_TOKEN != null
        with:
          token: ${{ env.MIXXXBOT_TOKEN }}
          repository: mixxxdj/mixxx
          event-type: update-ts-crtl-types
          client-payload: '{"branch": "${{ github.ref_name }}", "ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
        env:
          MIXXXBOT_TOKEN: ${{ secrets.MIXXXBOT_TS_TYPES_AUTOUPDATER_PAT }}

I'm a little bit unshure if this is the right way, because we have to pull and build the manual every time. Is it possible to run the Python script when mixxx-controls.rst is changed after the build, which runs anyway, and simply copy mixxx-controls.d.ts over to the Mixxx repository?

@JoergAtGithub
Copy link
Member

JoergAtGithub commented Jan 25, 2026

Could you please move mixxx-controls-ts.yml to a follow up PR. This is too much for a single PR and requires reviewers with different skills.
Set this PR as ready for review once all functionality is in place and let add the automation afterwards.

--
Workflow feedback:
I think ts_export_docs_controls.py should run in the manual repo, as the manual is the source. If we have a manual PR that breaks the required syntax, we wouldn't notice this until we merged the manual PR.

@Serveny
Copy link
Contributor Author

Serveny commented Jan 25, 2026

@JoergAtGithub All right, yes, that makes more sense. Then I'll move the ts_exportdocscontrols.py script and the workflow into a new pull request in the manual repository, then only the Typescript changes will remain here.

@Serveny Serveny marked this pull request as ready for review February 5, 2026 21:44
@Serveny
Copy link
Contributor Author

Serveny commented Feb 5, 2026

Hi, another quick (final?) update:

  • Added definitions for common-controller-scripts.js



For documentation, I wrote a little instruction how to use these types:

New Type Definitions & Usage Guide

Project Setup

Add the new definition files to your tsconfig.json include array:

{
  "compilerOptions": { ... },
  "include": [
    "/path/to/your/mixxx/controllers/mixxx-controls.d.ts",
    "/path/to/your/mixxx/controllers/script-api.d.ts"
  ]
}

Enhanced Documentation (Optional)

To see full control documentation and suggestions in your editor's "Quick Info," use TS Union Docs.

Warning

Do not install both the extension and the plugin simultaneously, as this will result in duplicate documentation tooltips.

Enabling Strict Type Checking

By default, these types are permissive (they won't complain if you pass a generic string). To enforce strict checking for groups and controls, create a global.d.ts file in your project:

declare namespace MixxxControls {
    interface Config {
        strict: true;
    }
}

Implementation Example

You can use these types to build your own typesafe abstractions in TypeScript. Here is an example of a typesafe Button class:

class Button<TGroup extends MixxxControls.Group> {
    constructor(
        public group: TGroup,
        public inKey: MixxxControls.CtrlRW<TGroup>, // Read or Write controls
        public outKey: MixxxControls.Ctrl<TGroup>   // Read and Write controls
    ) {}

    enable(): void {
        script.triggerControl(this.group, this.inKey);
    }

    read(): number {
        return engine.getValue(this.group, this.outKey);
    }
}

// Usage
const playBtn = new Button('[Channel2]', 'play', 'play_indicator');

@JoergAtGithub
Copy link
Member

  • Added definitions for common-controller-scripts.js

Could you explain why we need a declaration for this one JavaScript-Library only? Until now, we just included the Library-File itself in the jsconfig.json. How does this work now with all our other libraries written in JavaScript?

@Serveny
Copy link
Contributor Author

Serveny commented Feb 7, 2026

Could you explain why we need a declaration for this one JavaScript-Library only?

@JoergAtGithub Yes, because several functions in this library interact with Mixxx Controls. By using a .d.ts file, we can provide suggestions and quick info documentation for the controls. For example these functions:

function toggleControl<TGroup extends MixxxControls.Group>(group: TGroup, control: MixxxControls.CtrlRW<TGroup>): void;
function triggerControl<TGroup extends MixxxControls.Group>(group: TGroup, control: MixxxControls.CtrlRW<TGroup>, delay?: number): void;

How does this affect our other JavaScript libraries?

It’s entirely optional. If you include the declaration file in your jsconfig.json, you’ll get the enhanced documentation and type checking. If you don't, the behavior remains exactly as it was before.

The reason I added that is because I use strict types it in my controller mapping that I'm working on, and I thought it might be interesting for others as well. The only disadvantage is that if changes are made to the JS library API, the type definitions would have to be updated accordingly.

@ywwg
Copy link
Member

ywwg commented Feb 24, 2026

from the dev discussion: this is low risk and we'd like to get this in if we can

@Serveny
Copy link
Contributor Author

Serveny commented Feb 25, 2026

from the dev discussion: this is low risk and we'd like to get this in if we can

Cool, if there is anything else I need to adjust or document further, please let me know :)

@ywwg
Copy link
Member

ywwg commented Feb 25, 2026

@acolombier @daschuer any notes for @Serveny ?

@JoergAtGithub
Copy link
Member

I will test this again this weekend!

@JoergAtGithub JoergAtGithub self-requested a review February 25, 2026 18:30
@acolombier
Copy link
Member

I tried and getting it to work with the devcontainer setup but couldn't get it to load unfortunately. Would you be able to help here? I assume I must be missing something...

@JoergAtGithub
Copy link
Member

We should also consider that we published the following setup for mapping developers: https://mixxx.org/news/2024-08-18-controller-api-declartions/
If now a different setup is required, we somehow need to inform the mapping developers about the change.

@Serveny
Copy link
Contributor Author

Serveny commented Feb 25, 2026

I tried and getting it to work with the devcontainer setup but couldn't get it to load unfortunately.

@acolombier Hmm, so the problem is you listed the extension in .devcontainer/devcontainer.json under customizations.vscode.extensions and if you use the command Developer: Show running extensions the TS Union Docs-Extension is not there?

We should also consider that we published the following setup for mapping developers

@JoergAtGithub Yes, developers would mainly need to be informed about the new entries in ts/js-config and about the (optional) plugin. I think what I have written here covers everything.

@acolombier
Copy link
Member

The TS Union Docs-Extension in the running extension list, but not type annotations is available when editing the mapping files

@Serveny
Copy link
Contributor Author

Serveny commented Feb 26, 2026

@acolombier That's because the js/ts compiler must be informed that there are definitions. You could add a jsconfig.json into the res/controllers folder and it should work:

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false,
    "noEmit": true
  },
  "include": [
    "*.js",
    "*.d.ts"
  ]
}

@acolombier
Copy link
Member

Ah indeed, this was the needed fix. Should we add this file in the repo? Or do we think documenting the need for it is enough?

@JoergAtGithub
Copy link
Member

No, the jsconfig.js must always be user specific. It should only list the files, the user really uses in his project.(listed in the mappings XML file) otherwise you see symbols of files you haven't included.

Copy link
Member

@acolombier acolombier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
Tested and this provide much better intelisense. It isn't perfect, but it remains a net improvement so let's merge and start from there.

@acolombier acolombier merged commit c1c80bb into mixxxdj:2.6 Mar 5, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

control objects Issues and bugs specifically in regard to mixxx `ControlObjects` controller backend controller mappings developer experience Issues, bugs and PRs related to the development process, development environment & developer docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants