Skip to content

Conversation

@argosnothing
Copy link

@argosnothing argosnothing commented Dec 7, 2025

Hidden Workspaces

Justification

this thread proposes several implementations of a niri-scratchpad system. These implementations tend to rely on a declared stash workspace. Currently these workspaces are visible and treated by the compositor like any other workspace. My proposol is to introduce a flag to workspaces through the Workspace { hide true } option that effectively hides that workspace in the overview, drawn windows inside the overview, and socket.

In addition, there are workflows that will benefit from being able to dynamically "hide" a workspace in this fashion, consider a workflow in which you have a workspace you dedicate to social media, or any category that you have no reason to navigate to or see information from, you could add windows to that workspace, "hide" it, and then bring it back later when you actually need to use it in the same session. ( #2997 (comment) )

Implementation

Here is what I did:

  • Added: hidden option to workspace config
  • Added: ToggleWorkspaceVisibility to binds
  • On workspaces i've added both hidden and original_idx fields
  • Changes, mainly in monitor.rs for updates on how to handle idx with hidden-aware behavior.

Justification for Implementation

My goal is to try and touch as little of the workspace logic as possible, with minimal additions to the code. Having a nullable idx, for example would require substantial changes to everything that works with workspaces to handle variants.

Update in workspace logic.

  • If a workspace is hidden, we do not want it to be shown on bars or anything consuming niri workspace state, they should be effectively hidden from tools consuming that state unless explicitly requested, ( through niri msg workspaces-with-hidden ). I have omitted hidden workspaces from the event stream, but keep them in the state for workspaces-with-hidden.
  • If a workspace is hidden, we do not want it to present any gaps in workspaces that are visible, therefor we need to maintain that all hidden workspaces are at idx past any visible workspace. This means i've had to adjust several spots that deal with adding workspaces to bottom, and cleaning up workspace code.
  • A workspace, on hide should maintain it's original idx before being hidden, so it can "return" to said idx on unhide.
  • Hidden option for workspace in config.
  • Hidden workspaces are not viewable in overview
  • Hidden workspaces are not navigable to through up and down bindings
  • Hidden workspaces do not display in ext/workspaces
  • Hidden workspaces do not show through IPC ( partial, needs rewrite to maintain compatibility with code using ipc_crate )
  • Hidden workspaces id management. When hidden a hidden workspace should not cause skips in nonhidden workspace ids.
  • Hidden workspaces remember their last position when unhidden.
  • Hidden workspaces can be toggled through a binding. partial d5f4772#diff-608918babe5a4eb5db908e910f16f33a173c6821eb12ef6e1077bf277160da76
  • Hidden workspace windows do not display in recent windows, including All
  • Clean up dead code added when experimenting with alternative IPC
  • Edge case: monitor with only empty workspace, but a hidden stash workspace, shows a empty workspace below when it shouldn't.
  • Crash on config change that adds hidden true to a named workspace.
  • Navigating to a workspace by Idx still allows you to reach, and then interact with a hidden workspace ( even though it's windows are still hidden ). Behavior should be the same as navigating to a workspace that's higher than the last visible workspace instead, meaning if it's past the last visible workspace, it should instead snap you to the last visible workspace.
  • Handle Unset workspace name, and workspaces that have names. On unset workspace name, the workspace needs to lose any hidden it has.
  • Binds that change focus to a workspace through that workspaces name should unhide the workspace while in their view.
  • Workspaces revealed through go to workspace [name] should rehide on leaving the workspace.
  • Monitor disconnect/reconnect, hidden workspaces becoming visible/undefined behavior
  • Hidden workspaces #2997 (comment)

Config add

workspace "stash" {
    hidden true
}

Other

If anything I would just like to use this to start a dialogue about how we could go about implementing hidden workspaces. I believe there are many different ways to go about a scratchpad implementation, and it's understandable that it will take some time. In the meantime I think having more flexibility for how the user regards workspaces can only be a good thing and allow third party implementations to act as a temporary stopgap.

@Fireye04
Copy link
Contributor

Fireye04 commented Dec 7, 2025

Glad to see some progress here! Looking ahead at indexing implementation, I think negative numbers would make sense in this context? Especially as these hidden workspaces would be un-switchable.

@argosnothing
Copy link
Author

argosnothing commented Dec 7, 2025

Glad to see some progress here! Looking ahead at indexing implementation, I think negative numbers would make sense in this context? Especially as these hidden workspaces would be un-switchable.

that was my thought, but i think WorkspaceId wraps a u64

@Fireye04
Copy link
Contributor

Fireye04 commented Dec 8, 2025

Hmm yeah if that's a rigid implementation then that seems like an annoying workaround. In theory, you could wrap around and index backwards from 0xFFFFFFFFFFFFFFFF, then find a way to discount hidden workspaces from being detected & interpolated between.

I'd say a swap to i64 would be theoretically cleaner but that's definitely something for YaLTeR to comment on.

@argosnothing
Copy link
Author

argosnothing commented Dec 8, 2025

Hmm yeah if that's a rigid implementation then that seems like an annoying workaround. In theory, you could wrap around and index backwards from 0xFFFFFFFFFFFFFFFF, then find a way to discount hidden workspaces from being detected & interpolated between.

I'd say a swap to i64 would be theoretically cleaner but that's definitely something for YaLTeR to comment on.

That's not a bad method. If we force the hidden workspaces at the end of u64 we shouldn't need to worry about interpolation. But ill have to play with that.

As for larger changes like changing types on workspace id. Yeah, my hope is to step on as few as toes as possible with this, haha.

@markstos
Copy link
Contributor

markstos commented Dec 8, 2025

Every workspace is that's not the active one is already hidden (not visible) outside of the overview, while the overview is useful for reviewing what can't normally be seen. What's the benefit completely hiding workspaces even from the overview? I don't quite see the use over a normal workspace named "stash" that you perhaps treat differently from others using different keybindings for it.

@argosnothing
Copy link
Author

argosnothing commented Dec 8, 2025

Every workspace is that's not the active one is already hidden (not visible) outside of the overview, while the overview is useful for reviewing what can't normally be seen. What's the benefit completely hiding workspaces even from the overview? I don't quite see the use over a normal workspace named "stash" that you perhaps treat differently from others using different keybindings for it.

It declutters the interface for those using those stash workspaces. It's a concession every implementation of scratchpad has to make because niri has no hidden workspace. Workspaces that are "hidden" by virtue of not being in the current view can still be erroneously navigated to and also display through different protocols. Those using the workspace in this way will never need to see the workspace their scratchpads go to.

@danneu
Copy link

danneu commented Dec 9, 2025

Every workspace is that's not the active one is already hidden (not visible) outside of the overview, while the overview is useful for reviewing what can't normally be seen. What's the benefit completely hiding workspaces even from the overview? I don't quite see the use over a normal workspace named "stash" that you perhaps treat differently from others using different keybindings for it.

The scratchpad use-case might be too incidental to be the main selling point of this feature.

But I have a direct use-case. I work on a large project made up of subprojects that I open at the start of my work day.

  • I open a workspace for each subproject I'm working on (each one with its own editor, terminal, browser, etc), but in any 20min block, I only need a subset of workspaces visible.
  • I can't close the ones I don't need because they're stateful from when I used them 20min ago, I'll need them again in the near future, and they may be running necessary processes (like a dev server) in the terminal foreground.
  • Having all eight workspaces open is mentally taxing and has me constantly navigating past workspaces I don't need at the moment.
    • The niri/workspaces waybar widget gets so long that it crowds out the rest of my waybar
    • The workspaces also kind of look the same in the overview (full width editor next to a browser) so I get lost.

Another potential solution would be some sort of virtual output that holds workspaces offscreen.

@argosnothing
Copy link
Author

argosnothing commented Dec 9, 2025

Every workspace is that's not the active one is already hidden (not visible) outside of the overview, while the overview is useful for reviewing what can't normally be seen. What's the benefit completely hiding workspaces even from the overview? I don't quite see the use over a normal workspace named "stash" that you perhaps treat differently from others using different keybindings for it.

The scratchpad use-case might be too incidental to be the main selling point of this feature.

But I have a direct use-case. I work on a large project made up of subprojects that I open at the start of my work day.

* I open a workspace for each subproject I'm working on (each one with its own editor, terminal, browser, etc), but in any 20min block, I only need a subset of workspaces visible.

* I can't close the ones I don't need because they're stateful from when I used them 20min ago, I'll need them again in the near future, and they may be running necessary processes (like a dev server) in the terminal foreground.

* Having all eight workspaces open is mentally taxing and has me constantly navigating past workspaces I don't need at the moment.
  
  * The niri/workspaces waybar widget gets so long that it crowds out the rest of my waybar
  * The workspaces also kind of look the same in the overview (full width editor next to a browser) so I get lost.

Another potential solution would be some sort of virtual output that holds workspaces offscreen.

I like this idea. I was also worried that this story would be pointless without a third party tie that sends things to hidden. So would this entail keybinds that toggle hidden on workspaces by their id, name, etc?

EDIT: i've gone ahead and taken a shot at this req.
d5f4772#diff-608918babe5a4eb5db908e910f16f33a173c6821eb12ef6e1077bf277160da76

let output = self.output.as_ref().unwrap();
let Some(output) = self.output.as_ref() else {
return;
};
Copy link
Author

Choose a reason for hiding this comment

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

On the case of updating the output size of the ws a hidden workspace will not be rendered so we need to exit early if there is none. At least that seems to be the need, as otherwise i'd get core dump on the initial hide toggle.


/// Hidden workspaces need to track their original idx
///
pub original_idx: Option<usize>,
Copy link
Author

Choose a reason for hiding this comment

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

Ideally, I'd like a hidden workspace to try and keep it's idx so it can be at an intuitive place when you unhide it, similar to workspace movement on monitor connect/disconnect.

@argosnothing
Copy link
Author

tmp QsDgnBxr3O

Bit of progress on this.

@argosnothing
Copy link
Author

Currently, for my usecase this is functional for me, and I encourage anyone willing to experiment to try this PR and let me know if there are issues with it, or edgecases I didn't test. I have also added a separate branch on my scratchpad project that makes use of this PR's new niri_ipc WorkspacesWithHidden functionality to summon and stash windows bound to hidden workspaces.

@argosnothing
Copy link
Author

argosnothing commented Dec 15, 2025

An alternative approach I've been thinking about:
We maintain a separate vec of workspaces, remove the original_idx field, and simply use the workspace idx for the hidden workspaces vec to map onto the original workspace instead. I think this would be cleaner, and potentially lets us avoid unnecessary data on structs like hidden and original_idx. We will know if a workspace is hidden simply by it being in the hidden_workspaces vec, and we can determine the idx it wants to go by what its idx is, and simply insert by that idx when we unhide it.

So, we hide a workspace. We remove the workspace from the monitors workspace vec ( but retain its output. ). Then insert that workspace into a hidden_workspaces vec on that monitor. Because it's in a different vec we shouldn't need extra logic for managing idxs of the visible workspaces. When we unhide that workspace, we simply insert it by its idx into the main workspace vec at its desired position.

My hope is this method will require far fewer changes, less extra struct fields, less complexity for the same functionality.

main...argosnothing:niri:hidden-workspaces-2 an attempt was made. I'm not really convinced this is a better route after all. It seems to be even more code add than simply keeping hidden workspaces in the same vec and adjusting indexes to keep out of the visible idx range.

@argosnothing argosnothing force-pushed the hidden-workspaces branch 2 times, most recently from 6ce2667 to 0333fd0 Compare December 16, 2025 01:24
println!("The change will apply when it is connected.");
}
}
Msg::WorkspacesWithHidden => {
Copy link
Author

Choose a reason for hiding this comment

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

Initially I had added options to Workspaces for different filtering options with Hidden workspaces, but this method seems better since it won't break projects that use the rust_ipc crate as it's opt in.

Response::Outputs(outputs.collect())
}
Request::Workspaces => {
let state = ctx.event_stream_state.borrow();
Copy link
Author

Choose a reason for hiding this comment

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

We do not include hidden workspaces in normal workspace requests. If consumers want to access hidden workspaces, they'll need to use the new endpoint.

state.apply(event.clone());
server.send_event(event);
}

Copy link
Author

Choose a reason for hiding this comment

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

I played around with different methods to keep hidden workspaces out of event stream while still keeping it for the state, this just ended up being the only one I could get to work without horrible things happening.

self.workspaces[idx].hidden = true;
let mut ws = self.remove_workspace_by_idx(idx);
ws.original_idx = Some(idx);
self.workspaces.insert(self.workspaces.len(), ws);
Copy link
Author

Choose a reason for hiding this comment

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

This is the main "trick" of how im doing these hidden workspaces. If it's hidden, we make sure it's at the end of the vec, and that empty workspace is shown right before, creating the illusion that the empty workspace is the last workspace, and the hidden ones arn't behind it.

@alexjp
Copy link

alexjp commented Dec 19, 2025

Hi,

I don't want to add complexity, but may I ask if this method of hiding workspaces supports also hidding named workspaces?

I have a couple of usecases that would be great to have named workspaces (which have specific layouts) be hidden!

Thanks!

@argosnothing
Copy link
Author

argosnothing commented Dec 19, 2025

Hi,

I don't want to add complexity, but may I ask if this method of hiding workspaces supports also hidding named workspaces?

I have a couple of usecases that would be great to have named workspaces (which have specific layouts) be hidden!

Thanks!

Yes, in fact it is designed to only work on named workspaces.

Currently I have a workspace called stash that i send scratchpad windows to. This is usually hidden, but i can toggle it with a keybind. I have another workspace called "work" that Is unhidden but I toggle to hidden when im not working.

@alexjp
Copy link

alexjp commented Dec 20, 2025

Yes, in fact it is designed to only work on named workspaces.

Right, sorry, dumb question!

I tried your branch hidden-workspaces, and it works, I managed to hide some workspaces at startup and toggle them hidden/visible.

Some things I missed, and wondering if it is in scope of features:

  • If a workspace is hidden, but I have a shortcut to focus it, if it could be made visible automatically (and not focus a dynamically created workspace)
  • If the workspace is hidden, visible when selected, and then left (switch to another workspace), without any window open, if it could be hidden again. (maybe a setting "hide-when-empty")
  • If the bind toggle-workspace-visibility could work with current workspace, something like toggle-current-workspace-visibility

I understand if these are not wanted features, since I think I am trying to use this in a different usecase. Instead of one hidden "stash" workspace, I am trying to have several workspaces that I not always use, configured.
For example, my usecase is something like:

  • Workspace named "MultiMedia"
    • Has special layout settings
    • My wlr-which-key script with check the name of the workspace, and if it exists, it will show the specific menu (like using wlr-which-key -k "a m")
  • Workspace named "Chat"
    • Has special layout settings
    • My wlr-which-key script with check the name of the workspace, and if it exists, it will show the specific menu (like using wlr-which-key -k "a c")

Mentioning again, totally understand if this usecase it outside of scope!

Thanks!

@argosnothing
Copy link
Author

argosnothing commented Dec 20, 2025

Yes, in fact it is designed to only work on named workspaces.

Right, sorry, dumb question!

I tried your branch hidden-workspaces, and it works, I managed to hide some workspaces at startup and toggle them hidden/visible.

Some things I missed, and wondering if it is in scope of features:

* If a workspace is hidden, but I have a shortcut to focus it, if it could be made visible automatically (and not focus a dynamically created workspace)

* If the workspace is hidden, visible when selected, and then left (switch to another workspace), without any window open, if it could be hidden again. (maybe a setting "hide-when-empty")

* If the bind `toggle-workspace-visibility` could work with current workspace, something like `toggle-current-workspace-visibility`

I understand if these are not wanted features, since I think I am trying to use this in a different usecase. Instead of one hidden "stash" workspace, I am trying to have several workspaces that I not always use, configured. For example, my usecase is something like:

* Workspace named "MultiMedia"
  
  * Has special layout settings
  * My `wlr-which-key` script with check the name of the workspace, and if it exists, it will show the specific menu (like using `wlr-which-key -k "a m"`)

* Workspace named "Chat"
  
  * Has special layout settings
  * My `wlr-which-key` script with check the name of the workspace, and if it exists, it will show the specific menu (like using `wlr-which-key -k "a c"`)

Mentioning again, totally understand if this usecase it outside of scope!

Thanks!

1 and 2 seem like good ideas and would be fun to implement.

The issue with the third, and why I only have it setup for named workspaces ( although i believe i still need to properly represent this in the config )

  1. You are on idx 3. so the third workspace in a monitor. there is a workspace on 4 as well.
  2. You remove workspace on idx 3 ( because you are currently on that workspace and hide it)
  3. What happens is workspace at idx 3 becomes hidden, and its idx actually ends up being 5 ( or 6 when an empty workspace is there )
  4. The workspace that was idx 4 now becomes idx 3, taking that hidden workspaces place.
  5. You don't really have any method to access that workspace again because to the user workspaces are really regarded by that idx.

Your usecase makes sense though, and for the first two i'll add that to my todo on this PR.

EDIT: I will need to support dynamically assigned and unassigned workspace as well, so this might suit your first requirement after all once implemented.

@alexjp
Copy link

alexjp commented Dec 20, 2025

1 and 2 seem like good ideas and would be fun to implement.

The issue with the third, and why I only have it setup for named workspaces ( although i believe i still need to properly represent this in the config )

Great to hear!

I, in my own use case, don't see much issue in the third not being implemented if the first 2 are. Because if it "auto hides" based on having windows or not, I don't need much of "manual hidding".

For the manual hidding (and really hide windows), I can set a proper static scratchpad workspace, like the use case of this pull request.

Thanks!

@argosnothing
Copy link
Author

argosnothing commented Dec 20, 2025

1 and 2 seem like good ideas and would be fun to implement.
The issue with the third, and why I only have it setup for named workspaces ( although i believe i still need to properly represent this in the config )

Great to hear!

I, in my own use case, don't see much issue in the third not being implemented if the first 2 are. Because if it "auto hides" based on having windows or not, I don't need much of "manual hidding".

For the manual hidding (and really hide windows), I can set a proper static scratchpad workspace, like the use case of this pull request.

Thanks!

hopefully this is easy to follow, but this should be an example of both 1 and 2? It's not through a config option, the idea is that if you have focus-workspace to a named workspace that is hidden, it will reveal itself to you until you switch off from it, in which case it rehides. This isn't really ready to be merged into the main PR yet. ( the idx finding probably needs work and as you can see, when leaving the needs hidden workspace it tends to skip past 1 )
example

@alexjp
Copy link

alexjp commented Dec 21, 2025

Just tested.

Yeah, as you said exactly :)

It is interesting, maybe for temporary apps/windows, that I don't mind losing when closing Niri.
The issue with them being hidden as soon as I leave, is that then I need to look for open apps in every workspace.

I am testing with Dank shell, which allows to give special icons for named desktops in Niri. (one issue with Dank shell, is if I click on the taskbar, the icon of a window on a hidden workspace, it does not change to it)

Would be great to have an option to leave them visible if they have windows, but, this way is also interesting! (already have a use case in mind... to use llama-serve, which I want running but it would be handy to be hidden when not on it, and I would not care to close it before exiting Niri).

Thanks, looking good! :)

@argosnothing
Copy link
Author

Just tested.

Yeah, as you said exactly :)

It is interesting, maybe for temporary apps/windows, that I don't mind losing when closing Niri. The issue with them being hidden as soon as I leave, is that then I need to look for open apps in every workspace.

I am testing with Dank shell, which allows to give special icons for named desktops in Niri. (one issue with Dank shell, is if I click on the taskbar, the icon of a window on a hidden workspace, it does not change to it)

Would be great to have an option to leave them visible if they have windows, but, this way is also interesting! (already have a use case in mind... to use llama-serve, which I want running but it would be handy to be hidden when not on it, and I would not care to close it before exiting Niri).

Thanks, looking good! :)

Next time i do a rebuild i'll bring in these changes myself. When testing niri it actually crashed my scratchpad daemon so i need to retag my windows in my stash workspace to pick them back up after i restart the daemon. Would save me a keypress as I never need the workspace to stay unhidden when im done with it.

As for the other stuff i'll probably hold off until I get some additional feedback on what things other people would want/expect for a PR like this. Your suggestion of auto unhiding when explicitly focused makes intuitive sense to me though.

@fnzr
Copy link

fnzr commented Dec 22, 2025

I'm trying out this branch, and things have been pretty smooth so far. I'm also using https://github.com/argosnothing/niri-scratchpad-rs/tree/hidden-workspaces and it's working as expected.

I just did some basic sanity tests, but that's a feature I'm really looking forward to, I'll keep using it to see if something comes up. Good job!

Something I did notice, if you create a hidden workspace while niri is running, niri crashes.

I mean, adding

workspace "stash" {
   hidden true
}

and saving to hot reload, it causes a crash.

Creating a hidden false workspace does not crash, and creating a hidden false workspace and changing it to true also does not crash. Running niri again after the crash works normally

@argosnothing
Copy link
Author

I'm trying out this branch, and things have been pretty smooth so far. I'm also using https://github.com/argosnothing/niri-scratchpad-rs/tree/hidden-workspaces and it's working as expected.

I just did some basic sanity tests, but that's a feature I'm really looking forward to, I'll keep using it to see if something comes up. Good job!

Something I did notice, if you create a hidden workspace while niri is running, niri crashes.

I mean, adding

workspace "stash" {
   hidden true
}

and saving to hot reload, it causes a crash.

Creating a hidden false workspace does not crash, and creating a hidden false workspace and changing it to true also does not crash. Running niri again after the crash works normally

Ah i forgot to add that bug to my todos! I did run into this. I'll add it to the list. I try to get a few things knocked out on this PR a week and do my best to keep it up to date with master. I appreciate the feedback!

@argosnothing
Copy link
Author

I've added the fix to Crash on config change that adds hidden true to a named workspace..

One note is that hidden workspaces "want" to be sent to where they were previously when you hide and unhide. I use the idx for this. On a newly added hidden workspace that idx will be where that workspace would have been sent to if it were not hidden.

So lets say you have non hidden workspaces like
|0|1|2|

on hidden workspace add it's original_idx ( where it wants to go when unhidden ) will be set to 0, pushing the idx of other workspaces up, it's actual position on add will be at |3| (at the end of the vec, and hidden, inaccessible), once it gets unhid it will go to it's 0 position.

@GoodbyeNJN
Copy link

After using this feature daily for nearly a month, I find it to be sufficiently reliable and very practical. Are there any plans to convert this PR from a draft to a formal PR?

@argosnothing
Copy link
Author

argosnothing commented Jan 16, 2026

After using this feature daily for nearly a month, I find it to be sufficiently reliable and very practical. Are there any plans to convert this PR from a draft to a formal PR?

I would like some input from contributors and yalter first. There are currently many Issues in the pipeline so I'd rather get input before I add to the pile. There's also several edge cases I know exist like travelling hidden workspaces that I need to brainstorm workarounds for.

Prs to this are welcome as well, my plan is to keep this updated with niri major releases.

@MilitaoLucas
Copy link

Hello, great work! I am using this, and I am having a problem:

window-rule {
    match app-id="qalculate-gtk"
    open-floating true
    open-on-workspace "stash"
    default-column-width { proportion 0.75; }
    default-window-height { proportion 0.75; }
    default-floating-position x=0 y=100 relative-to="bottom"
}
workspace "stash" {
   hidden true
}

as soon as I open the application I get the workspace ID of the current workspace even though it was supposed to open in the stash workspace:

Window ID 13:
  Title: "Qalculate!"
  App ID: "qalculate-gtk"
  Is floating: yes
  PID: 107243
  Workspace ID: 1
  Layout:
    Tile size: 1200 x 642
    Workspace-view position: 200, 158
    Window size: 1200 x 642
    Window offset in tile: 0 x 0

It should show workspace ID 3:

❯ niri msg workspaces-with-hidden
Output "DP-1":
 * 1
   2

Output "HDMI-A-2":
 * 1
   2
   3 "stash"

I don't know if this is related to having 2 monitors. But I also can't see the window but know it is running because of niri msg windows.

@MilitaoLucas
Copy link

Hello, great work! I am using this, and I am having a problem:

window-rule {
    match app-id="qalculate-gtk"
    open-floating true
    open-on-workspace "stash"
    default-column-width { proportion 0.75; }
    default-window-height { proportion 0.75; }
    default-floating-position x=0 y=100 relative-to="bottom"
}
workspace "stash" {
   hidden true
}

as soon as I open the application I get the workspace ID of the current workspace even though it was supposed to open in the stash workspace:

Window ID 13:
  Title: "Qalculate!"
  App ID: "qalculate-gtk"
  Is floating: yes
  PID: 107243
  Workspace ID: 1
  Layout:
    Tile size: 1200 x 642
    Workspace-view position: 200, 158
    Window size: 1200 x 642
    Window offset in tile: 0 x 0

It should show workspace ID 3:

❯ niri msg workspaces-with-hidden
Output "DP-1":
 * 1
   2

Output "HDMI-A-2":
 * 1
   2
   3 "stash"

I don't know if this is related to having 2 monitors. But I also can't see the window but know it is running because of niri msg windows.

As a reference, I solved this by keeping track of when the windows is hidden by using a file in the tmp dir.

@argosnothing
Copy link
Author

Hello, great work! I am using this, and I am having a problem:

window-rule {
    match app-id="qalculate-gtk"
    open-floating true
    open-on-workspace "stash"
    default-column-width { proportion 0.75; }
    default-window-height { proportion 0.75; }
    default-floating-position x=0 y=100 relative-to="bottom"
}
workspace "stash" {
   hidden true
}

as soon as I open the application I get the workspace ID of the current workspace even though it was supposed to open in the stash workspace:

Window ID 13:
  Title: "Qalculate!"
  App ID: "qalculate-gtk"
  Is floating: yes
  PID: 107243
  Workspace ID: 1
  Layout:
    Tile size: 1200 x 642
    Workspace-view position: 200, 158
    Window size: 1200 x 642
    Window offset in tile: 0 x 0

It should show workspace ID 3:

❯ niri msg workspaces-with-hidden
Output "DP-1":
 * 1
   2

Output "HDMI-A-2":
 * 1
   2
   3 "stash"

I don't know if this is related to having 2 monitors. But I also can't see the window but know it is running because of niri msg windows.

Interesting. I haven't tested with against window rules. I'll add that to my todos. Thanks for letting me know.

@MilitaoLucas
Copy link

MilitaoLucas commented Jan 23, 2026

@argosnothing Something that is more of a QOL tip, would it be possible to just show some symbol or something in the normal workspace listing command to say that the workspace is hidden? Maybe it is something like:

3 "stash"

@MilitaoLucas
Copy link

@argosnothing Something that is more of a QOL tip, would it be possible to just show some symbol or something in the normal workspace listing command to say that the workspace is hidden? Maybe it is something like:

3 "stash"

I think that most scripts use json, so it shouldn't break a lot of stuff to unify the commands and add a flag. But, maybe you just created the command to make it more clear while testing the PR.

I will let you know if I find anything else. I plan to keep using it. I will also soon put the scripts I use to make the complete scratchpad setup work even with that issue.

Thank you for the awesome work! I don't have the time to help with code as I am not very proficient in Rust, but I hope I can help with reproducible bug reports.

@argosnothing
Copy link
Author

@argosnothing Something that is more of a QOL tip, would it be possible to just show some symbol or something in the normal workspace listing command to say that the workspace is hidden? Maybe it is something like:
3 "stash"

I think that most scripts use json, so it shouldn't break a lot of stuff to unify the commands and add a flag. But, maybe you just created the command to make it more clear while testing the PR.

I will let you know if I find anything else. I plan to keep using it. I will also soon put the scripts I use to make the complete scratchpad setup work even with that issue.

Thank you for the awesome work! I don't have the time to help with code as I am not very proficient in Rust, but I hope I can help with reproducible bug reports.

Yes, the initial intention was to never touch previous IPC code. If i remember correctly, I initially put the hidden in normal workspace list calls, with an additional hidden flag, but the issue is shells like noctalia do not know about this flag, and will simply include workspaces by default.

I initially added a flag to that endpoint, and this does work. The issue is mainly on applications that hook into the niri_ipc rust crate would also need to be updated for that version. In the end I think it does make more sense to pass this along any custom ipcs niri exposes, but since I also use this daily and make use of noctalia ( and also don't want to modify noctalias own dependencies to match ), I went ahead and just made a special endpoint to grab hidden.

@argosnothing
Copy link
Author

argosnothing commented Jan 24, 2026

Hello, great work! I am using this, and I am having a problem:

window-rule {
    match app-id="qalculate-gtk"
    open-floating true
    open-on-workspace "stash"
    default-column-width { proportion 0.75; }
    default-window-height { proportion 0.75; }
    default-floating-position x=0 y=100 relative-to="bottom"
}
workspace "stash" {
   hidden true
}

as soon as I open the application I get the workspace ID of the current workspace even though it was supposed to open in the stash workspace:

Window ID 13:
  Title: "Qalculate!"
  App ID: "qalculate-gtk"
  Is floating: yes
  PID: 107243
  Workspace ID: 1
  Layout:
    Tile size: 1200 x 642
    Workspace-view position: 200, 158
    Window size: 1200 x 642
    Window offset in tile: 0 x 0

It should show workspace ID 3:

❯ niri msg workspaces-with-hidden
Output "DP-1":
 * 1
   2

Output "HDMI-A-2":
 * 1
   2
   3 "stash"

I don't know if this is related to having 2 monitors. But I also can't see the window but know it is running because of niri msg windows.

As a reference, I solved this by keeping track of when the windows is hidden by using a file in the tmp dir.

The latest push should handle this window rule properly now. It will work similarly to keybinds that take you to a workspace based on name. So the workspace will be temporarily unhidden when you open it on that workspace, moving off the workspace should then rehide the workspace. Let me know if this is intuitive!

Had to revert this as it caused a regression. I also updated against latest niri main so might still be worth the build.

…regression for moving to workspace )

This reverts commit 8c972b8.
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.

8 participants