diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.github/mkdocs.yml b/.github/mkdocs.yml index c8f530e9..b0453a8e 100644 --- a/.github/mkdocs.yml +++ b/.github/mkdocs.yml @@ -5,7 +5,9 @@ docs_dir: '../docs' nav: - Introduction: 'index.md' - User Guide: 'userguide.md' - - Contributing Guide: 'contribute.md' + - Contributing: + - Contributing Guide: 'contribute.md' + - Roadmap: 'roadmap.md' - Documentation: - Known Issues: 'KnownIssues.md' - FAQ: 'faq.md' diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 7366e78c..da696558 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,5 +1,3 @@ -name-template: '$RESOLVED_VERSION' -tag-template: '$RESOLVED_VERSION' tag-prefix: "" categories: - title: '🚀 Features' @@ -21,10 +19,6 @@ template: | $CHANGES - ## Contributors - - $CONTRIBUTORS - change-title-escapes: '\<*_&"''' autolabeler: - label: 'documentation' @@ -57,4 +51,6 @@ replacers: - search: /`/g replace: '' exclude-labels: - - 'skip-changelog' \ No newline at end of file + - 'skip-changelog' + +filter-by-commitish: true \ No newline at end of file diff --git a/.github/workflows/cargo-lock.yml b/.github/workflows/cargo-lock.yml deleted file mode 100644 index 40c37209..00000000 --- a/.github/workflows/cargo-lock.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Remove Cargo.lock changes - -on: - pull_request: - types: [opened, synchronize] - paths: - - 'Cargo.lock' - -jobs: - remove-cargo-lock: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Remove Cargo.lock changes - run: | - git config user.name github-actions - git config user.email github-actions@github.com - git checkout ${{ github.head_ref }} - git reset origin/${{ github.base_ref }} -- Cargo.lock - git commit -m "Remove changes to Cargo.lock" || echo "No changes to commit" - git push origin ${{ github.head_ref }} --force diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 75bf243d..0bd2d476 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -3,10 +3,11 @@ name: GitHub Pages Deploy on: push: paths: - - 'mkdocs.yml' + - '.github/mkdocs.yml' + - '.github/requirements.txt' - 'docs/**' - 'overrides/**' - - 'CONTRIBUTING.md' + - '.github/CONTRIBUTING.md' workflow_dispatch: jobs: @@ -29,4 +30,6 @@ jobs: run: pip install -r .github/requirements.txt - name: Build & Deploy using mkdocs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: mkdocs gh-deploy --force -f .github/mkdocs.yml diff --git a/.github/workflows/linutil.yml b/.github/workflows/linutil.yml index d173b966..ef6b2c10 100644 --- a/.github/workflows/linutil.yml +++ b/.github/workflows/linutil.yml @@ -3,6 +3,7 @@ name: LinUtil Release on: push: branches: ["main"] + workflow_dispatch: permissions: contents: write @@ -59,3 +60,39 @@ jobs: file_pattern: "build/linutil" add_options: '--force' if: success() + + - name: Extract Version + id: extract_version + run: | + version=$(date +"%Y.%m.%d") + echo "version=$version" >> $GITHUB_ENV + shell: bash + + - name: Generate Release Notes + id: generate_notes + uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + config-name: release-drafter.yml + version: ${{ env.version }} + + - name: Create and Upload Release + id: create_release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.version }} + name: Pre-Release ${{ env.version }} + body: | + ${{ steps.generate_notes.outputs.body }} + + ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil) + append_body: false + files: | + ./build/linutil + ./start.sh + ./startdev.sh + prerelease: true + env: + version: ${{ env.version }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-labels.yaml b/.github/workflows/pr-labels.yaml new file mode 100644 index 00000000..eab956f4 --- /dev/null +++ b/.github/workflows/pr-labels.yaml @@ -0,0 +1,50 @@ +name: Manage labels based on PR body + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +jobs: + manage-labels: + runs-on: ubuntu-latest + steps: + - name: Analyze PR Body and manage labels + run: | + body="${{ github.event.pull_request.body }}" + labels_to_add=() + labels_to_remove=() + declare -A label_checks=( + ["New feature"]="enhancement" + ["Bug fix|Hotfix|Security patch"]="bug" + ["Documentation update"]="documentation" + ["Refactoring"]="refactor" + ["UI/UX improvement"]="UI/UX" + ) + for key in "${!label_checks[@]}"; do + if echo "$body" | grep -q "\- \[x\] $key"; then + labels_to_add+=("${label_checks[$key]}") + else + labels_to_remove+=("${label_checks[$key]}") + fi + done + echo "LABELS_TO_ADD=${labels_to_add[*]}" >> $GITHUB_ENV + echo "LABELS_TO_REMOVE=${labels_to_remove[*]}" >> $GITHUB_ENV + - name: Add labels if necessary + if: env.LABELS_TO_ADD != '' + run: | + for label in ${{ env.LABELS_TO_ADD }}; do + curl -s -X POST \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + -d "{\"labels\": [\"$label\"]}" \ + https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels + done + - name: Remove labels if necessary + if: env.LABELS_TO_REMOVE != '' + run: | + for label in ${{ env.LABELS_TO_REMOVE }}; do + curl -s -X DELETE \ + -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/$label + done \ No newline at end of file diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml deleted file mode 100644 index 5e4b139e..00000000 --- a/.github/workflows/pre-release.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: Pre-Release LinUtil - -permissions: - contents: write # Grant write permissions to contents - packages: write # Grant write permissions to packages - -on: - workflow_dispatch: # Manual trigger added - workflow_run: - workflows: ["LinUtil Release"] - types: - - completed - -jobs: - build-runspace: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Extract Version - id: extract_version - run: | - version=$(date +"%Y.%m.%d") - echo "version=$version" >> $GITHUB_ENV - shell: bash - - - name: Generate Release Notes - id: generate_notes - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - config-name: release-drafter.yml - version: ${{ env.version }} - - - name: Create and Upload Release - id: create_release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ env.version }} - name: Pre-Release ${{ env.version }} - body: | - ${{ steps.generate_notes.outputs.body }} - - ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil) - append_body: false - files: | - ./build/linutil - ./start.sh - ./startdev.sh - prerelease: true - env: - version: ${{ env.version }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 8dcac50e..7f03765d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /build rust/target rust/build +/build/linutil diff --git a/README.md b/README.md index b892afc1..c3a2ef68 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,9 @@ For comprehensive information on how to use Linutil, visit the [Linutil Official ## 🛠 Contributing -We welcome contributions from the community! Before you start, please review our [Contributing Guidelines](CONTRIBUTING.md) to understand how to make the most effective and efficient contributions. +We welcome contributions from the community! Before you start, please review our [Contributing Guidelines](.github/CONTRIBUTING.md) to understand how to make the most effective and efficient contributions. + +[Official LinUtil Roadmap](https://christitustech.github.io/linutil/roadmap) ## 🏅 Thanks to All Contributors diff --git a/build/linutil b/build/linutil deleted file mode 100755 index 1f2e49fb..00000000 Binary files a/build/linutil and /dev/null differ diff --git a/roadmap.md b/docs/roadmap.md similarity index 100% rename from roadmap.md rename to docs/roadmap.md diff --git a/tabs/applications-setup/alacritty-setup.sh b/tabs/applications-setup/alacritty-setup.sh index 09c9e8d0..92e49d33 100755 --- a/tabs/applications-setup/alacritty-setup.sh +++ b/tabs/applications-setup/alacritty-setup.sh @@ -16,6 +16,9 @@ setupAlacritty() { else echo "alacritty is already installed." fi +} + +setupAlacrittyConfig() { echo "Copy alacritty config files" if [ -d "${HOME}/.config/alacritty" ] && [ ! -d "${HOME}/.config/alacritty-bak" ]; then cp -r "${HOME}/.config/alacritty" "${HOME}/.config/alacritty-bak" @@ -27,4 +30,5 @@ setupAlacritty() { checkEnv checkEscalationTool -setupAlacritty \ No newline at end of file +setupAlacritty +setupAlacrittyConfig \ No newline at end of file diff --git a/tabs/applications-setup/dwmtitus-setup.sh b/tabs/applications-setup/dwmtitus-setup.sh index be70f8c8..acc56c0e 100755 --- a/tabs/applications-setup/dwmtitus-setup.sh +++ b/tabs/applications-setup/dwmtitus-setup.sh @@ -15,8 +15,8 @@ setupDWM() { pacman) $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel libx11 libxinerama libxft imlib2 libxcb ;; - apt) - $ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev libxcb1-dev libx11-xcb1 + apt-get|nala) + $ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev libx11-xcb-dev libfontconfig1 libx11-6 libxft2 libxinerama1 libxcb-res0-dev ;; dnf) $ESCALATION_TOOL "$PACKAGER" groupinstall -y "Development Tools" @@ -178,7 +178,7 @@ setupDisplayManager() { pacman) $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm xorg-xinit xorg-server ;; - apt) + apt-get|nala) $ESCALATION_TOOL "$PACKAGER" install -y xorg xinit ;; dnf) @@ -206,7 +206,7 @@ setupDisplayManager() { pacman) $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm "$DM" ;; - apt) + apt-get|nala) $ESCALATION_TOOL "$PACKAGER" install -y "$DM" ;; dnf) @@ -292,4 +292,4 @@ makeDWM install_slstatus install_nerd_font clone_config_folders -configure_backgrounds \ No newline at end of file +configure_backgrounds diff --git a/tabs/applications-setup/fastfetch-setup.sh b/tabs/applications-setup/fastfetch-setup.sh new file mode 100644 index 00000000..e3f9a4f7 --- /dev/null +++ b/tabs/applications-setup/fastfetch-setup.sh @@ -0,0 +1,33 @@ +#!/bin/sh -e + +. ../common-script.sh + +setupFastfetch() { + echo "Installing Fastfetch if not already installed..." + if ! command_exists fastfetch; then + case ${PACKAGER} in + pacman) + $ESCALATION_TOOL "${PACKAGER}" -S --needed --noconfirm fastfetch + ;; + *) + $ESCALATION_TOOL "${PACKAGER}" install -y fastfetch + ;; + esac + else + echo "Fastfetch is already installed." + fi +} + +setupFastfetchConfig() { + echo "Copying Fastfetch config files..." + if [ -d "${HOME}/.config/fastfetch" ] && [ ! -d "${HOME}/.config/fastfetch-bak" ]; then + cp -r "${HOME}/.config/fastfetch" "${HOME}/.config/fastfetch-bak" + fi + mkdir -p "${HOME}/.config/fastfetch/" + curl -sSLo "${HOME}/.config/fastfetch/config.jsonc" https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/config.jsonc +} + +checkEnv +checkEscalationTool +setupFastfetch +setupFastfetchConfig \ No newline at end of file diff --git a/tabs/applications-setup/kitty-setup.sh b/tabs/applications-setup/kitty-setup.sh index 9baee95c..7afbbe5d 100755 --- a/tabs/applications-setup/kitty-setup.sh +++ b/tabs/applications-setup/kitty-setup.sh @@ -16,6 +16,9 @@ setupKitty() { else echo "Kitty is already installed." fi +} + +setupKittyConfig() { echo "Copy Kitty config files" if [ -d "${HOME}/.config/kitty" ] && [ ! -d "${HOME}/.config/kitty-bak" ]; then cp -r "${HOME}/.config/kitty" "${HOME}/.config/kitty-bak" @@ -28,3 +31,4 @@ setupKitty() { checkEnv checkEscalationTool setupKitty +setupKittyConfig \ No newline at end of file diff --git a/tabs/applications-setup/mybash-setup.sh b/tabs/applications-setup/mybash-setup.sh new file mode 100644 index 00000000..7bca14e2 --- /dev/null +++ b/tabs/applications-setup/mybash-setup.sh @@ -0,0 +1,118 @@ +#!/bin/sh -e + +. ../common-script.sh + +gitpath="$HOME/.local/share/mybash" + +cloneMyBash() { + # Check if the dir exists before attempting to clone into it. + if [ -d "$gitpath" ]; then + rm -rf "$gitpath" + fi + mkdir -p "$HOME/.local/share" # Only create the dir if it doesn't exist. + cd "$HOME" && git clone https://github.com/ChrisTitusTech/mybash.git "$gitpath" +} + +installDepend() { + echo "Install mybash if not already installed" + case "$PACKAGER" in + pacman) + $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm bash bash-completion tar bat tree unzip fontconfig + ;; + apt) + $ESCALATION_TOOL "$PACKAGER" install -y bash bash-completion tar bat tree unzip fontconfig + ;; + dnf) + $ESCALATION_TOOL "$PACKAGER" install -y bash bash-completion tar bat tree unzip fontconfig + ;; + zypper) + $ESCALATION_TOOL "$PACKAGER" install -y bash bash-completion tar bat tree unzip fontconfig + ;; + *) + printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" # The packages above were grabbed out of the original mybash-setup-script. + exit 1 + ;; + esac +} + +installFont() { + # Check to see if the MesloLGS Nerd Font is installed (Change this to whatever font you would like) + FONT_NAME="MesloLGS Nerd Font Mono" + if fc-list :family | grep -iq "$FONT_NAME"; then + echo "Font '$FONT_NAME' is installed." + else + echo "Installing font '$FONT_NAME'" + # Change this URL to correspond with the correct font + FONT_URL="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/Meslo.zip" + FONT_DIR="$HOME/.local/share/fonts" + TEMP_DIR=$(mktemp -d) + curl -sSLo "$TEMP_DIR"/"${FONT_NAME}".zip "$FONT_URL" + unzip "$TEMP_DIR"/"${FONT_NAME}".zip -d "$TEMP_DIR" + mkdir -p "$FONT_DIR"/"$FONT_NAME" + mv "${TEMP_DIR}"/*.ttf "$FONT_DIR"/"$FONT_NAME" + fc-cache -fv + rm -rf "${TEMP_DIR}" + echo "'$FONT_NAME' installed successfully." + fi +} + +installStarshipAndFzf() { + if command_exists starship; then + echo "Starship already installed" + return + fi + + if ! curl -sSL https://starship.rs/install.sh | sh; then + printf "%b\n" "${RED}Something went wrong during starship install!${RC}" + exit 1 + fi + if command_exists fzf; then + echo "Fzf already installed" + else + git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf + $ESCALATION_TOOL ~/.fzf/install + fi +} + +installZoxide() { + if command_exists zoxide; then + echo "Zoxide already installed" + return + fi + + if ! curl -sSL https://raw.githubusercontent.com/ajeetdsouza/zoxide/main/install.sh | sh; then + printf "%b\n" "${RED}Something went wrong during zoxide install!${RC}" + exit 1 + fi +} + +linkConfig() { + OLD_BASHRC="$HOME/.bashrc" + if [ -e "$OLD_BASHRC" ] && [ ! -e "$HOME/.bashrc.bak" ]; then + printf "%b\n" "${YELLOW}Moving old bash config file to $HOME/.bashrc.bak${RC}" + if ! mv "$OLD_BASHRC" "$HOME/.bashrc.bak"; then + printf "%b\n" "${RED}Can't move the old bash config file!${RC}" + exit 1 + fi + fi + + printf "%b\n" "${YELLOW}Linking new bash config file...${RC}" + ln -svf "$gitpath/.bashrc" "$HOME/.bashrc" || { + printf "%b\n" "${RED}Failed to create symbolic link for .bashrc${RC}" + exit 1 + } + ln -svf "$gitpath/starship.toml" "$HOME/.config/starship.toml" || { + printf "%b\n" "${RED}Failed to create symbolic link for starship.toml${RC}" + exit 1 + } + printf "%b\n" "${GREEN}Done! restart your shell to see the changes.${RC}" +} + +checkEnv +checkEscalationTool +cloneMyBash +installDepend +installFont +installStarshipAndFzf +installZoxide +linkConfig \ No newline at end of file diff --git a/tabs/applications-setup/neovim-setup.sh b/tabs/applications-setup/neovim-setup.sh new file mode 100755 index 00000000..0d3a2578 --- /dev/null +++ b/tabs/applications-setup/neovim-setup.sh @@ -0,0 +1,55 @@ +#!/bin/sh -e + +. ../common-script.sh + +gitpath="$HOME/.local/share/neovim" + +cloneNeovim() { + # Check if the dir exists before attempting to clone into it. + if [ -d "$gitpath" ]; then + rm -rf "$gitpath" + fi + mkdir -p "$HOME/.local/share" # Only create the dir if it doesn't exist. + cd "$HOME" && git clone https://github.com/ChrisTitusTech/neovim.git "$HOME/.local/share/neovim" +} + +setupNeovim() { + echo "Install Neovim if not already installed" + case "$PACKAGER" in + pacman) + $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm neovim ripgrep fzf python-virtualenv luarocks go shellcheck + ;; + apt) + $ESCALATION_TOOL "$PACKAGER" install -y neovim ripgrep fd-find python3-venv luarocks golang-go shellcheck + ;; + dnf) + $ESCALATION_TOOL "$PACKAGER" install -y neovim ripgrep fzf python3-virtualenv luarocks golang ShellCheck + ;; + zypper) + $ESCALATION_TOOL "$PACKAGER" install -y neovim ripgrep fzf python3-virtualenv luarocks golang ShellCheck + ;; + *) + printf "%b\n" "${RED}Unsupported package manager: $PACKAGER${RC}" # The packages above were grabbed out of the original nvim-setup-script. + exit 1 + ;; + esac +} + +backupNeovimConfig() { + if [ -d "$HOME/.config/nvim" ] && [ ! -d "$HOME/.config/nvim-backup" ]; then + cp -r "$HOME/.config/nvim" "$HOME/.config/nvim-backup" + fi + rm -rf "$HOME/.config/nvim" +} + +linkNeovimConfig() { + mkdir -p "$HOME/.config/nvim" + ln -s "$gitpath/titus-kickstart/"* "$HOME/.config/nvim/" # Wild card is used here to link all contents of titus-kickstart. +} + +checkEnv +checkEscalationTool +cloneNeovim +setupNeovim +backupNeovimConfig +linkNeovimConfig \ No newline at end of file diff --git a/tabs/applications-setup/rofi-setup.sh b/tabs/applications-setup/rofi-setup.sh index ef4fe232..c5874517 100755 --- a/tabs/applications-setup/rofi-setup.sh +++ b/tabs/applications-setup/rofi-setup.sh @@ -16,6 +16,9 @@ setupRofi() { else echo "Rofi is already installed." fi +} + +setupRofiConfig() { echo "Copy Rofi config files" if [ -d "$HOME/.config/rofi" ] && [ ! -d "$HOME/.config/rofi-bak" ]; then cp -r "$HOME/.config/rofi" "$HOME/.config/rofi-bak" @@ -33,3 +36,4 @@ setupRofi() { checkEnv checkEscalationTool setupRofi +setupRofiConfig diff --git a/tabs/applications-setup/tab_data.toml b/tabs/applications-setup/tab_data.toml index 2ad73892..ce8f1b92 100644 --- a/tabs/applications-setup/tab_data.toml +++ b/tabs/applications-setup/tab_data.toml @@ -6,7 +6,7 @@ script = "alacritty-setup.sh" [[data]] name = "Bash Prompt" -command = "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\"" +script = "mybash-setup.sh" [[data]] name = "DWM-Titus" @@ -18,7 +18,7 @@ script = "kitty-setup.sh" [[data]] name = "Neovim" -command = "bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\"" +script = "neovim-setup.sh" [[data]] name = "Rofi" @@ -27,3 +27,7 @@ script = "rofi-setup.sh" [[data]] name = "ZSH Prompt" script = "zsh-setup.sh" + +[[data]] +name = "Fastfetch" +script = "fastfetch-setup.sh" \ No newline at end of file diff --git a/tabs/gaming/tab_data.toml b/tabs/gaming/tab_data.toml index ba997c8c..b460683f 100644 --- a/tabs/gaming/tab_data.toml +++ b/tabs/gaming/tab_data.toml @@ -1,7 +1,7 @@ name = "Gaming" [[data]] -name = "Diablo II Ressurected" +name = "Diablo II Resurrected" [[data.entries]] name = "Loot Filter Install" diff --git a/tabs/utils/ollama.sh b/tabs/utils/ollama.sh new file mode 100644 index 00000000..969d62a3 --- /dev/null +++ b/tabs/utils/ollama.sh @@ -0,0 +1,209 @@ +#!/bin/sh -e + +. ../common-script.sh + +installollama() { + clear + printf "%b\n" "${YELLOW}Checking if ollama is already installed...${RC}" + + # Check if ollama is already installed + if command_exists ollama; then + printf "%b\n" "${GREEN}ollama is already installed.${RC}" + else + printf "%b\n" "${YELLOW}Installing ollama...${RC}" + curl -fsSL https://ollama.com/install.sh | sh + $ESCALATION_TOOL systemctl start ollama + fi +} + +list_models() { + clear + printf "%b\n" "${YELLOW}Listing all models available on your system...${RC}" + ollama list +} + +show_model_info() { + clear + list_models + printf "%b\n" "${YELLOW}Enter the name of the model you want to show information for (e.g., llama3.1):${RC}" + read -r model_name + + printf "%b\n" "${YELLOW}Showing information for model '$model_name'...${RC}" + ollama show "$model_name" +} + +# Function to display available models +display_models() { + clear + printf "%b\n" "${RED}Available Models${RC}" + printf "1. Llama 3.1 - 8B (4.7GB)\n" + printf "2. Llama 3.1 - 70B (40GB)\n" + printf "3. Llama 3.1 - 405B (231GB)\n" + printf "4. Phi 3 Mini - 3.8B (2.3GB)\n" + printf "5. Phi 3 Medium - 14B (7.9GB)\n" + printf "6. Gemma 2 - 2B (1.6GB)\n" + printf "7. Gemma 2 - 9B (5.5GB)\n" + printf "8. Gemma 2 - 27B (16GB)\n" + printf "9. Mistral - 7B (4.1GB)\n" + printf "10. Moondream 2 - 1.4B (829MB)\n" + printf "11. Neural Chat - 7B (4.1GB)\n" + printf "12. Starling - 7B (4.1GB)\n" + printf "13. Code Llama - 7B (3.8GB)\n" + printf "14. Llama 2 Uncensored - 7B (3.8GB)\n" + printf "15. LLaVA - 7B (4.5GB)\n" + printf "16. Solar - 10.7B (6.1GB)\n" +} + +# Function to select model based on user input +select_model() { + local choice="$1" + case $choice in + 1) echo "llama3.1";; + 2) echo "llama3.1:70b";; + 3) echo "llama3.1:405b";; + 4) echo "phi3";; + 5) echo "phi3:medium";; + 6) echo "gemma2:2b";; + 7) echo "gemma2";; + 8) echo "gemma2:27b";; + 9) echo "mistral";; + 10) echo "moondream";; + 11) echo "neural-chat";; + 12) echo "starling-lm";; + 13) echo "codellama";; + 14) echo "llama2-uncensored";; + 15) echo "llava";; + 16) echo "solar";; + *) echo "$choice";; # Treat any other input as a custom model name + esac +} + +run_model() { + clear + display_models + + printf "%b\n" "${GREEN}Installed Models${RC}" + installed_models=$(ollama list) + printf "%b\n" "${installed_models}" + + printf "%b\n" "${YELLOW}Custom Models${RC}" + custom_models=$(ollama list | grep 'custom-model-prefix') + + printf "%b\n" "${YELLOW}Please select a model to run:${RC}" + printf "%b\n" "${YELLOW}Enter the number corresponding to the model or enter the name of a custom model:${RC}" + + read -r model_choice + + model=$(select_model "$model_choice") + + printf "%b\n" "${YELLOW}Running the model: $model...${RC}" + ollama run "$model" + +} + +create_model() { + clear + printf "%b\n" "${YELLOW}Let's create a new model in Ollama!${RC}" + display_models + + # Prompt for base model + printf "%b\n" "${YELLOW}Enter the base model (e.g. '13' for codellama):${RC}" + read -r base_model + + model=$(select_model "$base_model") + + printf "%b\n" "${YELLOW}Running the model: $model...${RC}" + ollama pull "$model" + + # Prompt for custom model name + printf "%b\n" "${YELLOW}Enter a name for the new customized model:${RC}" + read -r custom_model_name + + # Prompt for temperature setting + printf "%b\n" "${YELLOW}Enter the desired temperature (higher values are more creative, lower values are more coherent, e.g., 1):${RC}" + read -r temperature + + if [ -z "$temperature" ]; then + temperature=${temperature:-1} + fi + + # Prompt for system message + printf "%b\n" "${YELLOW}Enter the system message for the model customization (e.g., 'You are Mario from Super Mario Bros. Answer as Mario, the assistant, only.'):${RC}" + read -r system_message + + # Create the Modelfile + printf "%b\n" "${YELLOW}Creating the Modelfile...${RC}" + cat << EOF > Modelfile +FROM $base_model + +# set the temperature to $temperature +PARAMETER temperature $temperature + +# set the system message +SYSTEM """ +$system_message +""" +EOF + + # Create the model in Ollama + printf "%b\n" "${YELLOW}Creating the model in Ollama...${RC}" + ollama create "$custom_model_name" -f Modelfile + printf "%b\n" "${GREEN}Model '$custom_model_name' created successfully.${RC}" +} + +# Function to remove a model +remove_model() { + clear + printf "%b\n" "${GREEN}Installed Models${RC}" + installed_models=$(ollama list) + printf "%b\n" "${installed_models}" + + printf "%b\n" "${YELLOW}Please select a model to remove:${RC}" + printf "%b\n" "${YELLOW}Enter the name of the model you want to remove:${RC}" + + read -r model_to_remove + + if echo "$installed_models" | grep -q "$model_to_remove"; then + printf "%b\n" "${YELLOW}Removing the model: $model_to_remove...${RC}" + ollama rm "$model_to_remove" + printf "%b\n" "${GREEN}Model '$model_to_remove' has been removed.${RC}" + else + printf "%b\n" "${RED}Model '$model_to_remove' is not installed. Exiting.${RC}" + exit 1 + fi +} + +menu() { + while true; do + clear + printf "%b\n" "${YELLOW}Please select an option:${RC}" + printf "1) List all models\n" + printf "2) Show model information\n" + printf "3) Create a new model\n" + printf "4) Run a model\n" + printf "5) Remove a model\n" + printf "6) Exit\n" + + printf "%b" "${YELLOW}Enter your choice (1-5): ${RC}" + read -r choice + + case $choice in + 1) list_models ;; + 2) show_model_info ;; + 3) create_model ;; + 4) run_model ;; + 5) remove_model;; + 6) printf "%b\n" "${GREEN}Exiting...${RC}"; exit 0 ;; + *) printf "%b\n" "${RED}Invalid choice. Please try again.${RC}" ;; + esac + + printf "%b\n" "${YELLOW}Press Enter to continue...${RC}" + read -r dummy + done +} + +checkEnv +checkEscalationTool +installollama +menu + diff --git a/tabs/utils/service-control.sh b/tabs/utils/service-control.sh new file mode 100644 index 00000000..96aba4fc --- /dev/null +++ b/tabs/utils/service-control.sh @@ -0,0 +1,278 @@ +#!/bin/sh -e + +# Load common script functions +. ../common-script.sh + +#external services directory +SCRIPT_DIR="./services" + +# Function to show the main menu +show_menu() { + clear + echo "============================" + echo " Service Management Menu" + echo "============================" + echo "1. View all services" + echo "2. View enabled services" + echo "3. View disabled services" + echo "4. Add a new service" + echo "5. Remove a service" + echo "6. Start a service" + echo "7. Stop a service" + echo "8. Enable a service" + echo "9. Disable a service" + echo "10. Create a service from external scripts" + echo "11. Exit" + echo "============================" +} + +# Function to view all services +view_all_services() { + echo "Listing all services..." + $ESCALATION_TOOL systemctl list-units --type=service --all --no-legend | awk '{print $1}' | sed 's/\.service//' | more +} + +# Function to view enabled services +view_enabled_services() { + echo "Listing enabled services..." + $ESCALATION_TOOL systemctl list-unit-files --type=service --state=enabled --no-legend | awk '{print $1}' | sed 's/\.service//' | more +} + +# Function to view disabled services +view_disabled_services() { + echo "Listing disabled services..." + $ESCALATION_TOOL systemctl list-unit-files --type=service --state=disabled --no-legend | awk '{print $1}' | sed 's/\.service//' | more +} + +# Function to view started services +view_started_services() { + echo "Listing started services:" + $ESCALATION_TOOL systemctl list-units --type=service --state=running --no-pager | head -n -6 | awk 'NR>1 {print $1}' | more +} + +# Function to add a new service +add_service() { + while [ -z "$SERVICE_NAME" ]; do + echo "Enter the name of the new service (e.g., my_service):" + read -r SERVICE_NAME + + if $ESCALATION_TOOL systemctl list-units --type=service --all --no-legend | grep -q "$SERVICE_NAME.service"; then + echo "Service already exists!" + SERVICE_NAME="" + fi + done + + echo "Enter the description of the service:" + read -r SERVICE_DESCRIPTION + + echo "Enter the command to execute the service (e.g., /usr/local/bin/my_service.sh):" + read -r EXEC_START + + echo "Enter the user to run the service as (leave empty for default):" + read -r SERVICE_USER + + echo "Enter the working directory for the service (leave empty for default):" + read -r WORKING_DIRECTORY + + echo "Enter the restart policy (e.g., always, on-failure; leave empty for no restart):" + read -r RESTART_POLICY + + # Create the service unit file + SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service" + + # Create the service file with conditionals for optional fields + { + echo "[Unit]" + echo "Description=$SERVICE_DESCRIPTION" + echo "" + echo "[Service]" + echo "ExecStart=$EXEC_START" + [ -n "$SERVICE_USER" ] && echo "User=$SERVICE_USER" + [ -n "$WORKING_DIRECTORY" ] && echo "WorkingDirectory=$WORKING_DIRECTORY" + [ -n "$RESTART_POLICY" ] && echo "Restart=$RESTART_POLICY" + echo "" + echo "[Install]" + echo "WantedBy=multi-user.target" + } | $ESCALATION_TOOL tee "$SERVICE_FILE" > /dev/null + + # Set permissions and reload systemd + $ESCALATION_TOOL chmod 644 "$SERVICE_FILE" + $ESCALATION_TOOL systemctl daemon-reload + echo "Service $SERVICE_NAME has been created and is ready to be started." + + # Optionally, enable and start the service + echo "Do you want to start and enable the service now? (y/n)" + read -r START_ENABLE + + if [ "$START_ENABLE" = "y" ]; then + $ESCALATION_TOOL systemctl start "$SERVICE_NAME" + $ESCALATION_TOOL systemctl enable "$SERVICE_NAME" + echo "Service $SERVICE_NAME has been started and enabled." + else + echo "Service $SERVICE_NAME has been created but not started." + fi +} + +# Function to remove a service +remove_service() { + echo "Enter the name of the service to remove (e.g., my_service):" + read -r SERVICE_NAME + + SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service" + + if [ -f "$SERVICE_FILE" ]; then + echo "Stopping and disabling the service..." + $ESCALATION_TOOL systemctl stop "$SERVICE_NAME" + $ESCALATION_TOOL systemctl disable "$SERVICE_NAME" + + echo "Removing the service file..." + $ESCALATION_TOOL rm -f "$SERVICE_FILE" + $ESCALATION_TOOL systemctl daemon-reload + + echo "Service $SERVICE_NAME has been removed." + else + echo "Service $SERVICE_NAME does not exist." + fi +} + +# Function to start a service +start_service() { + view_disabled_services + echo "Enter the name of the service to start (e.g., my_service):" + read -r SERVICE_NAME + + if $ESCALATION_TOOL systemctl start "$SERVICE_NAME"; then + echo "Service $SERVICE_NAME has been started." + else + echo "Failed to start service: $SERVICE_NAME." + fi +} + +# Function to stop a service +stop_service() { + view_started_services + echo "Enter the name of the service to stop (e.g., my_service):" + read -r SERVICE_NAME + + if $ESCALATION_TOOL systemctl stop "$SERVICE_NAME"; then + echo "Service $SERVICE_NAME has been stopped." + else + echo "Failed to stop service: $SERVICE_NAME." + fi +} + +# Function to enable a service +enable_service() { + view_disabled_services + echo "Enter the name of the service to enable (e.g., my_service):" + read -r SERVICE_NAME + + if $ESCALATION_TOOL systemctl enable "$SERVICE_NAME"; then + echo "Service $SERVICE_NAME has been enabled." + else + echo "Failed to enable service: $SERVICE_NAME." + fi +} + +# Function to enable a service +disable_service() { + view_enabled_services + echo "Enter the name of the service to disable (e.g., my_service):" + read -r SERVICE_NAME + + if $ESCALATION_TOOL systemctl disable "$SERVICE_NAME"; then + echo "Service $SERVICE_NAME has been enabled." + else + echo "Failed to enable service: $SERVICE_NAME." + fi +} + +# Function to create, start, and enable a service from an external service file +create_service_from_external() { + + # List all .service files in the SCRIPT_DIR + echo "============================" + echo "Listing available service files" + echo "============================" + for FILE in "$SCRIPT_DIR"/*.service; do + echo "$(basename "$FILE")" + done + + echo "Enter the filename (without the .service extension) of the service to create:" + read -r SERVICE_NAME + + SERVICE_FILE="$SCRIPT_DIR/$SERVICE_NAME.service" + + if [ ! -f "$SERVICE_FILE" ]; then + echo "Service file $SERVICE_FILE does not exist." + return + fi + + echo "Enter the username to run the service as (leave empty for no specific user):" + read -r SERVICE_USER + + # Create the systemd service file path + SYSTEMD_SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service" + + # Add or update the User= line in the service file + if [ -n "$SERVICE_USER" ]; then + # Check if User= exists and append username if needed + if grep -q '^User=' "$SERVICE_FILE"; then + # Update the existing User= line with the new username + sed -i "s/^User=.*/User=$SERVICE_USER/" "$SERVICE_FILE" + else + # Add the User= line if it doesn't exist + sed -i '/^\[Service\]/a User='"$SERVICE_USER" "$SERVICE_FILE" + fi + fi + + # Copy the modified service file to /etc/systemd/system/ + $ESCALATION_TOOL cp "$SERVICE_FILE" "$SYSTEMD_SERVICE_FILE" + + # Set permissions and reload systemd + $ESCALATION_TOOL chmod 644 "$SYSTEMD_SERVICE_FILE" + $ESCALATION_TOOL systemctl daemon-reload + echo "Service $SERVICE_NAME has been created and is ready to be started." + + # Optionally, enable and start the service + echo "Do you want to start and enable the service now? (y/n)" + read -r START_ENABLE + + if [ "$START_ENABLE" = "y" ]; then + $ESCALATION_TOOL systemctl start "$SERVICE_NAME" + $ESCALATION_TOOL systemctl enable "$SERVICE_NAME" + echo "Service $SERVICE_NAME has been started and enabled." + else + echo "Service $SERVICE_NAME has been created but not started." + fi +} + +main() { + while true; do + show_menu + echo "Enter your choice:" + read -r CHOICE + + case $CHOICE in + 1) view_all_services ;; + 2) view_enabled_services ;; + 3) view_disabled_services ;; + 4) add_service ;; + 5) remove_service ;; + 6) start_service ;; + 7) stop_service ;; + 8) enable_service ;; + 9) disable_service ;; + 10) create_service_from_external ;; + 11) echo "Exiting..."; exit 0 ;; + *) echo "Invalid choice. Please try again." ;; + esac + + echo "Press [Enter] to continue..." + read -r dummy + done +} + +checkEnv +checkEscalationTool +main \ No newline at end of file diff --git a/tabs/utils/services/i3lock-on-lid-close.service b/tabs/utils/services/i3lock-on-lid-close.service new file mode 100644 index 00000000..8a56246d --- /dev/null +++ b/tabs/utils/services/i3lock-on-lid-close.service @@ -0,0 +1,14 @@ +/etc/systemd/system/ + +[Unit] +Description=i3lock on Lid Close +Before=sleep.target + +[Service] +User= +Type=forking +Environment=DISPLAY=:0 +ExecStart=/usr/bin/i3lock + +[Install] +WantedBy=sleep.target diff --git a/tabs/utils/tab_data.toml b/tabs/utils/tab_data.toml index 67ba893f..bf05a1b3 100644 --- a/tabs/utils/tab_data.toml +++ b/tabs/utils/tab_data.toml @@ -12,6 +12,14 @@ script = "bluetooth-control.sh" name = "Numlock on Startup" script = "numlock.sh" +[[data]] +name = "Ollama" +script = "ollama.sh" + +[[data]] +name = "Service Manager" +script = "service-control.sh" + [[data]] name = "Monitor Control" diff --git a/tui/src/float.rs b/tui/src/float.rs index e60fe464..bfdea73d 100644 --- a/tui/src/float.rs +++ b/tui/src/float.rs @@ -64,9 +64,7 @@ impl Float { // Returns true if the floating window is finished. pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool { match key.code { - KeyCode::Enter | KeyCode::Char('p') | KeyCode::Esc | KeyCode::Char('q') - if self.content.is_finished() => - { + KeyCode::Enter | KeyCode::Char('p') | KeyCode::Esc if self.content.is_finished() => { true } _ => self.content.handle_key_event(key), diff --git a/tui/src/main.rs b/tui/src/main.rs index 4355a910..d000f719 100644 --- a/tui/src/main.rs +++ b/tui/src/main.rs @@ -14,7 +14,6 @@ use std::{ use crate::theme::Theme; use clap::Parser; use crossterm::{ - cursor::RestorePosition, event::{self, DisableMouseCapture, Event, KeyEventKind}, style::ResetColor, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, @@ -38,7 +37,7 @@ struct Args { override_validation: bool, } -fn main() -> std::io::Result<()> { +fn main() -> io::Result<()> { let args = Args::parse(); let mut state = AppState::new(args.theme, args.override_validation); @@ -55,8 +54,8 @@ fn main() -> std::io::Result<()> { terminal.backend_mut().execute(LeaveAlternateScreen)?; terminal.backend_mut().execute(DisableMouseCapture)?; terminal.backend_mut().execute(ResetColor)?; - terminal.backend_mut().execute(RestorePosition)?; terminal.show_cursor()?; + Ok(()) } diff --git a/tui/src/state.rs b/tui/src/state.rs index 267e6769..1d11ce80 100644 --- a/tui/src/state.rs +++ b/tui/src/state.rs @@ -6,7 +6,7 @@ use crate::{ running_command::RunningCommand, theme::Theme, }; -use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use ego_tree::NodeId; use linutil_core::{Command, ListNode, Tab}; use ratatui::{ @@ -185,6 +185,7 @@ impl AppState { draw_shortcuts(self, frame, vertical[1]); } + pub fn handle_key(&mut self, key: &KeyEvent) -> bool { match &mut self.focus { Focus::FloatingWindow(command) => { @@ -192,31 +193,43 @@ impl AppState { self.focus = Focus::List; } } + Focus::Search => match self.filter.handle_key(key) { SearchAction::Exit => self.exit_search(), SearchAction::Update => self.update_items(), _ => {} }, - _ if key.code == KeyCode::Char('q') => return false, + + _ if key.code == KeyCode::Char('q') + || key.code == KeyCode::Char('c') + && key.modifiers.contains(KeyModifiers::CONTROL) => + { + return false; + } + Focus::TabList => match key.code { KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => { self.focus = Focus::List } + KeyCode::Char('j') | KeyCode::Down if self.current_tab.selected().unwrap() + 1 < self.tabs.len() => { self.current_tab.select_next(); self.refresh_tab(); } + KeyCode::Char('k') | KeyCode::Up => { self.current_tab.select_previous(); self.refresh_tab(); } + KeyCode::Char('/') => self.enter_search(), KeyCode::Char('t') => self.theme.next(), KeyCode::Char('T') => self.theme.prev(), _ => {} }, + Focus::List if key.kind != KeyEventKind::Release => match key.code { KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(), KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(), @@ -235,10 +248,12 @@ impl AppState { KeyCode::Char('T') => self.theme.prev(), _ => {} }, - _ => {} + + _ => (), }; true } + fn update_items(&mut self) { self.filter.update_items( &self.tabs, @@ -246,12 +261,14 @@ impl AppState { *self.visit_stack.last().unwrap(), ); } + /// Checks ehther the current tree node is the root node (can we go up the tree or no) /// Returns `true` if we can't go up the tree (we are at the tree root) /// else returns `false` pub fn at_root(&self) -> bool { self.visit_stack.len() == 1 } + fn enter_parent_directory(&mut self) { self.visit_stack.pop(); self.selection.select(Some(0));