Skip to content

feat: Open a window as a wlr-layer-shell surface via WindowOptions.layerShell. - #64

Merged
remorses merged 1 commit into
remorses:mainfrom
KiteShell:feat/layer-shell-window-kind
Sep 12, 2026
Merged

feat: Open a window as a wlr-layer-shell surface via WindowOptions.layerShell.#64
remorses merged 1 commit into
remorses:mainfrom
KiteShell:feat/layer-shell-window-kind

Conversation

@dim-ghub

Copy link
Copy Markdown
Contributor

In zed/ there is already an implemention of layer-shell at Rust level (Window Kind::Layer Shell, layershell::Layer Shell Options) but nothing was exposed to JS. The Window Options did not provide any way of requesting it so it could not be used outside of it to open normal floating windows. Either a panel or a dock or a notification-shade or a wallpaper required an edge anchored surface which must have exclusive zone which is precisely whatlayershellwas intended for.

Add two fields to napi Window Options:

  • layerShell?: Layer Shell Options - containsnamespace, layer (background | bottom | top | overlay), anchor (top | bottom | left | right, in the two corners opposite in Y axes, it is used for extending an axis from an edge), exclusiveZone, exclusiveEdge, margin ([top, right, bottom, left]), keyboardInteractivity (none | on-demand | exclusive). All these options are optional, it is assumed by default anchor covers top and left and right edges (anchored top bar) and it assumes layer is 'top'.
  • appId?: string - WMCLASS for X11 or appid for Wayland. This field is already useful to be declared to allow a compositor targeting layer surface by rule, it can also be useful by itself.

The function togpuiwindowoptions will use layerShell on Window Kind::Layer Shell and will remove the titlebar. The Variant only is available for linux gpui on wayland-enabled target, so the layerShell argument is only translated when cfg(targetos="linux"), it is ignored on macOS, Windows and an appId still apply. The functioninit_threaded opens a surface at origin rather thanBounds::centered (compositor places based on anchor and exclusivezone)

The GPUI fork in zed/ already implements layer-shell at the Rust level
(WindowKind::LayerShell, layer_shell::LayerShellOptions), but nothing
reached JS: WindowOptions had no way to ask for it, so @gpuix/react
could only open normal floating windows. A panel, dock, notification
shade or wallpaper needs an edge-anchored surface with an exclusive
zone, which is exactly what layer-shell is for.

Add to the napi WindowOptions:

- layerShell?: LayerShellOptions - namespace, layer (background |
  bottom | top | overlay), anchor (top / bottom / left / right, two
  opposite edges stretch that axis), exclusiveZone, exclusiveEdge,
  margin ([top, right, bottom, left]), keyboardInteractivity (none |
  on-demand | exclusive). All optional; the default anchor is a top
  bar (top | left | right) and the default layer is top.
- appId?: string - Wayland app_id / X11 WM_CLASS, useful on its own
  and needed for a compositor to target a layer surface by rule.

to_gpui_window_options maps layerShell onto WindowKind::LayerShell and
drops the titlebar. The WindowKind variant only exists on a
wayland-enabled Linux gpui build, so the mapping is
cfg(target_os = "linux"); on macOS and Windows layerShell is ignored
and appId still applies. init_threaded opens a layer surface at the
origin instead of Bounds::centered, since the compositor places it
from its anchor.

index.d.ts is hand-edited rather than regenerated: the @napi-rs/cli
in node_modules emits a much smaller index.d.ts than the committed
one (it drops every doc comment and all of TestGpuixRenderer's
methods), so a full `napi build` regen here would be a large
destructive diff unrelated to this change. The added block matches
the shape napi produces for these structs.

Tests in renderer.rs cover the mapping (anchor union, exclusive zone
and edge, margin, keyboard interactivity, titlebar dropped) and that a
window with no layerShell stays Normal with a titlebar.

Verified on Hyprland (3 monitors): a @gpuix/react app calling
render(<Bar/>, { layerShell: { anchor: ['top','left','right'],
exclusiveZone: 34 } }) opens a 34px top bar, hyprctl reports the
kite-panel layer surface and reserved: [0, 34, 0, 0], and a React
setInterval clock re-renders live on the surface.
@remorses
remorses merged commit e47bd1c into remorses:main Sep 12, 2026
@remorses

Copy link
Copy Markdown
Owner

thanks!

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