Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions mangowm-status/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Mangowm Status

A [Noctalia](https://noctalia.dev) v5 bar widget that displays the current [Mangowm](https://github.com/mangowm/mangowm) tiling layout and keymode — click to open a layout picker, scroll to cycle layouts.

![Thumbnail](thumbnail.webp)

## Plugin

| Field | Value |
| --- | --- |
| ID | `x0d7x/mangowm-status` |
| Entries | Bar widget: `status`; panel: `layout-picker` |

## Screenshots

| Widget (bar) | Layout picker panel |
| --- | --- |
| ![Widget showing Tile layout](screenshots/widget.webp) | ![Layout picker with all layouts](screenshots/panel.webp) |

## Features

- **Live layout badge** — shows the active layout glyph and letter code on your bar
- **Keymode indicator** — displays non-default keymodes (resize, move, etc.) beside the layout
- **Click to pick** — left-click opens a scrollable panel of all 14 supported layouts
- **Scroll to cycle** — horizontal scroll cycles through core layouts
- **Live updates** — uses `mmsg watch` streams, no polling
- **14 layouts** — all Mangowm tiling layouts supported

## Install

```sh
noctalia plugin install x0d7x/mangowm-status
```

Or clone manually into `~/.local/share/noctalia/plugins/mangowm-status/`:

```sh
git clone https://github.com/x0d7x/mangowm-status.git \
~/.local/share/noctalia/plugins/mangowm-status
```

## Requirements

- Noctalia v5
- [`mmsg`](https://github.com/mangowm/mango) — Mangowm's IPC CLI (install with `cargo install mmsg` or grab a release from the [releases page](https://github.com/mangowm/mango/releases)); must be on `PATH`

## Requirements

- Noctalia v5
- [`mmsg`](https://github.com/mangowm/mango) — Mangowm's IPC CLI (install with `cargo install mmsg` or grab a release from the [releases page](https://github.com/mangowm/mango/releases)); must be on `PATH`

## Usage

### Bar widget

Add the **Mangowm Status** widget from the Add-widget picker in your bar settings, or configure it by hand:

```toml
[widget.mangowm-status]
type = "x0d7x/mangowm-status:status"
```

The widget shows the current layout's icon and letter code. When the active keymode is not `default`, the keymode name appears beside the layout code.

![Widget screenshot](screenshots/widget.webp)

- **Left-click** → opens the **Layout Picker** panel. Click any layout to switch.
- **Horizontal scroll** → cycles through core layouts (T → S → G → …).

### Layout picker panel

Open programmatically with:

```sh
noctalia msg panel-toggle x0d7x/mangowm-status:layout-picker
```

A scrollable list of all 14 layouts. The current layout is highlighted. Click any other layout to switch — the panel closes automatically.

![Panel screenshot](screenshots/panel.webp)

## Settings

| Setting | Type | Default | Description |
| --- | --- | --- | --- |
| Hide on default | `bool` | `false` | Hide the widget when keymode is `default` |
| Show text | `bool` | `true` | Show the keymode name beside the layout glyph |
| Notify on change | `bool` | `true` | Show a notification when the keymode changes |

## IPC

Control the widget from the terminal:

```sh
# Refresh layout data
noctalia msg plugin x0d7x/mangowm-status:status focused refresh

# Get current status (logged as JSON)
noctalia msg plugin x0d7x/mangowm-status:status focused get-status

# Set layout by code
noctalia msg plugin x0d7x/mangowm-status:status focused set-layout S
```

## Layout Codes

| Code | Layout | Icon |
|------|--------|------|
| T | Tile | columns |
| S | Scroller | menu |
| G | Grid | dashboard |
| M | Monocle | window |
| K | Deck | cards |
| CT | Center Tile | layout |
| RT | Right Tile | columns |
| DW | Dwindle | columns |
| F | Fair | layout |
| VS | Vertical Scroller | menu |
| VT | Vertical Tile | columns |
| VG | Vertical Grid | dashboard |
| VK | Vertical Deck | cards |
| VF | Vertical Fair | layout |

## Notes

- This plugin spawns `mmsg` subprocesses to query and change the compositor state. `mmsg` must be installed and on `PATH`.
- Layout changes use `mmsg dispatch setlayout,<name>` (lowercase layout name, not the short code).
- The widget keeps two long-running `mmsg watch` streams (keymode + all-tags) for live updates, with a low-rate (5 s) polling fallback.
- The panel entry fetches tag data on open and does not keep a persistent connection.

## License

MIT
139 changes: 139 additions & 0 deletions mangowm-status/panel.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
--!nonstrict
-- Mangowm Status — Layout Picker Panel
-- Shows all available tiling layouts; click one to switch immediately.

-- Layout manifest: glyph + letter + name (same as widget.luau)
local layoutConfig = {
T = { glyph = "columns", letter = "T", name = "tile" },
S = { glyph = "menu", letter = "S", name = "scroller" },
G = { glyph = "dashboard", letter = "G", name = "grid" },
M = { glyph = "window", letter = "M", name = "monocle" },
K = { glyph = "cards", letter = "K", name = "deck" },
CT = { glyph = "layout", letter = "C", name = "center_tile" },
RT = { glyph = "columns", letter = "R", name = "right_tile" },
VS = { glyph = "menu", letter = "V", name = "vertical_scroller" },
VT = { glyph = "columns", letter = "V", name = "vertical_tile" },
VG = { glyph = "dashboard", letter = "G", name = "vertical_grid" },
VK = { glyph = "cards", letter = "K", name = "vertical_deck" },
DW = { glyph = "columns", letter = "D", name = "dwindle" },
F = { glyph = "layout", letter = "F", name = "fair" },
VF = { glyph = "layout", letter = "F", name = "vertical_fair" },
}

-- Layout codes in display order
local layoutOrder = { "T", "S", "G", "M", "K", "CT", "RT", "DW", "F", "VS", "VT", "VG", "VK", "VF" }

local currentLayoutCode = "T"
local switching = false

-- Forward-declared so setLayout's failure path can re-render the panel
-- (doRender is defined after setLayout below).
local doRender

-- Helper to switch layout and close panel
local function setLayout(code)
if switching or code == currentLayoutCode then return end
switching = true

local cfg = layoutConfig[code]
local name = cfg and cfg.name or "tile"
noctalia.runAsync("mmsg dispatch setlayout," .. name, function(result)
switching = false
if result.exitCode == 0 then
currentLayoutCode = code
panel.close()
else
noctalia.notifyError("Mangowm Status", "Failed to set layout: " .. (result.stderr or "unknown error"))
doRender()
end
end)
end

-- Named global functions for each layout button (onClick doesn't pass arguments)
function onLayoutT() setLayout("T") end
function onLayoutS() setLayout("S") end
function onLayoutG() setLayout("G") end
function onLayoutM() setLayout("M") end
function onLayoutK() setLayout("K") end
function onLayoutCT() setLayout("CT") end
function onLayoutRT() setLayout("RT") end
function onLayoutDW() setLayout("DW") end
function onLayoutF() setLayout("F") end
function onLayoutVS() setLayout("VS") end
function onLayoutVT() setLayout("VT") end
function onLayoutVG() setLayout("VG") end
function onLayoutVK() setLayout("VK") end
function onLayoutVF() setLayout("VF") end

function doRender()
-- Map layout code -> onClick handler name
local onClickHandler = {
T = "onLayoutT", S = "onLayoutS", G = "onLayoutG",
M = "onLayoutM", K = "onLayoutK", CT = "onLayoutCT",
RT = "onLayoutRT", DW = "onLayoutDW", F = "onLayoutF",
VS = "onLayoutVS", VT = "onLayoutVT", VG = "onLayoutVG",
VK = "onLayoutVK", VF = "onLayoutVF",
}

-- Current layout indicator (compact, shown at top)
local currentCfg = layoutConfig[currentLayoutCode] or layoutConfig["T"]
local currentBadge = ui.row({ gap = 6, align = "center", fill = "primary/0.12", radius = 6, paddingH = 10, paddingV = 5 }, {
ui.label({ text = "Now:", color = "on_surface_variant", fontSize = 11 }),
ui.glyph({ name = currentCfg.glyph, size = 12, color = "primary" }),
ui.label({ text = currentCfg.letter .. " — " .. currentCfg.name, color = "primary", fontWeight = "bold", fontSize = 11 }),
})

-- Build layout buttons
local buttons = {}
for _, code in ipairs(layoutOrder) do
local cfg = layoutConfig[code]
if cfg then
local isActive = code == currentLayoutCode
table.insert(buttons, ui.button({
key = "layout-" .. code,
glyph = cfg.glyph,
glyphSize = 14,
text = " " .. cfg.letter .. " — " .. cfg.name,
contentAlign = "start",
variant = isActive and "primary" or "ghost",
controlSize = "sm",
onClick = onClickHandler[code],
}))
end
end

panel.render(ui.column({ flexGrow = 1, gap = 2, padding = 8 }, {
currentBadge,
ui.separator({ thickness = 1, color = "outline/0.15", spacing = 3 }),
ui.scroll({ flexGrow = 1, gap = 2 }, buttons),
}))
end

-- Fetch current layout from mmsg, then render
local function refreshCurrentLayout()
noctalia.runAsync("mmsg get all-tags", function(result)
if result.exitCode == 0 and result.stdout then
local ok, data = pcall(noctalia.json.decode, result.stdout)
if ok and data and data.all_tags then
for _, monitorEntry in ipairs(data.all_tags) do
if type(monitorEntry) == "table" and monitorEntry.tags then
for _, tag in ipairs(monitorEntry.tags) do
if type(tag) == "table" and tag.is_active and tag.layout then
currentLayoutCode = tag.layout
doRender()
return
end
end
end
end
end
end
-- Render even if fetch failed (uses fallback currentLayoutCode)
doRender()
end)
end

function onOpen(_context)
-- Fetch current layout first, then render (avoids flash of wrong layout)
refreshCurrentLayout()
end
44 changes: 44 additions & 0 deletions mangowm-status/plugin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
id = "x0d7x/mangowm-status"
name = "Mangowm Status"
version = "1.1.0"
plugin_api = 4
author = "x0d7x"
license = "MIT"
dependencies = ["mmsg"]
icon = "grid-4x4"
description = "Shows current tiling layout and keymode for Mangowm"
tags = ["bar", "mangowc"]

[[widget]]
id = "status"
entry = "widget.luau"

[[widget.setting]]
key = "hide_on_default"
type = "bool"
label_key = "settings.hide_on_default.label"
description_key = "settings.hide_on_default.description"
default = false

[[widget.setting]]
key = "show_text"
type = "bool"
label_key = "settings.show_text.label"
description_key = "settings.show_text.description"
default = true

[[widget.setting]]
key = "notify_change"
type = "bool"
label_key = "settings.notify_change.label"
description_key = "settings.notify_change.description"
default = true

# Layout picker panel — opens on click, shows all layouts to choose from
[[panel]]
id = "layout-picker"
entry = "panel.luau"
width = 280
height = 400
placement = "floating"
position = "auto"
Binary file added mangowm-status/thumbnail.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions mangowm-status/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"plugin": {
"description": "Shows current tiling layout and keymode for Mangowm"
},
"settings": {
"hide_on_default": {
"label": "Hide on default",
"description": "Hide the widget when keymode is default"
},
"show_text": {
"label": "Show text",
"description": "Show the keymode name beside the glyph"
},
"notify_change": {
"label": "Notify on change",
"description": "Show a notification when keymode changes"
}
}
}
Loading