Compare commits

...

2 commits

Author SHA1 Message Date
5683fa2b48
update some READMEs 2024-10-06 21:31:02 +02:00
9427af1efe
use central build.zig for scripts and plugins 2024-10-06 21:26:07 +02:00
23 changed files with 140 additions and 433 deletions

View file

@ -2,12 +2,12 @@
I'm actually making the claim now that this repo might just be the most hilariously complicated configuration in existance.
This is my system configuration, built mostly for arch & SystemD based linuxes.
This is my system configuration, built mostly for NixOS & SystemD based Linuces.
Here's some useful facts:
- Config files are generated using confgen, my config file template engine. Options that can be changed are in cg_opts.lua. This makes for a centralized place for common options like fonts. This allows for complete deduplication.
- The neovim config is written in part Zig (yes, really) and part fennel. Use `./setup.rkt setup-nvim-config` to build and install it.
- Lua/Fennel files in the nvim config are compiled to lua bytecode.
- The Neovim config is written in part Zig (yes, really) and part fennel. Use `./setup.rkt setup-nvim-config` to build and install it.
- Lua/Fennel files in the nvim config are compiled to Lua bytecode.
- Theres a `setup.rkt` racket script with convenient functions such as installing scripts, building the config and setting up the neovim configuration.
- I have a lot of scripts, written in Zig, Racket and some in shell.

35
build.zig Normal file
View file

@ -0,0 +1,35 @@
const std = @import("std");
const inst = @import("setup/installers.zig");
const Options = @import("setup/Options.zig");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const zargs = .{ .target = target, .optimize = optimize };
const opts = try Options.parseConfig(b.allocator);
// Symlink Scripts
inst.addScript(b, opts, "brightness.rkt", "brightness");
inst.addScript(b, opts, "map-touch-display.rkt", "map-touch-display");
inst.addScript(b, opts, "typstwatch.sh", "typstwatch");
inst.addScript(b, opts, "videos-duration.sh", "videos-duration");
// Scripts
inst.addZBuild(b, opts, zargs, "hyprtool");
inst.addZBuild(b, opts, zargs, "mzteinit");
inst.addZBuild(b, opts, zargs, "mzteriver");
inst.addZBuild(b, opts, zargs, "openbrowser");
inst.addZBuild(b, opts, zargs, "playvid");
inst.addZBuild(b, opts, zargs, "prompt");
inst.addZBuild(b, opts, zargs, "randomwallpaper");
inst.addZBuild(b, opts, zargs, "vinput");
inst.addZBuild(b, opts, zargs, "withjava");
inst.addZBuild(b, opts, zargs, "wlbg");
// Plugins
inst.addZBuild(b, opts, zargs, "mzte-mpv");
}

28
build.zig.zon Normal file
View file

@ -0,0 +1,28 @@
.{
.name = "dotfiles",
.version = "0.0.0",
.paths = .{""},
.dependencies = .{
// Setup Deps
.ziggy = .{
.url = "git+https://github.com/kristoff-it/ziggy.git#42b6f5d7320340bc5903c4c29d34065e8517a549",
.hash = "12200b69baf035d0474e04b131862d36b2657fbce88a5c9dc657af861430e5fc7e3d",
},
// Scripts
.hyprtool = .{ .path = "scripts/hyprtool" },
.mzteinit = .{ .path = "scripts/mzteinit" },
.mzteriver = .{ .path = "scripts/mzteriver" },
.openbrowser = .{ .path = "scripts/openbrowser" },
.playvid = .{ .path = "scripts/playvid" },
.prompt = .{ .path = "scripts/prompt" },
.randomwallpaper = .{ .path = "scripts/randomwallpaper" },
.vinput = .{ .path = "scripts/vinput" },
.withjava = .{ .path = "scripts/withjava" },
.wlbg = .{ .path = "scripts/wlbg" },
// Plugins
.@"mzte-mpv" = .{ .path = "plugins/mzte-mpv" },
},
}

View file

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1727148303,
"narHash": "sha256-T/TuyfNci3PBKm2nUxpr+4hSq4bVuWBHqNp1Nthp83g=",
"lastModified": 1728228884,
"narHash": "sha256-E9JaDKGi21oUypH0P9881lbkhi6USNJ6XL2tFzU5uuE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "eae30cdaac7a5235e427eb0d85104f54ddea3fbd",
"rev": "ff0da78cfd41aa1784910ce1fea89119822013ce",
"type": "github"
},
"original": {

View file

@ -14,4 +14,8 @@
:ros :commonlisp
;; Haxe
:hx :haxe
:hxml :hxml}})
:hxml :hxml
;; Ziggy
:ziggy :ziggy
:ziggy-schema :ziggy_schema}})

View file

@ -1,6 +1,5 @@
(local (mztenv configs parsers ts-utils)
(values (require :mzte_nv)
(require :nvim-treesitter.configs)
(values (require :mzte_nv) (require :nvim-treesitter.configs)
(require :nvim-treesitter.parsers)
(require :nvim-treesitter.ts_utils)))

View file

@ -18,12 +18,12 @@
(("comptime" @keyword) (#set! conceal "󰟾"))
;; Functions
((BUILTINIDENTIFIER) @include
((builtin_identifier) @include
(#any-of? @include "@import" "@cImport")
(#set! conceal ""))
;; Common Variables
(((IDENTIFIER) @variable
(((identifier) @variable
(#eq? @variable "self"))
(#set! conceal ""))

View file

@ -7,7 +7,6 @@ let
(pkgs.linkFarm "clang-nvim" (map
(bin: { name = "bin/${bin}"; path = "${clang-tools}/bin/${bin}"; })
[ "clangd" "clang-format" ])) # Don't include everything from clang-tools
elixir-ls
(pkgs.stdenv.mkDerivation {
name = "glsl-analyzer";
src = pkgs.fetchFromGitHub {

View file

@ -1,2 +0,0 @@
/target
/Cargo.lock

View file

@ -1,15 +0,0 @@
[package]
name = "i3status"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.71"
env_logger = "0.10.0"
i3status-rs = { git = "https://github.com/greshake/i3status-rust.git" }
serde_json = "1.0.96"
tokio = "1.28.1"
toml = "0.7.4"
unicode-segmentation = "1.10.1"

View file

@ -1,125 +0,0 @@
# Material from NerdFont
# https://www.nerdfonts.com/cheat-sheet
backlight = [
"\ue38d", # nf-weather-moon_new
"\ue3d4", # nf-weather-moon_alt_waxing_gibbous_6
"\ue3d3", # nf-weather-moon_alt_waxing_gibbous_5
"\ue3d2", # nf-weather-moon_alt_waxing_gibbous_4
"\ue3d1", # nf-weather-moon_alt_waxing_gibbous_3
"\ue3d0", # nf-weather-moon_alt_waxing_gibbous_2
"\ue3cf", # nf-weather-moon_alt_waxing_gibbous_1
"\ue3ce", # nf-weather-moon_alt_first_quarter
"\ue3cd", # nf-weather-moon_alt_waxing_crescent_6
"\ue3cc", # nf-weather-moon_alt_waxing_crescent_5
"\ue3cb", # nf-weather-moon_alt_waxing_crescent_4
"\ue3ca", # nf-weather-moon_alt_waxing_crescent_3
"\ue3c9", # nf-weather-moon_alt_waxing_crescent_2
"\ue3c8", # nf-weather-moon_alt_waxing_crescent_1
"\ue39b", # nf-weather-moon_full
]
bat_charging = "\U000f0084" # nf-md-battery_charging
bat_not_available = "\U000f0091" # nf-md-battery_unknown
bat = [
"\U000f007a", # nf-md-battery_10
"\U000f007b", # nf-md-battery_20
"\U000f007c", # nf-md-battery_30
"\U000f007d", # nf-md-battery_40
"\U000f007e", # nf-md-battery_50
"\U000f007f", # nf-md-battery_60
"\U000f0080", # nf-md-battery_70
"\U000f0081", # nf-md-battery_80
"\U000f0082", # nf-md-battery_90
"\U000f0079", # nf-md-battery
]
bell = "\U000f009c" # nf-md-bell_outline
bell-slash = "\U000f009b" # nf-md-bell_off
bluetooth = "\U000f00af" # nf-md-bluetooth
calendar = "\U000f00ed" # nf-md-calendar
cogs = "\U000f0493" # nf-md-cog
cpu = [
"\U000F0F86", # nf-md-speedometer_slow
"\U000F0F85", # nf-md-speedometer_medium
"\U000F04C5", # nf-md-speedometer
]
cpu_boost_on = "\U000f0521" # nf-md-toggle_switch
cpu_boost_off = "\U000f0a19" # nf-md-toggle_switch_off_outline
disk_drive = "\U000f02ca" # nf-md-harddisk
docker = "\uf308" # nf-linux-docker
github = "\U000f02a4" # nf-md-github
gpu = "\U000f0379" # nf-md-monitor
headphones = "\U000f02cb" # nf-md-headphones
joystick = "\U000f0297" # nf-md-gamepad_variant
keyboard = "\U000f030c" # nf-md-keyboard
mail = "\U000f01ee" # nf-md-email
memory_mem = "\U000f035b" # nf-md-memory
memory_swap = "\U000f02ca" # nf-md-harddisk
mouse = "\U000f037d" # nf-md-mouse
music = "\U000f075a" # nf-md-music
music_next = "\U000f04ad" # nf-md-skip_next
music_pause = "\U000f03e4" # nf-md-pause
music_play = "\U000f040a" # nf-md-play
music_prev = "\U000f04ae" # nf-md-skip_previous
net_bridge = "\U000f04aa" # nf-md-sitemap
net_down = "\U000f01da" # nf-md-download
net_loopback = "\U000f006f" # nf-md-backup_restore
net_modem = "\U000f03f2" # nf-md-phone
net_cellular = [
"\U000F08FD", # nf-md-network_strength_off_outline
"\U000F08FE", # nf-md-network_strength_outline
"\U000F08F4", # nf-md-network_strength_1
"\U000F08F6", # nf-md-network_strength_2
"\U000F08F8", # nf-md-network_strength_3
"\U000F08FA", # nf-md-network_strength_4
]
net_up = "\U000f0552" # nf-md-upload
net_vpn = "\U000f0582" # nf-md-vpn
net_wired = "\U000f0200" # nf-md-ethernet
net_wireless = [
"\U000F092F", # nf-md-wifi_strength_outline
"\U000F091F", # nf-md-wifi_strength_1
"\U000F0922", # nf-md-wifi_strength_2
"\U000F0925", # nf-md-wifi_strength_3
"\U000F0928", # nf-md-wifi_strength_4
]
notification = "\U000f009c" # nf-md-bell_outline
phone = "\U000f03f2" # nf-md-phone
phone_disconnected = "\U000f0658" # nf-md-phone_minus
ping = "\U000f051f" # nf-md-timer_sand
pomodoro = "\ue001" # nf-pom-pomodoro_done
pomodoro_break = "\U000f0176" # nf-md-coffee
pomodoro_paused = "\U000f03e4" # nf-md-pause
pomodoro_started = "\U000f040a" # nf-md-play
pomodoro_stopped = "\U000f04db" # nf-md-stop
resolution = "\U000f0293" # nf-md-fullscreen
tasks = "\U000f05c7" # nf-md-playlist_check
tea = "\U000f0d9e" # nf-md-tea
thermometer = [
"\U000f10c3", # nf-md-thermometer_low
"\U000f050f", # nf-md-thermometer
"\U000f10c2", # nf-md-thermometer_high
]
time = "\U000f0150" # nf-md-clock_outline
toggle_off = "\U000f0a19" # nf-md-toggle_switch_off_outline
toggle_on = "\U000f0521" # nf-md-toggle_switch
unknown = "\U000f0186" # nf-md-comment_question_outline | TODO: Make default?
update = "\U000f03d5" # nf-md-package_up
uptime = "\U000f0153" # nf-md-clock_in
volume_muted = "\U000f075f" # nf-md-volume_mute
volume = [
"\U000f057f", # nf-md-volume_low
"\U000f0580", # nf-md-volume_medium
"\U000f057e", # nf-md-volume_high
]
microphone_muted = "\U000f036d" # nf-md-microphone_off
microphone = [
"\U000f036e", # nf-md-microphone_outline
"\U000f036c", # nf-md-microphone
"\U000f036c", # nf-md-microphone
]
weather_clouds = "\ue33d" # nf-weather-cloud
weather_default = "\ue33d" # Cloud symbol as default
weather_rain = "\ue371" # nf-weather-raindrop
weather_snow = "\ue36f" # nf-weather-snowflake_cold
weather_sun = "\ue30d" # nf-weather-day_sunny
weather_thunder = "\ue31d" # nf-weather-thunderstorm
xrandr = "\U000f037a" # nf-md-monitor_multiple

View file

@ -1,18 +0,0 @@
use i3status_rs::themes::color::{Color, Rgba};
const fn c(r: u8, g: u8, b: u8) -> Color {
Color::Rgba(Rgba { r, g, b, a: 0xff })
}
pub const BASE: Color = c(30, 30, 46);
pub const MANTLE: Color = c(24, 24, 37);
//pub const CRUST: Color = c(17, 17, 27);
pub const SURFACE: [Color; 3] = [c(49, 50, 68), c(69, 71, 90), c(88, 91, 112)];
pub const TEXT: Color = c(205, 214, 244);
pub const RED: Color = c(243, 166, 168);
pub const PEACH: Color = c(250, 179, 172);
pub const GREEN: Color = c(166, 227, 161);
pub const BLUE: Color = c(137, 220, 235);

View file

@ -1,22 +0,0 @@
//! Utilities to determine the GPU the system is running.
pub enum Type {
NVidia,
Amd,
Unknown,
}
impl Type {
pub fn get() -> anyhow::Result<Self> {
if std::fs::try_exists("/sys/module/nvidia")? {
return Ok(Type::NVidia);
}
// TODO: is it called amdgpu?
if std::fs::try_exists("/sys/module/amdgpu")? {
return Ok(Type::Amd);
}
Ok(Type::Unknown)
}
}

View file

@ -1,165 +0,0 @@
#![warn(clippy::pedantic)]
#![feature(fs_try_exists)]
use std::sync::Arc;
use unicode_segmentation::UnicodeSegmentation;
use i3status_rs::{
blocks,
config::{BlockConfigEntry, SharedConfig},
escape::CollectEscaped,
icons::Icons,
protocol,
themes::{
color::{Color, Rgba},
separator::Separator,
Theme,
},
widget::{State, Widget},
BarState,
};
mod catppuccin;
mod gpu;
#[tokio::main(flavor = "current_thread")]
async fn main() {
if let Err(e) = try_main().await {
let err_widget = Widget::new()
.with_text(e.to_string().graphemes(true).collect_pango_escaped())
.with_state(State::Critical);
serde_json::to_writer(
std::io::stdout(),
&err_widget.get_data(&SharedConfig::default(), 0).unwrap(),
)
.unwrap();
println!(",");
eprintln!("{e}");
std::future::pending::<()>().await;
}
}
async fn try_main() -> anyhow::Result<()> {
env_logger::try_init()?;
protocol::init(false);
let icons = Icons(toml::from_str(include_str!("../assets/material-nf.toml"))?);
let theme = Theme {
// catppuccin theme
idle_bg: catppuccin::MANTLE,
idle_fg: catppuccin::TEXT,
info_bg: catppuccin::BLUE,
info_fg: catppuccin::SURFACE[0],
good_bg: catppuccin::GREEN,
good_fg: catppuccin::BASE,
warning_bg: catppuccin::PEACH,
warning_fg: catppuccin::SURFACE[2],
critical_bg: catppuccin::RED,
critical_fg: catppuccin::SURFACE[1],
separator: Separator::Custom("\u{e0b2}".to_string()),
separator_bg: Color::Auto,
separator_fg: Color::Auto,
alternating_tint_bg: Color::Rgba(Rgba {
r: 0x11,
g: 0x11,
b: 0x11,
a: 0x00,
}),
alternating_tint_fg: Color::Auto,
end_separator: Separator::Native,
};
let mut bar = BarState::new(i3status_rs::config::Config {
shared: SharedConfig {
theme: Arc::new(theme),
icons: Arc::new(icons),
..Default::default()
},
..Default::default()
});
spawn_blocks(&mut bar).await?;
bar.run_event_loop(|| panic!("Hey! No restarting!")).await?;
Ok(())
}
#[allow(clippy::needless_pass_by_ref_mut)] // clippy is to stupid to reason about my macro
async fn spawn_blocks(bar: &mut BarState) -> anyhow::Result<()> {
macro_rules! spawn {
($mod:ident $structinit:tt) => {
bar.spawn_block(BlockConfigEntry {
config: blocks::BlockConfig::$mod(blocks::$mod::Config $structinit),
common: Default::default(),
})
.await?;
};
}
spawn!(memory {
format: " $icon $mem_used_percents $mem_used/$mem_avail ".parse()?,
format_alt: Some(" $icon $swap_used_percents $swap_used/$swap_free ".parse()?),
..Default::default()
});
spawn!(cpu {
interval: 1.into(),
format: " $icon $frequency $barchart $utilization ".parse()?,
..Default::default()
});
spawn!(temperature {
interval: 5.into(),
format: " $icon $min - $average ~ $max + ".parse()?,
chip: Some("*-isa-*".into()),
idle: Some(40.),
info: Some(65.),
warning: Some(80.),
..Default::default()
});
match gpu::Type::get()? {
gpu::Type::NVidia => {
spawn!(nvidia_gpu {
format: " $icon $utilization $memory $temperature $fan_speed $power ".parse()?,
..Default::default()
});
},
gpu::Type::Amd => {
spawn!(amd_gpu {
format: " $icon $utilization $vram_used_percents ".parse()?,
..Default::default()
});
},
gpu::Type::Unknown => {}, // no GPU block
}
spawn!(music {
interface_name_exclude: vec![".*kdeconnect.*".to_string(), "mpd".to_string()],
format: " $icon {$combo.str(max_w:20, rot_interval:0.1) $prev $play $next |}".parse()?,
..Default::default()
});
spawn!(sound {
format: " $icon $output_description{ $volume|} ".parse()?,
..Default::default()
});
spawn!(battery {
interval: 10.into(),
format: " $icon $percentage $power ".parse()?,
missing_format: "".parse()?,
..Default::default()
});
spawn!(time {
interval: 1.into(),
format: " $icon $timestamp.datetime(f:'%a %d.%m.%Y %T') ".parse()?,
..Default::default()
});
Ok(())
}

View file

@ -1,41 +0,0 @@
#!/usr/bin/env -S ros -Q --
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
(ros:ensure-asdf)
#+quicklisp(ql:quickload '(uiop) :silent t))
(defpackage :launchmenu
(:use :cl))
(in-package :launchmenu)
(defun write-app (stream icon layout programs)
(format stream "IMG:~a~C~s~%" icon #\Tab (cons layout programs)))
(defun write-commands (stream)
(write-app stream "scalable/www-browser.svg" nil '(("openbrowser")))
(write-app stream "scalable/irc-chat.svg" "chat" '(("nheko") ("discord")))
(write-app stream "scalable/system-file-manager.svg" "double-fileman" '(("thunar") ("thunar"))))
(defun main (&rest argv)
(declare (ignorable argv))
(destructuring-bind (layout . programs) (uiop:run-program '("pmenu")
:output #'read
:error-output :interactive
:input #'write-commands)
(when layout
(uiop:run-program (list "i3-msg" (format
nil
"append_layout ~a/.local/share/i3-layouts/~a.json"
(user-homedir-pathname)
layout))
:output :interactive
:error-output :interactive
:input :interactive))
(dolist (program programs)
(uiop:launch-program program
:output :interactive
:error-output :interactive
:input :interactive))))

View file

@ -57,7 +57,7 @@ pub fn build(b: *std.Build) !void {
exe.root_module.addImport("opts", opts.createModule());
exe.root_module.addImport("wayland", b.createModule(.{ .root_source_file = scanner.result }));
scanner.addCustomProtocol("river-control-unstable-v1.xml");
scanner.addCustomProtocol(b.pathFromRoot("river-control-unstable-v1.xml"));
scanner.generate("zriver_control_v1", 1);
scanner.generate("wl_seat", 7);

View file

@ -22,7 +22,7 @@ pub fn build(b: *std.Build) void {
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
scanner.addSystemProtocol("unstable/xdg-output/xdg-output-unstable-v1.xml");
scanner.addCustomProtocol("wlr-layer-shell-unstable-v1.xml");
scanner.addCustomProtocol(b.pathFromRoot("wlr-layer-shell-unstable-v1.xml"));
scanner.generate("wl_compositor", 5);
scanner.generate("wl_shm", 1);

View file

@ -38,9 +38,6 @@
['install-scripts
(local-require "setup/commands/install-scripts.rkt")
(run)]
['install-plugins
(local-require "setup/commands/install-plugins.rkt")
(run)]
['setup-nvim-config
(local-require "setup/commands/setup-nvim-config.rkt")
(run)]

41
setup/Options.zig Normal file
View file

@ -0,0 +1,41 @@
const std = @import("std");
const ziggy = @import("ziggy");
ignored_scripts: []const []const u8 = &.{},
const Options = @This();
pub fn parseConfig(arena: std.mem.Allocator) !Options {
const opts_path = try std.fs.path.join(arena, &.{
std.posix.getenv("HOME") orelse return error.NoHome,
".config/mzte_localconf/setup-opts.ziggy",
});
if (std.fs.cwd().readFileAllocOptions(
arena,
opts_path,
1024 * 1024,
null,
@alignOf(u8),
0,
)) |opts_data| {
var diag: ziggy.Diagnostic = .{ .path = opts_path };
return ziggy.parseLeaky(Options, arena, opts_data, .{
.diagnostic = &diag,
.copy_strings = .to_unescape,
}) catch |e| {
std.log.err("failed to parse opts:\n{}", .{diag});
return e;
};
} else |e| {
std.log.warn("Couldn't read options: {}", .{e});
return .{};
}
}
pub fn isBlacklisted(self: Options, script: []const u8) bool {
for (self.ignored_scripts) |s| {
if (std.mem.eql(u8, s, script)) return true;
}
return false;
}

View file

@ -1 +1 @@
Racket code for installing my dotfiles.
Racket code for installing my dotfiles as well as some modules for the build.zig file.

View file

@ -1,7 +0,0 @@
#lang racket
(require "../common.rkt")
(provide run)
(define (run)
(install-zig "plugins/mzte-mpv")
(build-haxe "plugins/tampermonkey-mzte"))

View file

@ -8,24 +8,5 @@
(generate-cgopt-json)
;; Symlink interpreted scripts
(install-link "scripts/brightness.rkt" (bin-path "brightness"))
(install-link "scripts/map-touch-display.rkt" (bin-path "map-touch-display"))
(install-link "scripts/typstwatch.sh" (bin-path "typstwatch"))
(install-link "scripts/videos-duration.sh" (bin-path "videos-duration"))
;; Compiled scripts
(install-zig "scripts/hyprtool")
(install-rust "scripts/i3status")
(install-zig "scripts/mzteinit")
(install-zig "scripts/mzteriver")
(install-zig "scripts/openbrowser")
(install-zig "scripts/playvid")
(install-zig "scripts/prompt")
(install-zig "scripts/randomwallpaper")
(install-zig "scripts/vinput")
(install-zig "scripts/withjava")
(install-zig "scripts/wlbg")
(install-roswell "scripts/launchmenu.ros")
(cmd "zig" "build" "-Doptimize=ReleaseFast" "-p" (output-bin-path))
null)

18
setup/installers.zig Normal file
View file

@ -0,0 +1,18 @@
const std = @import("std");
const Options = @import("Options.zig");
pub fn addScript(b: *std.Build, opts: Options, src: []const u8, dest: []const u8) void {
if (opts.isBlacklisted(dest)) return;
b.installFile(
b.fmt("scripts/{s}", .{src}),
b.fmt("bin/{s}", .{dest}),
);
}
pub fn addZBuild(b: *std.Build, opts: Options, args: anytype, dep: []const u8) void {
if (opts.isBlacklisted(dep)) return;
for (b.dependency(dep, args).builder.install_tls.step.dependencies.items) |d| {
b.installArtifact((d.cast(std.Build.Step.InstallArtifact) orelse continue).artifact);
}
}