Skip to content

feat: add mango_layouts plugin - #192

Draft
ezequielgk wants to merge 7 commits into
noctalia-dev:mainfrom
ezequielgk:add-mango-layouts
Draft

feat: add mango_layouts plugin#192
ezequielgk wants to merge 7 commits into
noctalia-dev:mainfrom
ezequielgk:add-mango-layouts

Conversation

@ezequielgk

Copy link
Copy Markdown

Plugin

  • Id: ezequiel/mango_layouts
  • New plugin
  • Update to an existing plugin (version bumped in plugin.toml)

What it does

A clean, minimalist layout switcher plugin for MangoWC. It provides a bar widget to see the current workspace layout, and a fast popup panel to switch workspace layouts on the fly directly from the desktop. Includes dynamic settings to customize the widget appearance (icon, text, colors) and a vertical list mode.

External dependencies

  • jq: Used to parse the JSON output from the MangoWC IPC.
  • mmsg: The MangoWC CLI utility used to read the active monitor state and set the new layout.

Testing

Toggled layouts using the panel grid and vertical list modes. Tested all dynamic plugin settings (glyph, text, color, layout visibility toggles). Verified mmsg IPC commands work seamlessly and panel updates instantly.

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor: MangoWC
  • Noctalia version tested against: v5
  • Plugin API level: 14

Screenshots / Videos

recording.mp4

Checklist

  • The directory name matches the part of id after the / in plugin.toml exactly.
  • It ships plugin.toml, README.md, thumbnail.webp, and translations/en.json.
  • README.md follows the
    README template, documents
    every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
  • I created thumbnail.webp with the thumbnail generator.
  • version follows semver and is bumped in this PR; plugin_api is the oldest API level this plugin requires.
  • Every non-English translation in this PR uses a locale supported by Noctalia core, and I can read, write, and
    understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
  • I did not edit catalog.toml; CI generates it.
  • This PR touches exactly one plugin directory.

Code review attestation

Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:

  • The code is readable and not obfuscated, minified, or generated.
  • It does not download and execute remote code.
  • Every network call, filesystem write, and spawned process is something the description above accounts for.
  • I have the right to publish this code under the license declared in plugin.toml.

@ItsLemmy

ItsLemmy commented Aug 1, 2026

Copy link
Copy Markdown
Contributor
  1. blocking - mango_layouts/panel.luau:31-52

    Picking a layout does more than dispatch to the compositor. The spawned bash script
    rewrites the user's MangoWC configuration file at
    $HOME/.config/mango/conf.d/workspaces.conf: it copies the file to a mktemp temp file
    in the same directory, runs sed -i over it, and then mv replaces the original.

    Nothing declares this. The PR description lists only "reads the active monitor state
    and set the new layout", README.md:56 says only that mmsg is used "for reading the
    active monitor state and applying the selected layout", and the "Code review
    attestation" checkbox asserting that every filesystem write is accounted for is
    checked. A user installing a "layout switcher" has no way to know that a single click
    permanently edits their compositor config.

    The write is also destructive rather than additive. mv "$TMP_FILE" "$CONF_FILE"
    replaces the original with no backup, so a sed pattern that matches something the
    author did not anticipate silently rewrites the user's config with no way back. The
    temp file comes from mktemp (mode 0600), so the moved-in file also silently loses the
    original config file's permissions.

    The default is the widest possible one. panel.luau:1 sets global_toggle = true, so
    the "Apply to all" branch is what runs on the first click, and
    sed "s/layout_name:[a-zA-Z_]*/layout_name:$LAYOUT/g" rewrites every layout_name:
    occurrence in the whole file, including tagrules for workspaces the user was not
    switching. That toggle is not documented anywhere: it appears only as a hardcoded
    "Apply to all" label at panel.luau:169 and is absent from the README settings tables.

    To resolve: document the config-file rewrite in README.md and the PR description,
    default the persistence toggle to off, and prefer a non-destructive update path
    (preserve the original mode and keep a backup, or write only the targeted tagrule).

  2. non-blocking - mango_layouts/plugin.toml:10

    dependencies = ["jq", "mmsg"] omits the shell utilities the panel actually invokes:
    bash (panel.luau:32), plus mktemp, cp, sed, and mv (panel.luau:39-49).
    Repo convention is to list these; see drive-health/plugin.toml:8 and
    jetbrains-provider/plugin.toml:10, which declare bash, sed, mktemp, and mv explicitly.
    README.md:14 likewise mentions only jq and mmsg.

  3. non-blocking - mango_layouts/widget.luau:49-53

    The custom_color setting is advertised as "Icon/Text Color"
    (mango_layouts/translations/en.json:4, mango_layouts/README.md:52), but the widget only
    calls barWidget.setColor, which sets text color, and barWidget.setForeground, which
    is not part of the widget API at all (the binding table is setText, setGlyph, setImage,
    setTooltip, clearTooltip, setFont, setColor, setGlyphColor, isVertical, outputName,
    setVisible, render). Both calls are wrapped in pcall, so the failure is invisible.

    With the shipped defaults (show_glyph = true at plugin.toml:22, show_text = false at
    plugin.toml:28) the widget renders only a glyph, so setting custom_color produces no
    visible effect whatsoever. barWidget.setGlyphColor is the missing call.

  4. non-blocking - mango_layouts/widget.luau:40

    barWidget.setGlyph(nil) cannot clear the glyph: the binding does a checked string
    read of argument 1, so nil raises, and the surrounding pcall swallows it. Same at
    widget.luau:58. Passing an empty string is the working form.

  5. non-blocking - mango_layouts/widget.luau:27

    A 500 ms update interval spawns /bin/sh -c "mmsg get all-monitors | jq ..." twice per
    second for the lifetime of the session, which is three processes per tick. Every other
    plugin in the repo that polls an external command uses 1000 ms or slower, including the
    comparable mangowm-keymode/keymode.luau:20, which polls mmsg at 1000 ms.

  6. non-blocking - mango_layouts/README.md:51

    Documents show_text default as true; plugin.toml:28 sets default = false.

  7. non-blocking - mango_layouts/panel.luau:169

    The "Apply to all" label is a hardcoded English string even though the plugin ships
    translations/en.json and routes every settings label through label_key. The layout
    names at panel.luau:4-17 are hardcoded English as well.


I really don't see why you need to write to the config for a simple "Switcher" seems very overkill / dangerous.

@ItsLemmy
ItsLemmy marked this pull request as draft August 1, 2026 13:31
@ezequielgk

Copy link
Copy Markdown
Author

Thank you so much for the review. You were right about the config file rewrite it was definitely overkill and not the right approach for such a simple switcher.

I've just pushed a new commit addressing all of your feedback:

  1. Removed the config file rewrite (Blocking): The Bash script has been removed entirely. The panel now simply sends an mmsg dispatch setlayout,$LAYOUT command through noctalia.runAsync to switch the layout dynamically. Since the plugin no longer edits or persists changes to the user's configuration files, I also removed the undocumented "Apply to all" global toggle and simplified the overall logic.
  2. Dependencies: With the Bash script gone, the plugin no longer relies on mktemp, cp, sed, or mv. The declared dependencies (jq and mmsg) are now accurate.
  3. Widget Color API: Replaced the non-existent barWidget.setForeground call with barWidget.setGlyphColor, and removed the unnecessary pcall wrappers so the color is applied correctly.
  4. Missing Glyph: Changed barWidget.setGlyph(nil) to barWidget.setGlyph("") so the glyph is cleared correctly without silently triggering errors.
  5. Update Interval: Increased the polling interval in widget.luau from 500 ms to 1000 ms to match the repository's performance guidelines.
  6. Hardcoded UI Strings: Moved all layout names (Tile, Monocle, etc.) into en.json and now apply them through noctalia.translate() inside panel.luau.
  7. README: Corrected the show_text documentation to indicate that the default value is false.

If everything looks good now, let me know if there's anything else needed for approval!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants