-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
webapps: add module for declarative web application desktop entries #7917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
webapps: add module for declarative web application desktop entries #7917
Conversation
d8ba7b9 to
d0b2c07
Compare
d0b2c07 to
c48be9e
Compare
| ... | ||
| }: | ||
|
|
||
| with lib; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| with lib; |
| cfg = config.programs.webApps; | ||
|
|
||
| # Type for a single web app | ||
| webAppOpts = types.submodule ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer in-lining submodule usage or scoping in a let block closer to usage.
| isChromiumBased = elem browserName [ | ||
| "chromium" | ||
| "brave" | ||
| "google-chrome" | ||
| "google-chrome-stable" | ||
| "vivaldi" | ||
| ]; | ||
|
|
||
| binary = "${toString browserPkg}/bin/${browserName}"; | ||
| in | ||
| if isChromiumBased then | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}" | ||
| else if browserName == "firefox" then | ||
| "${binary} ${escapeDesktopArg url}" # Firefox doesn't support --app mode | ||
| else | ||
| # Fallback: assume chromium-based behavior | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}"; | ||
|
|
||
| # Auto-detect browser if not explicitly set | ||
| detectedBrowser = | ||
| if cfg.browser != null then | ||
| cfg.browser | ||
| else if config.programs.chromium.enable && config.programs.chromium.package != null then | ||
| config.programs.chromium.package | ||
| else if config.programs.brave.enable && config.programs.brave.package != null then | ||
| config.programs.brave.package | ||
| else if config.programs.firefox.enable && config.programs.firefox.package != null then | ||
| config.programs.firefox.package | ||
| else | ||
| pkgs.chromium; # Default fallback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be cleaner to create an attrset used for configuring these default / known browsers instead of multiple if else if blocks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion we should make it so cfg.browser is type = types.package (i.e: not-nullable) and set an appropriate default in the module option. We should also document the default choice.
| name = mkOption { | ||
| type = types.nullOr types.str; | ||
| default = null; | ||
| description = "Name of the web application. If not provided, will be derived from the attribute name."; | ||
| example = "GitHub"; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types.submodule can use a function to get the attribute name, this avoids the nullOr which doesn't really want null:
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
// ...
name = lib.mkOption {
type = lib.types.str;
default = name;
description = "...";
};
// ...
}));| "Network" | ||
| "WebBrowser" | ||
| ]; | ||
| description = "Categories in which the entry should be shown in application menus."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a canonical list of categories we can link to?
| # Desktop entries don't need shell escaping, just basic space escaping | ||
| escapeDesktopArg = arg: builtins.replaceStrings [ " " ] [ "\\ " ] (toString arg); | ||
|
|
||
| optionString = concatStringsSep " " ( | ||
| mapAttrsToList (name: value: "--${name}=${escapeDesktopArg value}") extraOptions | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I feel like toGNUCommandLine should be able to be used in this circumstance. Perhaps a PR to add a way to customize the escaping function would be good.
| isChromiumBased = elem browserName [ | ||
| "chromium" | ||
| "brave" | ||
| "google-chrome" | ||
| "google-chrome-stable" | ||
| "vivaldi" | ||
| ]; | ||
|
|
||
| binary = "${toString browserPkg}/bin/${browserName}"; | ||
| in | ||
| if isChromiumBased then | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}" | ||
| else if browserName == "firefox" then | ||
| "${binary} ${escapeDesktopArg url}" # Firefox doesn't support --app mode | ||
| else | ||
| # Fallback: assume chromium-based behavior | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An isFirefoxBased filter would lead to less duplication.
| isChromiumBased = elem browserName [ | ||
| "chromium" | ||
| "brave" | ||
| "google-chrome" | ||
| "google-chrome-stable" | ||
| "vivaldi" | ||
| ]; | ||
|
|
||
| binary = "${toString browserPkg}/bin/${browserName}"; | ||
| in | ||
| if isChromiumBased then | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}" | ||
| else if browserName == "firefox" then | ||
| "${binary} ${escapeDesktopArg url}" # Firefox doesn't support --app mode | ||
| else | ||
| # Fallback: assume chromium-based behavior | ||
| "${binary} --app=${escapeDesktopArg url} ${optionString}"; | ||
|
|
||
| # Auto-detect browser if not explicitly set | ||
| detectedBrowser = | ||
| if cfg.browser != null then | ||
| cfg.browser | ||
| else if config.programs.chromium.enable && config.programs.chromium.package != null then | ||
| config.programs.chromium.package | ||
| else if config.programs.brave.enable && config.programs.brave.package != null then | ||
| config.programs.brave.package | ||
| else if config.programs.firefox.enable && config.programs.firefox.package != null then | ||
| config.programs.firefox.package | ||
| else | ||
| pkgs.chromium; # Default fallback |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion we should make it so cfg.browser is type = types.package (i.e: not-nullable) and set an appropriate default in the module option. We should also document the default choice.
| config = { | ||
| programs.webApps = { | ||
| enable = true; | ||
| browser = pkgs.chromium; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dummy package.
| config = { | ||
| programs.webApps = { | ||
| enable = true; | ||
| browser = pkgs.firefox; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dummy package.
| { | ||
| programs.webApps = { | ||
| enable = true; | ||
| browser = pkgs.chromium; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dummy package.
|
|
||
| programs.webApps = { | ||
| enable = true; | ||
| browser = pkgs.chromium; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dummy package.
| }; | ||
|
|
||
| extraOptions = mkOption { | ||
| type = types.attrs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use the usual attrsOf (oneOf [ str number boolean ]) type for command line options.
|
Thanks for the review, will update it towards weekend hopefully as i'm traveling |
Description
This PR adds a new webapps module for Home Manager that allows users to declaratively configure web applications as desktop entries. This enables creating application launcher entries for web services like Gmail, Slack, Discord, GitHub, etc., with proper browser integration and desktop environment support.
Inspiration: This module was inspired by the webapps functionality in omarchy. Special thanks to @dhh for the inspiration and approach.
Features
--app=flags for Chromium-based browsers for true webapp experienceUsage Example
Checklist
nix fmt.Tests Included
The module has been thoroughly tested and is working correctly with Hyprland + Rofi, creating proper desktop entries that appear in application launchers and launch web applications in app mode.
Special thanks to @dhh and the omarchy project for the inspiration!