1. Installation
On Windows and macOS you'll likely have to trust the download manually to run it for the first time. Mallard's built-in updater takes care of subsequent versions for you.
- macOS:
xattr -dr com.apple.quarantine /Applications/Mallard.app - Windows:
Unblock-File .\Mallard_0.23.0_x64-setup.exein PowerShell, or click "More info" → "Run anyway" on the SmartScreen prompt. - Linux: no trust dance.
chmod +xthe AppImage and run it, or — for automatic updates — add the Flatpak repo once and install from it:
Updates then arrive viaflatpak remote-add --if-not-exists mallard https://dl.vnsf.xyz/mallard.flatpakrepo flatpak install mallard net.mallard.app flatpak run net.mallard.appflatpak updateand your desktop's software centre. The standaloneMallard.flatpakbundle still installs withflatpak install --user ./Mallard.flatpak, but a bundle install has no remote to update from, so prefer the repo above.
2. Getting started
On first launch there are no worlds. Click + in the left tab strip to open the Manage worlds modal, then either pick a curated world or choose Add custom world….
A world has three setting blocks:
- Connection —
name,host,port,TLS,auto-connect, and the optionalcommand separator(see below). - Authentication — toggle auto-login to reveal
username/passwordfields. Credentials are written to the OS keychain, not to the world file. Optional regex fields (login_user_regex,login_pass_regex) tell Mallard when to send each value. - Display & behaviour — per-world overrides for theme, font, line height, wrap mode, ANSI palette, charset, and local echo.
Save, then either click Connect in the connection bar or hit ⌘+⇧+C.
2.1 The Welcome panel
The first time you open a world, Mallard opens a Welcome panel with a getting-started checklist — nine steps that walk you through the client: connect to the world, open the command palette, browse the plugin marketplace, install a plugin, visit the Aliases and Triggers tabs, set up your first alias and your first trigger, and a Coming from MUSHclient? row. Each row deep-links straight to the place it describes, and items tick off automatically as you complete them. When you're done — or if you'd rather skip it — dismiss the panel and it stays closed for good.
2.2 Tips as you explore
Beyond the checklist, Mallard points out the less-obvious bits the first time you reach them, and every tip shows only once:
- Contextual banners introduce each surface as you open it for the first time — Triggers, Aliases, Schedules, the Inspector, and Variables — and explain things like pattern modes (Exact / Regex / full-line), history search, and keymap stacking.
- Toast tips are top-center nudges for moments that benefit from one: a plugin panel first appearing in the tray, docking it, and closing it; the first clickable line in game output; building your first multi-line (chain) trigger; and, once you've installed your first plugin, a reminder of the plugin command prefix. Tip toasts carry a lightbulb and linger for 30 seconds so there's time to read them.
3. Key bindings
All shortcuts use ⌘ on macOS and Ctrl on Windows / Linux interchangeably; ⇧ means Shift.
Global
| Combo | Action |
|---|---|
| ⌘+K | Open the command palette. The palette lists every action — connect, disconnect, switch world, open the Triggers/Aliases/Timers browser, edit settings, etc. Type to filter, ⏎ to run. |
| ⌘+/ or F2 | Show the full keyboard-shortcuts cheatsheet. F2 is a modifier-free, layout-independent alias — handy on keyboards where "/" hides behind Shift or AltGr. |
| ⌘+⇧+F | Search scrollback for the active world (opens the search modal). |
| ⌘+F | Find in output — an in-place, incremental find bar over the output pane, with a live match count and case / regex toggles. |
| ⌘+⇧+I | Toggle the inspector panel. |
| ⌘+⇧+O | Focus the Output panel. |
| ⌘+⇧+B | Jump the Output pane to the bottom (live tail). |
| ⌘+⇧+J | Jump to a clickable target in Output — type the shown hint label to activate it. |
| Esc | Return focus to the main input. |
Connection & tabs
| Combo | Action |
|---|---|
| ⌘+⇧+C | Connect the active world. |
| ⌘+⇧+D | Disconnect the active world. |
| ⌘+1 … ⌘+9 | Jump to tab n. |
| Ctrl+Tab / Ctrl+⇧+Tab | Next / previous tab (every platform). On macOS, ⌘+⌥+↑ / ⌘+⌥+↓ also cycles the vertical tab strip. |
| ⌘+W | Close active tab. |
Input pane
| Combo | Action |
|---|---|
| ⏎ | Send the input. |
| ⇧+⏎ | Insert a newline. |
| ↑ / ↓ | Walk command history. |
| Tab / ⇧+Tab | Cycle autocomplete candidates for the current word. |
| Ctrl+R | Reverse incremental search through command history. |
| Ctrl+S | Forward step through i-search matches. |
Plugins can bind additional shortcuts per world; the active world's keymap is visible in the Plugins modal.
4. Input
4.1 Multi-line input
The input is a textarea that grows up to six rows. ⏎ submits the buffer; ⇧+⏎ inserts a literal newline. When the buffer contains newlines, Mallard sends one command per line, in order.
A buffer starting with a space ( look) is sent but not persisted to command history — useful for one-off commands you don't want bound to ↑.
4.2 Command separator (character splitter)
Manage worlds → Connection → Command separator defines a single-character splitter, e.g. ;. When set, the splitter is rewritten to a newline before the buffer is sent, so a single line like:
get sword;wield sword;say ready
becomes three separate commands. Leave the field empty to disable; multi-line input via ⇧+⏎ works regardless.
The splitter is purely a frontend rewrite — it happens once, before sending, so a literal ; sent over the wire still requires either disabling the splitter or escaping it from the source MUD's perspective.
4.3 Autocomplete
Every word you send is ingested into a per-world vocabulary. As you type, the suffix of the candidate appears as a ghost; Tab commits it and cycles to the next candidate, ⇧+Tab cycles backward, Esc reverts the cycle.
5. Commands
Most of what you type in the input goes straight to the MUD. A line that begins with the command prefix (a single character, default /) is instead handled by Mallard as a command. There are two kinds: the built-in commands covered below, and the client commands that plugins register. Both share the same dispatch — Mallard strips the prefix, matches the first word against the command table, and hands the rest to the command as its arguments — and both are surfaced together by /commands.
The prefix character is configurable in Settings → General → Command prefix; the literal / is always accepted as a fallback even after you change it, so instructions that say /vault keep working whatever your prefix.
5.1 Built-in commands
Mallard ships a family of built-in commands that let you drive settings and manage the client entirely from the input line — no GUI required, and they work while disconnected. They're registered by the host before any plugin loads, honour the configured prefix (with the literal / always accepted as a fallback), and show up in the Built-in section of /commands.
| Command | Shortcut | Usage |
|---|---|---|
/commands | /c | List all client commands — built-in and plugin-provided (see below). |
/global | /g | global <list|get|set|reset> [key] [value] — view and edit app-wide settings. |
/world | /w | world <list|get|set|reset|connect|disconnect|remove> … — view and edit the active world's settings, and connect/disconnect/delete it. |
/plugin | /p | plugin <list|info|install|uninstall|enable|disable|update|get|set|reset> … — manage plugins and their per-world settings. |
/keymap | /k | keymap <list|active|activate|deactivate|toggle> [name] — manage keymap configs and the active stack. |
/layout | /l | layout <list|save|activate|rename|delete|export> [name] — manage saved layouts for the active world. |
/rules | /r | rules <list|enable|disable|delete|var> … — manage triggers, aliases and variables. |
Verbs and keys. The settings domains all share the same shape: list shows everything in the domain, and get / set / reset operate on a single named key (/global set command-prefix !, /world get charset). Run a domain bare (/global) or with an unknown verb to get its help screen. Typing a command in the input pane offers in-pane tab completion for verbs, keys, and item names — multi-word item names can be quoted — and commands share the input's ↑/↓ history.
Two-stage confirmation. Destructive verbs — like /world remove, /plugin uninstall, and the permission-preview step of /plugin install — don't act immediately. They arm a pending action and wait; complete it with /confirm (shortcut /y) or abandon it with /cancel (shortcut /n). Pass --force / -f to a destructive command to skip the prompt. /confirm and /cancel are hidden from the /commands listing — they're only meaningful right after arming — but you can still inspect them with /commands confirm.
Shortcuts are reserved. Every built-in has a unique single-letter shortcut, and both the full names and those letters are reserved: a plugin whose mud.command tries to register one of them is rejected on the standard first-wins path (it surfaces as owned by Mallard in the plugin's error pane), so a built-in name always resolves to Mallard. When the active prefix isn't /, the shortcuts and the / fallback both keep working — a command's detail card spells out the exact forms.
Built-in command output shares one semantic colour palette with /commands — command literals in cyan-bold, shortcuts and keys in cyan, key = value values bold, section headers bold, hints and footers dimmed, and destructive markers in red/yellow. The colours are ANSI-palette roles, so they resolve through whatever theme you're running and stay readable in both light and dark.
5.2 Discovering what's available: /commands
The built-in /commands command lists every command currently bound in the active world — both Mallard's own built-in commands and the client commands registered by every installed-and-enabled plugin. It's available out of the box — no plugin required, and it works even while disconnected — and is the canonical way to find out what's currently bound.
The listing has three levels:
- Top level — type
/commandson its own and you get a grouped index: a Built-in section first, then one heading per plugin with that plugin's command names underneath. Each row shows the command's single-letter shortcut in its own column. A filter box at the top narrows by command name or plugin id. - Per-plugin drill-down — type
/commands <plugin>(e.g./commands com.example.bank) to see just that plugin's commands with their one-line summaries. The plugin name in the top-level listing is also clickable as a shortcut. - Per-command usage — type
/commands <name>(e.g./commands vault) to see the full detail card for a single command: description, usage/argument shape, its shortcut and any aliases, and its source — the owning plugin, or Mallard (built-in). For a settings-domain built-in the card also breaks down its verbs, with destructive ones flagged. You can also reach a card by its shortcut (/commands gshows the/globalcard). Click a command name in either of the higher-level listings to land here.
For plugin commands, the description, args, and examples shown here are whatever the plugin author documented in their command registration; an undocumented command still shows up in the index but with an empty usage block.
6. Incremental command search
Press Ctrl+R in the input pane. An overlay shows:
(i-search 'que'): quench torch
The semantics mirror bash / readline:
- Typing extends the query and live-refines the match. Matching is case-insensitive.
- Ctrl+R again steps to the next-older match for the same query.
- Ctrl+S steps forward (to a newer match).
- When no match exists for the query, the prompt becomes
(failing i-search '…'). - ⏎ accepts the current match into the textarea and exits i-search.
- Esc cancels and restores the buffer you had before entering i-search.
7. Triggers
Triggers, aliases, and timers are configured per-world. Open the engine browser from the command palette (⌘+K → Open Triggers/Aliases/Timers panel), pick the Triggers tab, then + New.
A trigger has a kind (line — fired against incoming text; gmcp — fired on a GMCP event; lifecycle — fired on connect/disconnect), a pattern, a list of actions, optional tags, a priority, and an optional fires remaining count for one-shot triggers.
7.1 Pattern modes
Line- and GMCP-string triggers (and aliases) can use one of three modes. The mode selector is at the top of the editor.
| Mode | Semantics | Example |
|---|---|---|
| Exact | Substring literal. Matches anywhere in the line. {name} placeholders expand into named captures. |
You loot {item} from the corpse |
| Exact (full line) | Same placeholder syntax, but the pattern must match the entire line (anchored ^…$). This is the default mode for aliases. |
{verb} {target} |
| Regex | Standard regex. Named groups (?P<name>…) appear as captures; available flags are i (case-insensitive), m, s, x. |
^You (?P<dir>walk|run) (?P<way>\w+)\.$ |
7.2 Pattern args & placeholders
In the two Exact modes, {name} is a pattern arg: it captures one greedy run of text from the matched line and binds it to name. A {name=default} form gives a default that is used when the placeholder doesn't match (and whitespace immediately before such an optional placeholder is folded in, so the rest of the pattern still matches).
Pattern (Exact): give {item} to {npc}
Input line: give gold to guard
Captures: item="gold", npc="guard"
Action templates use ${path} interpolation. The following paths resolve:
| Path | Resolves to |
|---|---|
${arg.name} | The named pattern arg from an Exact-mode trigger/alias, falling back to the {name=default} default if declared. |
${match.0} … ${match.9} | Positional regex captures; match.0 is the full match. Use this with Regex-mode patterns. |
${var.name} | A per-world user variable (set via the SetVar action). |
${gmcp.dotted.path} | An auto-mirrored GMCP value (read-only). |
${world.host}, ${world.port}, ${world.name}, ${world.character} | World metadata. |
Unresolved paths render as the literal ${path} and produce a warning in the log. Interpolation runs once, before any Send action splits its template on newlines, so a multi-line Send with placeholders sends one fully-interpolated command per line.
The editor includes a Pattern tester at the bottom: paste a sample line and click Test to see the captures dictionary, or a parse error if the pattern is malformed.
7.3 Actions
Each trigger runs an ordered list of actions. Add them with the + buttons under the Actions fieldset.
| Action | Effect |
|---|---|
| Send | Send one or more commands to the MUD (one per line, after interpolation). |
| SetVar | Set a per-world user variable. Empty value deletes. |
| Gag | Suppress the matched line from the output pane. |
| Highlight | Restyle a span of the matched line (target by capture name, regex group, or literal). Line triggers only. |
| Note | Insert a styled note line into the output. Style is configurable via the inline picker. |
| RunAlias | Invoke another named alias. |
| SendRaw | Send raw bytes (base64-encoded). Useful for out-of-band telnet sequences. |
| Notify | OS notification with title, body, and icon (info / warning / error). |
| PlaySound | Play a file from the sounds folder at a given volume. Files are managed in Settings → Sound → Reveal sounds folder. |
| StopSounds | Stop currently-playing sounds, optionally filtered by tag. |
| Speak | Read text aloud through the host text-to-speech engine, with optional per-utterance voice, rate, pitch, volume, and a channel for queueing/interruption. Defaults come from Settings → Speech; a no-op when speech is disabled there. |
| StopSpeech | Stop queued and active speech, optionally limited to a single channel. |
| StartTimer / StopTimer | Schedule a timer that runs its own action list at an interval. |
| SetEnabled | Toggle a named trigger, alias, or timer on or off. |
Aliases use the same pattern + action shape, but they fire against your input rather than incoming lines. By default an alias is Exact (full line), so gn matches the input gn and nothing else.
8. Settings
Open with the command palette (⌘+K → Open settings).
| Section | Controls |
|---|---|
| General | Command prefix — the single character that marks a line as a plugin client command (e.g. /vault). Must be a printable ASCII character that isn't a letter or digit; defaults to /. The literal / is always accepted as a fallback even after you change the prefix, so docs that say /vault keep working. |
| Theme | Pick an installed theme. Reload themes rescans the theme folder. |
| Display | Show system lines and Show echo lines. |
| Keymap | Define and switch between named keymap configs — useful for situational layouts (a travel numpad, a combat numpad, etc.). Each row of the editor binds one combo; modifier-bearing combos and F-keys fire regardless of focus, bare-key bindings fire outside text inputs. A visual numpad lets you bind Numpad0–Numpad9 (plus + / - / * / / / . / Enter) independently from the main-row digits. Plugins can drive which config is active, but the bindings themselves live here. |
| Sound | Master volume, mute, Pause sounds when window hidden, and a Reveal sounds folder button. Sound files dropped into that folder become available to the PlaySound trigger action. |
| Speech | Text-to-speech defaults for the Speak trigger/alias action and the plugin mud.speak API. A master Enable speech toggle (speak-enabled) governs everything — with it off, every utterance is dropped. Below it, a default Voice (speak-voice, chosen from the voices the OS speech engine exposes), plus default Rate and Pitch (speak-rate, speak-pitch; -1…1, 0 is normal) and Volume (speak-volume; 0…1). Per-call options passed to mud.speak override these defaults, but the master toggle always wins. |
| Updates | Current updater state and a manual Check for updates button. |
Anything that varies per world — TLS pin mode, charset, ANSI palette, font, font size, theme override, local echo, line height, wrap mode, log enabled, command separator — lives on the world itself, under Manage worlds → Display & behaviour.
Everything here can also be driven from the input line — no GUI required, and it works while disconnected — through the built-in commands /global, /world, /plugin, and /keymap.
9. Plugins & marketplace
Plugins are Lua scripts that extend Mallard — they can register triggers, aliases, timers, client commands, GMCP/MXP handlers, keymaps, settings, and custom dockable panels. The plugin manager and the marketplace live behind the same panel.
9.1 Plugin manager
Open it from the command palette: ⌘+K → Open Plugin Manager. The panel docks into your layout and has three tabs: Browse, Updates, and Installed.
The Installed tab lists every plugin currently on disk. Each row shows a source pill — marketplace, sideload, or dev — plus:
- Enabled here — toggles the plugin on/off for the active world. A plugin is installed globally but enabled per-world.
- Auto-update (marketplace plugins only) — when on, new versions are pulled in automatically.
- Permissions disclosure — what the plugin was granted (network, GMCP, keychain, notifications, etc.). You can revoke or restore defaults from here.
- Settings disclosure — present only if the plugin declares configurable settings. Settings are per-world.
When the list gets long, a filter bar above it keeps things scannable: a search box matches on name, description, author, or id; source chips narrow to marketplace, from-file, or dev plugins; state chips narrow to enabled, disabled, or errored; and a sort dropdown orders by name, source, or status. A live “N of M shown” count tracks the current filter, a Clear filters button resets everything, and a distinct empty state appears when nothing matches.
Header buttons:
- Install from file… — pick a
.mallardxarchive from disk. - Reload plugins — rescans the plugins folder without restarting the app.
- Reveal plugins folder — opens the folder in the OS file manager. Dropping a
.mallardxfile into that folder also installs it.
9.2 Marketplace
The Browse tab fetches a curated catalog over the network. Each card shows the plugin's name, description, tags, version, and an Install button (or an Installed badge if you already have it). Type into the search field to filter by name/description; click a tag pill to filter by category. Refresh re-fetches the catalog; an Updated … ago stamp shows the last sync.
Installing from the marketplace downloads the archive, verifies its minisign signature against the catalog, prompts you to confirm the requested permissions, and then unpacks and loads the plugin. Sideloaded plugins (installed from a local .mallardx) are not signature-verified and surface that fact in the permission dialog.
The Updates tab lists installed marketplace plugins that have a newer version available; a badge on the tab shows the count. Each row has an Update button; permissions are re-checked if the new version asks for anything new.
9.3 Trigger packs
A trigger pack (.mallardpack.toml) is separate from a plugin. Packs bundle data — your triggers, aliases, and timers — for sharing or moving between machines. Plugins bundle code.
Export and import live in the engine browser (the Triggers/Aliases/Timers panel), not in the plugin manager:
- Export… — pick rows by tag or individually, save to a
.mallardpack.toml. - Import trigger pack — point at a
.mallardpack.toml. The review dialog summarises new, will replace, unchanged, and ambiguous items, lets you pick a merge policy (apply all changes / add new only / add everything as new with auto-suffixed names), and optionally lets you decide row-by-row. The import is transactional — all items land or none do.
Packs are not signature-verified; treat them like config files you move between your own machines or share informally.
9.4 Client commands
Alongside Mallard's own built-in commands, a plugin can register a name-keyed client command — a prefixed input the user types into the regular input pane, like /vault deposit 100 or /cast heal self. Mallard strips the prefix, looks the name up in this world's per-plugin command table, and fires the plugin's callback with everything after the name as its arguments. Client commands dispatch before the regular regex alias pass, so a registered command always wins over an alias that would also match. Like every command, they honour the configurable command prefix, with the literal / always accepted as a fallback.
Commands surface in the plugin rules UI as their own row alongside the plugin's triggers and aliases, and obey the same enabled/disabled toggle. To see what a given world currently has bound, type /commands — the command directory lists plugin commands right alongside the built-ins.
If two plugins try to register the same command name, the first one in wins and the loser's registration is dropped with a warning to the Plugin Activity log — so a name in /commands always resolves to exactly one plugin. A plugin also can't claim a built-in's name or single-letter shortcut; those are reserved. Prefer descriptive, prefix-able names (vault-deposit over deposit) when authoring plugins to keep the namespace tidy.
10. Plugin tray
An installed plugin can register one or more panels — little iframed UIs that live in your dock layout (a vitals readout, a room map, an inventory grid, whatever the plugin author cooked up). The tray is the holding area for those panels: every panel that exists but isn't currently docked sits as a chip in the tray rail, ready to be slotted into the layout.
The rail only appears when at least one installed-and-enabled plugin in the active world has registered a panel. If you have plugins installed but no chips, check the plugin manager's Installed tab — the plugin may be disabled for this world, or it may not declare any panels at all.
10.1 Chips, states & the flyout
Each chip is a 28×28 square showing a two-letter initial of the panel's title (Vitals → Vi, X Ray → XR). Hover anywhere on the rail and after a brief delay a flyout slides out alongside it, listing each chip with its full panel title and the plugin it belongs to. Move off the rail to dismiss; click the pin icon at the top of the rail to keep the flyout open until you click it again. ⌘+⇧+R focuses the rail and pins it open in one motion, then ↑ / ↓ walk the rows and ⏎ docks the highlighted one.
A chip shows its state through colour:
| State | Appearance | Meaning |
|---|---|---|
| Idle | Plain background | Panel exists, lives in the tray, click to dock. |
| Docked | Accent-coloured border + text | The panel is currently somewhere in the dock layout. Clicking the chip focuses it; closing it from its tab's × returns it here. |
| New | Soft gold ring | You haven't opened this panel yet on this world. The ring clears the first time you dock it. |
| Disconnected | Faded | The world isn't connected. Chips still work, but the plugin runtime may have nothing to show until you connect. |
| Disabled | Faded, non-interactive | The plugin is disabled for this world. Left-click does nothing; right-click to enable it. |
10.2 Docking from the tray
A plain left-click on a chip drops the panel into the active group of the current world's dock layout (or creates a sensible default position if there's no active group). Right-click to choose where it lands instead:
| Item | Effect |
|---|---|
| Dock here | Same as left-click — into the active group. |
| Dock to right / left / above / below | Split off a new group from the active panel in the requested direction. |
| Close (return to tray) | Shown only when the chip is already docked. Removes the panel from the layout; the chip stays in the tray. |
| Configure {plugin}… | Opens the plugin manager's Installed tab with this plugin's settings disclosure expanded. Same destination as the Configure… entry on the panel's tab context menu. |
| Enable / Disable {plugin} | Toggles the plugin for the active world. Disabling here is equivalent to flipping the Enabled here toggle in the plugin manager. |
Closing a docked panel — whether from its tab's ×, from the tab context menu's Close, or from the chip's right-click menu — never deletes anything. The panel returns to the tray as an idle chip; its layout position will be re-used the next time you dock it within the same session.
11. Managing the layout
Each world has its own dock layout. Layouts persist per-world — close the app, reopen it, switch tabs, and the panel arrangement comes back as you left it.
11.1 Sliding dividers
Every boundary between panels is a draggable splitter. Hover the seam, the cursor flips to a resize arrow, then click-and-drag to resize. Splitters between groups stacked vertically resize heights; splitters between side-by-side groups resize widths. The new sizes are recorded into the world's layout file as soon as you let go, so they survive restarts without any explicit save step.
11.2 Right-clicking tab titles
Each panel's tab title is right-clickable; the context menu is the primary way to rearrange the layout without dragging. The menu shows:
| Item | Effect |
|---|---|
| Pin / Unpin group size | Lock or unlock the current group's width and height — see below. |
| Move to left of {other panel} | Submenu listing every other panel in the world. Picking one drops this panel into a new group immediately left of the chosen panel's group. |
| Move to right of {other panel} | Same idea, to the right. |
| Move above {other panel} | New group above the chosen panel. |
| Move below {other panel} | New group below. |
| Stack with {other panel} | Joins the chosen panel's group as another tab inside it. |
| Move to new column | Splits this panel off into a fresh group to the right of its current one. Handy when a panel is stacked with others and you want to pop it out without picking a specific neighbour. |
| Close | Removes the panel from the layout. Plugin panels return to the tray; built-in panels (Output, Inspector, Engine browser, Variables, Plugins) can be re-opened from the command palette. |
| Configure {plugin}… | Appears only on plugin-panel tabs whose plugin declares settings. Routes to the plugin manager's Installed tab with the settings disclosure open. |
If a panel is the only one in the world, the directional and stack submenus are present but disabled — there's no other panel to anchor against.
11.3 Pinning a group's size
Pin group size freezes the current width and height of a group, so subsequent changes elsewhere in the layout (resizing other splitters, opening or closing panels, re-docking from the tray) don't push the pinned group around. Useful for sidebar-shaped panels — a vitals readout you want to stay exactly 240 px wide regardless of what happens to its neighbours.
Every group's header carries a small pin glyph in the top right — click it to pin or unpin, or use Pin group size / Unpin group size in the tab right-click menu.
Pinning cuts both ways: a pinned group will also refuse resizes from other directions if they would change its size. Drag a splitter that would push or shrink a pinned neighbour and the splitter simply won't move. If you're mid-rearrangement and dividers feel stuck, the easiest path is to unpin everything, get the layout sorted, then re-pin the groups you want held in place.