724 lines
34 KiB
Nix
724 lines
34 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
inherit (lib) mkOption mkIf types;
|
|
inherit (types) either;
|
|
|
|
clamp =
|
|
x: min: max:
|
|
if x < min then
|
|
min
|
|
else if x > max then
|
|
max
|
|
else
|
|
x;
|
|
|
|
cfg = config.programs.tetrio-desktop;
|
|
in
|
|
{
|
|
options.programs.tetrio-desktop = {
|
|
enable = lib.mkEnableOption "tetrio";
|
|
package = lib.mkPackageOption pkgs "tetrio-desktop" { };
|
|
|
|
plus = {
|
|
enable = lib.mkEnableOption "tetrio-plus";
|
|
package = lib.mkOption {
|
|
type = types.path;
|
|
description = "The package to use for TETR.IO PLUS";
|
|
default = pkgs.tetrio-plus;
|
|
};
|
|
|
|
skin = mkOption {
|
|
type = types.nullOr (
|
|
types.submodule {
|
|
options = {
|
|
package = mkOption {
|
|
type = types.package;
|
|
description = "The package to use for the skin";
|
|
example = "mpkgs.tetrio.skin.simple-connected";
|
|
};
|
|
|
|
nearest = mkOption {
|
|
type = types.bool;
|
|
description = "Force nearest-neighbor scaling";
|
|
default = false;
|
|
};
|
|
};
|
|
}
|
|
);
|
|
default = null;
|
|
description = "In-game tetrimino skin";
|
|
};
|
|
|
|
hideOnStartup = lib.mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Disable the TETR.IO PLUS configuration panel showing";
|
|
};
|
|
};
|
|
settings = {
|
|
handling =
|
|
let
|
|
buffering = types.enum [
|
|
"off"
|
|
"hold"
|
|
"tap"
|
|
];
|
|
in
|
|
{
|
|
|
|
auto_repeat_rate = mkOption {
|
|
type = types.number;
|
|
apply = x: clamp x 0 5;
|
|
description = "Automatic Repeat Rate: the speed at which tetrominoes move when holding down movement keys, measured in frames per movement.";
|
|
default = 2;
|
|
};
|
|
delayed_auto_shift = mkOption {
|
|
type = types.number;
|
|
apply = x: clamp x 1 20;
|
|
description = "Delayed Auto Shift: the time between the initial keypress and the start of its automatic repeat movement, measured in frames.";
|
|
default = 10;
|
|
};
|
|
das_cut_delay = mkOption {
|
|
type = types.number;
|
|
apply = x: clamp x 0 20;
|
|
|
|
description = "DAS Cut Delay (frames): if not 0, any ongoing DAS movement will pause for a set amount of time after dropping/rotating a piece, measured in frames.";
|
|
default = 1;
|
|
};
|
|
soft_drop_factor = mkOption {
|
|
type = types.number;
|
|
apply = x: clamp x 5 41;
|
|
description = "Soft Drop Factor: the factor with which soft drops change the gravity speed.";
|
|
default = 6;
|
|
};
|
|
prevent_hard_drops = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, when a piece locks on its own, the hard drop key becomes unavailable for a few frames. This prevents accidental hard drops.";
|
|
default = true;
|
|
};
|
|
direction_cancel_das = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, DAS charge is cancelled when you change directions.";
|
|
default = false;
|
|
};
|
|
soft_drop_over_movement = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, at very high speeds soft drop will always take precedence over horizontal movement, for a more consistent game feel.";
|
|
default = true;
|
|
};
|
|
rotation_buffering = mkOption {
|
|
type = buffering;
|
|
description = "When to buffer rotations for the next piece (\"hold\": only if you hold the key as the next piece spawns).";
|
|
default = "tap";
|
|
};
|
|
hold_buffering = mkOption {
|
|
type = buffering;
|
|
description = "When to buffer holding for the next piece (\"hold\": only if you hold the key as the next piece spawns).";
|
|
default = "tap";
|
|
};
|
|
};
|
|
audio =
|
|
let
|
|
percentage = x: x / 100.0;
|
|
in
|
|
{
|
|
music = {
|
|
volume = mkOption {
|
|
type = types.number;
|
|
apply = x: percentage (clamp x 0 200);
|
|
description = "Volume (%) at which BGM and jingles play.";
|
|
default = 100;
|
|
};
|
|
reset_on_retry = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If enabled, the background music will reset whenever you retry a game.";
|
|
default = false;
|
|
};
|
|
preferences = mkOption {
|
|
type =
|
|
with types;
|
|
attrsOf (enum [
|
|
"off"
|
|
"--"
|
|
"-"
|
|
""
|
|
"+"
|
|
"++"
|
|
]);
|
|
apply =
|
|
x:
|
|
pkgs.lib.mapAttrs (
|
|
_: value:
|
|
{
|
|
"off" = "ban";
|
|
"--" = "minmin";
|
|
"-" = "min";
|
|
"" = "base";
|
|
"+" = "plus";
|
|
"++" = "plusplus";
|
|
}
|
|
."${value}"
|
|
) x;
|
|
default = { };
|
|
};
|
|
};
|
|
sfx = {
|
|
volume = mkOption {
|
|
type = types.number;
|
|
apply = x: percentage (clamp x 0 200);
|
|
description = "Volume (%) at which sound effects play.";
|
|
default = 100;
|
|
};
|
|
next_pieces = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to play a sound effect that signifies the next piece that'll come up.";
|
|
default = false;
|
|
};
|
|
other_players = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to hear the sounds of other people playing in Multiplayer.";
|
|
default = true;
|
|
};
|
|
attacks = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to hear the sounds of attacks going towards and from you.";
|
|
default = true;
|
|
};
|
|
speed_changes = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to hear a sound when your Climb Speed changes in Quick Play.";
|
|
default = true;
|
|
};
|
|
};
|
|
scroll_adjust_volume = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to allow adjusting the volume via scrolling in-game (or everywhere while holding ALT).";
|
|
default = true;
|
|
};
|
|
mute_unfocused = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, the background music will be muted when TETR.IO is minimized or tabbed away.";
|
|
default = true;
|
|
};
|
|
stereo = mkOption {
|
|
type = types.number;
|
|
apply = x: percentage (clamp x 0 100);
|
|
description = "Intensity (%) of stereo effects. 0% -> Centered; 100% -> Directional";
|
|
default = 50;
|
|
};
|
|
disable = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, no audio will ever play. This will speed up the game, with a rather obvious drawback.";
|
|
default = false;
|
|
};
|
|
};
|
|
visual =
|
|
let
|
|
toFloat = x: if builtins.typeOf x == "str" then builtins.fromJSON x else x;
|
|
percentage =
|
|
x: min: max:
|
|
builtins.toString ((clamp (toFloat x) min max) / 100.0);
|
|
in
|
|
{
|
|
graphics = {
|
|
pipeline = mkOption {
|
|
type = types.enum [
|
|
"compatability"
|
|
"webgl-1"
|
|
"webgl-2"
|
|
];
|
|
apply =
|
|
x:
|
|
{
|
|
"compatability" = "legacy";
|
|
"webgl-1" = "webgl1";
|
|
"webgl-2" = "webgl2";
|
|
}
|
|
."${x}";
|
|
description = "The WebGL mode to use for rendering";
|
|
default = "webgl-2";
|
|
};
|
|
tier = mkOption {
|
|
type = types.enum [
|
|
"minimal"
|
|
"low"
|
|
"medium"
|
|
"high"
|
|
"ultra"
|
|
];
|
|
description = "The graphics tier to use for rendering";
|
|
default = "ultra";
|
|
};
|
|
cache = mkOption {
|
|
type = types.bool;
|
|
apply = x: if x then "medium" else "low";
|
|
description = "Whether or not to enable caching of resources";
|
|
default = true;
|
|
};
|
|
particle_count = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 10 150;
|
|
description = "How many particles to display (%).";
|
|
default = 150;
|
|
};
|
|
|
|
powersave = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, prioritize power saving over performance.";
|
|
default = false;
|
|
};
|
|
low_resolution = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, render blurrier graphics. Not very pretty but helps performance.";
|
|
default = false;
|
|
};
|
|
low_precision_counters = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, don't show as much precision on in-game counters. Speeds up the game significantly.";
|
|
default = false;
|
|
};
|
|
};
|
|
show_unfocus_warning = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, a warning is shown when TETR.IO is out of focus.";
|
|
default = true;
|
|
};
|
|
action_text = mkOption {
|
|
type = types.enum [
|
|
"off"
|
|
"some"
|
|
"all"
|
|
];
|
|
description = "Determines how much action text to display when performing special attacks.";
|
|
default = "some";
|
|
};
|
|
board_bounciness = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 200;
|
|
description = "How much (%) the board reacts when you move pieces around.";
|
|
default = 40;
|
|
};
|
|
damage_shakiness = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 110;
|
|
description = "How much (%) the board reacts when you receive damage.";
|
|
default = 100;
|
|
};
|
|
grid_opacity = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 110;
|
|
description = "How visible the grid is (%). 0% makes the grid invisible.";
|
|
default = 10;
|
|
};
|
|
board_opacity = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 110;
|
|
description = "How visible the board is (%). 0% makes the board invisible.";
|
|
default = 85;
|
|
};
|
|
shadow_opacity = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 110;
|
|
description = "How visible the shadow piece is (%). 0% makes the shadow piece invisible.";
|
|
default = 15;
|
|
};
|
|
background_opacity = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 100;
|
|
description = "How visible the background images are. 0% makes the background entirely black.";
|
|
default = 5;
|
|
};
|
|
show_background_in_menus = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If enabled, do not show the background when in menus. This will majorly improve performance in menus, but you will no longer see the background.";
|
|
default = false;
|
|
};
|
|
board_zoom = mkOption {
|
|
type = with types; either str number;
|
|
apply = x: percentage x 0 110;
|
|
description = "How large the board displays. Values over 100% may cause some elements to not be visible.";
|
|
default = 100;
|
|
};
|
|
spin_board = mkOption {
|
|
type = types.bool;
|
|
description = "When enabled, the board reacts to T-Spins by rotating a little with it.";
|
|
default = true;
|
|
};
|
|
fire_meter = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, fill a fire meter when doing well, illuminating boards that have a lot of fire.";
|
|
default = true;
|
|
};
|
|
danger_warning = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, makes the board red and play a warning sound when you're in danger.";
|
|
default = true;
|
|
};
|
|
colored_shadow = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, colors the shadow piece.";
|
|
default = true;
|
|
};
|
|
color_locked_hold_piece = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, the HOLD piece will be grayed out if it may not be used.";
|
|
default = true;
|
|
};
|
|
|
|
};
|
|
multiplayer = {
|
|
chat = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If disabled, chat will be hidden when ingame.";
|
|
default = true;
|
|
};
|
|
filter = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, profanity in chat will be filtered. Note that such filters are never perfect.";
|
|
default = true;
|
|
};
|
|
show_emotes = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show emotes in chat.";
|
|
default = true;
|
|
};
|
|
show_animated_emotes = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show animated emotes in chat.";
|
|
default = true;
|
|
};
|
|
invert = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, chat text shows in black (good for light backgrounds).";
|
|
default = false;
|
|
};
|
|
darken = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show a background behind chat messages when typing.";
|
|
default = true;
|
|
};
|
|
};
|
|
notifications =
|
|
let
|
|
notification.optional = types.enum [
|
|
"off"
|
|
"in-game"
|
|
"desktop"
|
|
];
|
|
notification.required = types.enum [
|
|
"in-game"
|
|
"desktop"
|
|
];
|
|
apply =
|
|
x:
|
|
{
|
|
"off" = "off";
|
|
"in-game" = "ingame";
|
|
"desktop" = "both";
|
|
}
|
|
."${x}";
|
|
in
|
|
{
|
|
when = {
|
|
friend_online = mkOption {
|
|
type = notification.optional;
|
|
inherit apply;
|
|
description = "Notify me when a friend goes online";
|
|
default = "in-game";
|
|
};
|
|
friend_offline = mkOption {
|
|
type = notification.optional;
|
|
inherit apply;
|
|
description = "Notify me when a friend goes offline";
|
|
default = "off";
|
|
};
|
|
friend_dm_recieved = mkOption {
|
|
type = notification.optional;
|
|
inherit apply;
|
|
description = "Notify me when a friend sends me a direct message";
|
|
default = "desktop";
|
|
};
|
|
dm_recieved = mkOption {
|
|
type = notification.optional;
|
|
inherit apply;
|
|
description = "Notify me when a non-friend sends me a direct message";
|
|
default = "desktop";
|
|
};
|
|
room_invite = mkOption {
|
|
type = notification.required;
|
|
inherit apply;
|
|
description = "Notify me when someone invites me to a room";
|
|
default = "desktop";
|
|
};
|
|
other = mkOption {
|
|
type = notification.required;
|
|
inherit apply;
|
|
description = "Other notifications";
|
|
default = "desktop";
|
|
};
|
|
};
|
|
enable_desktop_notifications = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show notifications outside of the game.";
|
|
default = true;
|
|
};
|
|
suppress_while_playing = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, unimportant notifications don't show ingame, but will be shown after the game.";
|
|
default = false;
|
|
};
|
|
full_volume = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, notification sounds always play at full volume.";
|
|
default = true;
|
|
};
|
|
};
|
|
hide_room_ids = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, room IDs will not be shown, to protect you from streamsniping.";
|
|
default = false;
|
|
};
|
|
show_network_warnings = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If disabled, network warning icons will not be shown.";
|
|
default = true;
|
|
};
|
|
notify_elim = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show a popup when you KO someone or get KO'd.";
|
|
default = true;
|
|
};
|
|
duels_side_by_side = mkOption {
|
|
type = types.bool;
|
|
description = "Whether to display a duel side-by-side. This causes your own board to move slightly to the left.";
|
|
default = true;
|
|
};
|
|
simplify_thumbnails = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, always shows the simpler thumbnails for other players. This increases performance in games between 2 and 8 players, but looks less nice.";
|
|
default = false;
|
|
};
|
|
animate_super_lobby_background = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If enabled, the background of Super Lobbies (rooms of 100+ players) animate.";
|
|
default = true;
|
|
};
|
|
animate_background_in_quickplay = mkOption {
|
|
type = types.bool;
|
|
apply = x: !x;
|
|
description = "If enabled, the background of Quick Play animate.";
|
|
default = true;
|
|
};
|
|
welcome_guide = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show the simple guide with the keybinds in multiplayer lobbies.";
|
|
default = true;
|
|
};
|
|
};
|
|
keep_replay_tools_open = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, the replay tools do not collapse when you're not using them.";
|
|
default = false;
|
|
};
|
|
skip_login_screen = mkOption {
|
|
type = types.enum [
|
|
"never"
|
|
"by-url"
|
|
"always"
|
|
];
|
|
apply =
|
|
x:
|
|
{
|
|
"never" = "never";
|
|
"by-url" = "quickjoin";
|
|
"always" = "always";
|
|
}
|
|
."${x}";
|
|
description = "When to skip the login screen (\"by-url\": only when joining matches or viewing replays by url)";
|
|
default = "always";
|
|
};
|
|
discord_rpc = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, show your current activity in Discord.";
|
|
default = true;
|
|
};
|
|
flash_taskbar_icon = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, the taskbar icon will flash when something important happens.";
|
|
default = true;
|
|
};
|
|
devtools = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, allow opening the developer toolbox.";
|
|
default = false;
|
|
};
|
|
advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable = mkOption {
|
|
type = types.bool;
|
|
description = "If enabled, most/all third-party ads will not show.";
|
|
default = false;
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable (
|
|
lib.mkMerge [
|
|
{
|
|
home.packages = [
|
|
(cfg.package.override {
|
|
withTetrioPlus = cfg.plus.enable;
|
|
tetrio-plus = cfg.plus.package;
|
|
})
|
|
];
|
|
|
|
home.initialFile.".config/tetrio-desktop/Local Storage/leveldb" = {
|
|
mode = "0644";
|
|
source = pkgs.callPackage ./leveldb.nix {
|
|
value = {
|
|
handling =
|
|
let
|
|
set = cfg.settings.handling;
|
|
in
|
|
{
|
|
arr = set.auto_repeat_rate;
|
|
das = set.delayed_auto_shift;
|
|
dcd = set.das_cut_delay;
|
|
sdf = set.soft_drop_factor;
|
|
safelock = set.prevent_hard_drops;
|
|
cancel = set.direction_cancel_das;
|
|
may20g = set.soft_drop_over_movement;
|
|
irs = set.rotation_buffering;
|
|
ihs = set.hold_buffering;
|
|
};
|
|
volume =
|
|
let
|
|
set = cfg.settings.audio;
|
|
in
|
|
{
|
|
music = set.music.volume;
|
|
bgmtweak = set.music.preferences;
|
|
sfx = set.sfx.volume;
|
|
stereo = set.stereo;
|
|
scrollable = set.scroll_adjust_volume;
|
|
oof = set.mute_unfocused;
|
|
next = set.sfx.next_pieces;
|
|
others = set.sfx.other_players;
|
|
attacks = set.sfx.attacks;
|
|
zenithrank = set.sfx.speed_changes;
|
|
disable = set.disable;
|
|
};
|
|
video =
|
|
let
|
|
set = cfg.settings.visual;
|
|
mul = cfg.settings.multiplayer;
|
|
in
|
|
{
|
|
actionText = set.action_text;
|
|
bounciness = set.board_bounciness;
|
|
shakiness = set.damage_shakiness;
|
|
gridopacity = set.grid_opacity;
|
|
boardopacity = set.board_opacity;
|
|
shadowopacity = set.shadow_opacity;
|
|
zoom = set.board_zoom;
|
|
sidebyside = mul.duels_side_by_side;
|
|
spin = set.spin_board;
|
|
kos = mul.notify_elim;
|
|
siren = set.danger_warning;
|
|
colorshadow = set.colored_shadow;
|
|
|
|
graphics = set.graphics.tier;
|
|
caching = set.graphics.cache;
|
|
webgl = set.graphics.pipeline;
|
|
particles = set.graphics.particle_count;
|
|
background = set.background_opacity;
|
|
powersave = set.graphics.powersave;
|
|
lowres = set.graphics.low_resolution;
|
|
lowrescounters = set.graphics.low_precision_counters;
|
|
alwaystiny = mul.simplify_thumbnails;
|
|
nosuperlobbyanim = mul.animate_super_lobby_background;
|
|
nozenithanim = mul.animate_background_in_quickplay;
|
|
nobg = set.show_background_in_menus;
|
|
chatfilter = mul.chat.filter;
|
|
nochat = mul.chat.enable;
|
|
hideroomids = mul.hide_room_ids;
|
|
emotes = mul.chat.show_emotes;
|
|
emotes_anim = mul.chat.show_animated_emotes;
|
|
invert = mul.chat.invert;
|
|
chatbg = mul.chat.darken;
|
|
replaytoolsnocollapse = cfg.settings.keep_replay_tools_open;
|
|
hidenetwork = mul.show_network_warnings;
|
|
focuswarning = set.show_unfocus_warning;
|
|
guide = mul.welcome_guide;
|
|
|
|
desktopnotifications = mul.notifications.enable_desktop_notifications;
|
|
};
|
|
notifications =
|
|
let
|
|
set = cfg.settings.multiplayer.notifications;
|
|
in
|
|
{
|
|
suppress = set.suppress_while_playing;
|
|
forcesound = set.full_volume;
|
|
online = set.when.friend_online;
|
|
offline = set.when.friend_offline;
|
|
dm = set.when.friend_dm_recieved;
|
|
dm_pending = set.when.dm_recieved;
|
|
invite = set.when.room_invite;
|
|
other = set.when.other;
|
|
};
|
|
electron = {
|
|
loginskip = cfg.settings.skip_login_screen;
|
|
adblock =
|
|
cfg.settings.advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable;
|
|
taskbarflash = cfg.settings.flash_taskbar_icon;
|
|
autoupdate = false;
|
|
anglecompat = false;
|
|
presence = cfg.settings.discord_rpc;
|
|
devtools = cfg.settings.devtools;
|
|
};
|
|
};
|
|
};
|
|
recursive = true;
|
|
};
|
|
}
|
|
(mkIf (cfg.plus.enable) {
|
|
home.file.".config/tetrio-desktop/tetrioplus/tpkey-tetrioPlusEnabled.json" = {
|
|
text = ''
|
|
{ "value": true }
|
|
'';
|
|
force = true;
|
|
};
|
|
home.file.".config/tetrio-desktop/tetrioplus/tpkey-hideTetrioPlusOnStartup.json" = {
|
|
text = ''
|
|
{ "value": ${lib.trivial.boolToString cfg.plus.hideOnStartup} }
|
|
'';
|
|
force = true;
|
|
};
|
|
})
|
|
(mkIf (cfg.plus.enable && cfg.plus ? skin) {
|
|
home.file.".config/tetrio-desktop/tetrioplus/tpkey-skin.json" = {
|
|
source = cfg.plus.skin.package;
|
|
force = true;
|
|
};
|
|
home.file.".config/tetrio-desktop/tetrioplus/tpkey-forceNearestScaling.json" = {
|
|
text = ''
|
|
{ "value": ${lib.trivial.boolToString cfg.plus.skin.nearest} }
|
|
'';
|
|
force = true;
|
|
};
|
|
})
|
|
]
|
|
);
|
|
}
|