mirror of
				https://github.com/community-scripts/ProxmoxVE.git
				synced 2025-11-04 02:12:49 +00:00 
			
		
		
		
	Compare commits
	
		
			54 Commits
		
	
	
		
			2025-09-04
			...
			2025-09-08
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b6c0b50e79 | ||
| 
						 | 
					0f07f1927e | ||
| 
						 | 
					4493d86e51 | ||
| 
						 | 
					fbfeeeb88f | ||
| 
						 | 
					2366111bba | ||
| 
						 | 
					5fb42b87f1 | ||
| 
						 | 
					500c35c58d | ||
| 
						 | 
					ca3446c90e | ||
| 
						 | 
					4c3d42d5d1 | ||
| 
						 | 
					299a10efe8 | ||
| 
						 | 
					7adac2a342 | ||
| 
						 | 
					eb58b10d75 | ||
| 
						 | 
					5e46d81c45 | ||
| 
						 | 
					2963926c45 | ||
| 
						 | 
					d9a0b863a8 | ||
| 
						 | 
					db6369f3c6 | ||
| 
						 | 
					d450e263f0 | ||
| 
						 | 
					462960d9bf | ||
| 
						 | 
					8ea4829e8a | ||
| 
						 | 
					c5d23dc883 | ||
| 
						 | 
					3dc973e4ac | ||
| 
						 | 
					751f488f9e | ||
| 
						 | 
					0ed4363247 | ||
| 
						 | 
					e77612c27e | ||
| 
						 | 
					59c8e214af | ||
| 
						 | 
					c790f03a6b | ||
| 
						 | 
					54d4d58b15 | ||
| 
						 | 
					889a58c00e | ||
| 
						 | 
					e9a5d7587a | ||
| 
						 | 
					6f84046741 | ||
| 
						 | 
					ff96351db8 | ||
| 
						 | 
					1b632199b3 | ||
| 
						 | 
					9fb02a93b4 | ||
| 
						 | 
					3aa56651d8 | ||
| 
						 | 
					17d4c59041 | ||
| 
						 | 
					2bfb35779b | ||
| 
						 | 
					83b93390c9 | ||
| 
						 | 
					7ce47ad414 | ||
| 
						 | 
					646e0c31f4 | ||
| 
						 | 
					0c7e200a94 | ||
| 
						 | 
					a9b02b3e9c | ||
| 
						 | 
					b62adc2c5c | ||
| 
						 | 
					3438b046e5 | ||
| 
						 | 
					cbd6d6d0f3 | ||
| 
						 | 
					cf6c429c57 | ||
| 
						 | 
					bbe71cbbb9 | ||
| 
						 | 
					9b6a736c8c | ||
| 
						 | 
					c395ccc270 | ||
| 
						 | 
					3dd72e08e9 | ||
| 
						 | 
					1d989bea73 | ||
| 
						 | 
					f881b88344 | ||
| 
						 | 
					06dbad83ef | ||
| 
						 | 
					7ff966b042 | ||
| 
						 | 
					62264f37a6 | 
							
								
								
									
										73
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -10,8 +10,81 @@
 | 
				
			|||||||
> [!CAUTION]
 | 
					> [!CAUTION]
 | 
				
			||||||
Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
 | 
					Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit the project's popularity for potentially malicious purposes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-09
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-08
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - CT's: fix missing variable declaration (actualBudget, openziti, umlautadaptarr) [@MickLesk](https://github.com/MickLesk) ([#7483](https://github.com/community-scripts/ProxmoxVE/pull/7483))
 | 
				
			||||||
 | 
					    - karakeep: fix service file [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7482](https://github.com/community-scripts/ProxmoxVE/pull/7482))
 | 
				
			||||||
 | 
					    - Update searxng-install.sh [@sebguy](https://github.com/sebguy) ([#7469](https://github.com/community-scripts/ProxmoxVE/pull/7469))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Immich: bump to version 1.141.1 [@vhsdream](https://github.com/vhsdream) ([#7418](https://github.com/community-scripts/ProxmoxVE/pull/7418))
 | 
				
			||||||
 | 
					    - [core]: switch all base_settings to variables [@MickLesk](https://github.com/MickLesk) ([#7479](https://github.com/community-scripts/ProxmoxVE/pull/7479))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 💥 Breaking Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - RustDesk Server: Update the credentials info [@tremor021](https://github.com/tremor021) ([#7473](https://github.com/community-scripts/ProxmoxVE/pull/7473))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Format numerical values in DataFetcher component for better readability [@BramSuurdje](https://github.com/BramSuurdje) ([#7477](https://github.com/community-scripts/ProxmoxVE/pull/7477))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - feat: enhance github stars button to be better looking and more compact [@BramSuurdje](https://github.com/BramSuurdje) ([#7464](https://github.com/community-scripts/ProxmoxVE/pull/7464))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - Update ExecStart path for karakeep service [@CrazyWolf13](https://github.com/CrazyWolf13) ([#7460](https://github.com/community-scripts/ProxmoxVE/pull/7460))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 2025-09-06
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🆕 New Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Resilio Sync ([#7442](https://github.com/community-scripts/ProxmoxVE/pull/7442))
 | 
				
			||||||
 | 
					- leantime ([#7414](https://github.com/community-scripts/ProxmoxVE/pull/7414))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - use debian source for direct installation of MQTT [@EtlamGit](https://github.com/EtlamGit) ([#7423](https://github.com/community-scripts/ProxmoxVE/pull/7423))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### ✨ New Features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - feat: added mobile ui subscription nag removal [@ivan-penchev](https://github.com/ivan-penchev) ([#7164](https://github.com/community-scripts/ProxmoxVE/pull/7164))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 📝 Script Information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - MediaManager Configuration Path [@austinpilz](https://github.com/austinpilz) ([#7408](https://github.com/community-scripts/ProxmoxVE/pull/7408))
 | 
				
			||||||
 | 
					    - Paperless-NGX: Remove default credentials from json [@tremor021](https://github.com/tremor021) ([#7403](https://github.com/community-scripts/ProxmoxVE/pull/7403))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-09-05
 | 
					## 2025-09-05
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - Tududi: Pin version to 0.80 [@vhsdream](https://github.com/vhsdream) ([#7420](https://github.com/community-scripts/ProxmoxVE/pull/7420))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - #### 🐞 Bug Fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - AdventureLog: Update dependencies [@tremor021](https://github.com/tremor021) ([#7404](https://github.com/community-scripts/ProxmoxVE/pull/7404))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 🌐 Website
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - refactor: Enhance ScriptAccordion and Sidebar components to support selectedCategory state [@BramSuurdje](https://github.com/BramSuurdje) ([#7405](https://github.com/community-scripts/ProxmoxVE/pull/7405))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2025-09-04
 | 
					## 2025-09-04
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 🚀 Updated Scripts
 | 
					### 🚀 Updated Scripts
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,13 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
 | 
				
			|||||||
# Source: https://actualbudget.org/
 | 
					# Source: https://actualbudget.org/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="Actual Budget"
 | 
					APP="Actual Budget"
 | 
				
			||||||
var_tags="finance"
 | 
					var_tags="${var_tags:-finance}"
 | 
				
			||||||
var_cpu="2"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="2048"
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
var_disk="4"
 | 
					var_disk="${var_disk:-4}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,10 @@ function update_script() {
 | 
				
			|||||||
    msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
 | 
					  if ! command -v memcached >/dev/null 2>&1; then
 | 
				
			||||||
 | 
					    $STD apt-get update
 | 
				
			||||||
 | 
					    $STD apt-get install -y memcached libmemcached-tools
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
  if check_for_gh_release "adventurelog" "seanmorley15/adventurelog"; then
 | 
					  if check_for_gh_release "adventurelog" "seanmorley15/adventurelog"; then
 | 
				
			||||||
    msg_info "Stopping Services"
 | 
					    msg_info "Stopping Services"
 | 
				
			||||||
    systemctl stop adventurelog-backend
 | 
					    systemctl stop adventurelog-backend
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								ct/headers/leantime
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/leantime
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					    __                     __  _              
 | 
				
			||||||
 | 
					   / /   ___  ____ _____  / /_(_)___ ___  ___ 
 | 
				
			||||||
 | 
					  / /   / _ \/ __ `/ __ \/ __/ / __ `__ \/ _ \
 | 
				
			||||||
 | 
					 / /___/  __/ /_/ / / / / /_/ / / / / / /  __/
 | 
				
			||||||
 | 
					/_____/\___/\__,_/_/ /_/\__/_/_/ /_/ /_/\___/ 
 | 
				
			||||||
 | 
					                                              
 | 
				
			||||||
							
								
								
									
										6
									
								
								ct/headers/resiliosync
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ct/headers/resiliosync
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					    ____            _ ___          _____                 
 | 
				
			||||||
 | 
					   / __ \___  _____(_) (_)___     / ___/__  ______  _____
 | 
				
			||||||
 | 
					  / /_/ / _ \/ ___/ / / / __ \    \__ \/ / / / __ \/ ___/
 | 
				
			||||||
 | 
					 / _, _/  __(__  ) / / / /_/ /   ___/ / /_/ / / / / /__  
 | 
				
			||||||
 | 
					/_/ |_|\___/____/_/_/_/\____/   /____/\__, /_/ /_/\___/  
 | 
				
			||||||
 | 
					                                     /____/              
 | 
				
			||||||
@@ -61,7 +61,7 @@ function update_script() {
 | 
				
			|||||||
    done
 | 
					    done
 | 
				
			||||||
    msg_ok "Image-processing libraries up to date"
 | 
					    msg_ok "Image-processing libraries up to date"
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
  RELEASE="1.140.1"
 | 
					  RELEASE="1.141.1"
 | 
				
			||||||
  if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
 | 
					  if check_for_gh_release "immich" "immich-app/immich" "${RELEASE}"; then
 | 
				
			||||||
    msg_info "Stopping Services"
 | 
					    msg_info "Stopping Services"
 | 
				
			||||||
    systemctl stop immich-web
 | 
					    systemctl stop immich-web
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,11 @@ function update_script() {
 | 
				
			|||||||
      systemctl daemon-reload
 | 
					      systemctl daemon-reload
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if grep -q '^ExecStart=/usr/bin/node\s\+dist/index\.mjs$' /etc/systemd/system/karakeep-workers.service; then
 | 
				
			||||||
 | 
					      sed -i -E 's#^(ExecStart=/usr/bin/node\s+dist/)index\.mjs$#\1index.js#' /etc/systemd/system/karakeep-workers.service
 | 
				
			||||||
 | 
					      systemctl daemon-reload
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "karakeep" "karakeep-app/karakeep"
 | 
					    fetch_and_deploy_gh_release "karakeep" "karakeep-app/karakeep"
 | 
				
			||||||
    if command -v corepack >/dev/null; then
 | 
					    if command -v corepack >/dev/null; then
 | 
				
			||||||
      $STD corepack disable
 | 
					      $STD corepack disable
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								ct/leantime.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								ct/leantime.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: Stroopwafe1
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://leantime.io
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="Leantime"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-productivity}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-20}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					  header_info
 | 
				
			||||||
 | 
					  check_container_storage
 | 
				
			||||||
 | 
					  check_container_resources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if [[ ! -d /opt/leantime ]]; then
 | 
				
			||||||
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if check_for_gh_release "leantime" "Leantime/leantime"; then
 | 
				
			||||||
 | 
					    msg_info "Creating Backup"
 | 
				
			||||||
 | 
					    mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql"
 | 
				
			||||||
 | 
					    tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}"
 | 
				
			||||||
 | 
					    mv /opt/leantime /opt/leantime_bak
 | 
				
			||||||
 | 
					    msg_ok "Backup Created"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Restoring Config & Permissions"
 | 
				
			||||||
 | 
					    mv /opt/leantime_bak/config/.env /opt/leantime/config/.env
 | 
				
			||||||
 | 
					    chown -R www-data:www-data "/opt/leantime"
 | 
				
			||||||
 | 
					    chmod -R 750 "/opt/leantime"
 | 
				
			||||||
 | 
					    msg_ok "Restored Config & Permissions"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Removing Backup"
 | 
				
			||||||
 | 
					    rm -rf /opt/leantime_bak
 | 
				
			||||||
 | 
					    msg_ok "Removed Backup"
 | 
				
			||||||
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
 | 
					echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install${CL}"
 | 
				
			||||||
@@ -6,13 +6,13 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/m
 | 
				
			|||||||
# Source: https://github.com/openziti/ziti
 | 
					# Source: https://github.com/openziti/ziti
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="openziti-controller"
 | 
					APP="openziti-controller"
 | 
				
			||||||
var_tags="network;openziti-controller"
 | 
					var_tags="${var_tags:-network;openziti-controller}"
 | 
				
			||||||
var_cpu="2"
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
var_ram="1024"
 | 
					var_ram="${var_ram:-1024}"
 | 
				
			||||||
var_disk="8"
 | 
					var_disk="${var_disk:-8}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
@@ -20,18 +20,18 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
   header_info
 | 
					  header_info
 | 
				
			||||||
   check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
   check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
   if [[ ! -d /opt/openziti ]]; then
 | 
					  if [[ ! -d /opt/openziti ]]; then
 | 
				
			||||||
      msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
      exit
 | 
					    exit
 | 
				
			||||||
   fi
 | 
					  fi
 | 
				
			||||||
   msg_info "Updating $APP LXC"
 | 
					  msg_info "Updating $APP LXC"
 | 
				
			||||||
   $STD apt-get update
 | 
					  $STD apt-get update
 | 
				
			||||||
   $STD apt-get -y upgrade
 | 
					  $STD apt-get -y upgrade
 | 
				
			||||||
   msg_ok "Updated $APP LXC"
 | 
					  msg_ok "Updated $APP LXC"
 | 
				
			||||||
   exit
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
@@ -41,4 +41,4 @@ description
 | 
				
			|||||||
msg_ok "Completed Successfully!\n"
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:<port>/zac${CL}"
 | 
					echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:<port>/zac${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								ct/resiliosync.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								ct/resiliosync.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: David Bennett (dbinit)
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://www.resilio.com/sync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APP="Resilio Sync"
 | 
				
			||||||
 | 
					var_tags="${var_tags:-sync}"
 | 
				
			||||||
 | 
					var_cpu="${var_cpu:-2}"
 | 
				
			||||||
 | 
					var_ram="${var_ram:-2048}"
 | 
				
			||||||
 | 
					var_disk="${var_disk:-8}"
 | 
				
			||||||
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header_info "$APP"
 | 
				
			||||||
 | 
					variables
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function update_script() {
 | 
				
			||||||
 | 
					    header_info
 | 
				
			||||||
 | 
					    check_container_storage
 | 
				
			||||||
 | 
					    check_container_resources
 | 
				
			||||||
 | 
					    if [[ ! -d /var/lib/resilio-sync ]]; then
 | 
				
			||||||
 | 
					        msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
 | 
					        exit
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    msg_info "Updating ${APP} LXC"
 | 
				
			||||||
 | 
					    $STD apt-get update
 | 
				
			||||||
 | 
					    $STD apt-get -y upgrade
 | 
				
			||||||
 | 
					    msg_ok "Updated Successfully"
 | 
				
			||||||
 | 
					    exit
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					start
 | 
				
			||||||
 | 
					build_container
 | 
				
			||||||
 | 
					description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_ok "Completed Successfully!\n"
 | 
				
			||||||
 | 
					echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
 | 
				
			||||||
 | 
					echo -e "${INFO}${YW} Access it using the following URL:${CL}"
 | 
				
			||||||
 | 
					echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8888${CL}"
 | 
				
			||||||
@@ -37,7 +37,7 @@ function update_script() {
 | 
				
			|||||||
    rm -rf /opt/tududi/backend/dist
 | 
					    rm -rf /opt/tududi/backend/dist
 | 
				
			||||||
    msg_ok "Backup and removed Files"
 | 
					    msg_ok "Backup and removed Files"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fetch_and_deploy_gh_release "tududi" "chrisvel/tududi"
 | 
					    fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "v0.80" "/opt/tududi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_info "Updating ${APP}"
 | 
					    msg_info "Updating ${APP}"
 | 
				
			||||||
    cd /opt/tududi
 | 
					    cd /opt/tududi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,13 +6,13 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
 | 
				
			|||||||
# Source: https://github.com/PCJones/UmlautAdaptarr
 | 
					# Source: https://github.com/PCJones/UmlautAdaptarr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APP="UmlautAdaptarr"
 | 
					APP="UmlautAdaptarr"
 | 
				
			||||||
var_tags="arr"
 | 
					var_tags="${var_tags:-arr}"
 | 
				
			||||||
var_cpu="1"
 | 
					var_cpu="${var_cpu:-1}"
 | 
				
			||||||
var_ram="512"
 | 
					var_ram="${var_ram:-512}"
 | 
				
			||||||
var_disk="4"
 | 
					var_disk="${var_disk:-4}"
 | 
				
			||||||
var_os="debian"
 | 
					var_os="${var_os:-debian}"
 | 
				
			||||||
var_version="12"
 | 
					var_version="${var_version:-12}"
 | 
				
			||||||
var_unprivileged="1"
 | 
					var_unprivileged="${var_unprivileged:-1}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header_info "$APP"
 | 
					header_info "$APP"
 | 
				
			||||||
variables
 | 
					variables
 | 
				
			||||||
@@ -20,33 +20,33 @@ color
 | 
				
			|||||||
catch_errors
 | 
					catch_errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function update_script() {
 | 
					function update_script() {
 | 
				
			||||||
    header_info
 | 
					  header_info
 | 
				
			||||||
    check_container_storage
 | 
					  check_container_storage
 | 
				
			||||||
    check_container_resources
 | 
					  check_container_resources
 | 
				
			||||||
    if [[ ! -d /opt/UmlautAdaptarr ]]; then
 | 
					  if [[ ! -d /opt/UmlautAdaptarr ]]; then
 | 
				
			||||||
        msg_error "No ${APP} Installation Found!"
 | 
					    msg_error "No ${APP} Installation Found!"
 | 
				
			||||||
        exit
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    RELEASE=$(curl -fsSL https://api.github.com/repos/PCJones/Umlautadaptarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
					 | 
				
			||||||
    if [[ ! -f /opt/UmlautAdaptarr_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/UmlautAdaptarr_version.txt)" ]]; then
 | 
					 | 
				
			||||||
        msg_info "Stopping Service"
 | 
					 | 
				
			||||||
        systemctl stop umlautadaptarr
 | 
					 | 
				
			||||||
        msg_ok "Stopped Service"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        msg_info "Updating ${APP}"
 | 
					 | 
				
			||||||
        temp_file=$(mktemp)
 | 
					 | 
				
			||||||
        curl -fsSL "https://github.com/PCJones/Umlautadaptarr/releases/download/${RELEASE}/linux-x64.zip" -o $temp_file
 | 
					 | 
				
			||||||
        $STD unzip -u $temp_file '*/**' -d /opt/UmlautAdaptarr
 | 
					 | 
				
			||||||
        msg_ok "Updated ${APP}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        msg_info "Starting Service"
 | 
					 | 
				
			||||||
        systemctl start umlautadaptarr
 | 
					 | 
				
			||||||
        msg_ok "Started Service"
 | 
					 | 
				
			||||||
        msg_ok "$APP has been updated to ${RELEASE}."
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        msg_ok "No update required. ${APP} is already at ${RELEASE}"
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
    exit
 | 
					    exit
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  RELEASE=$(curl -fsSL https://api.github.com/repos/PCJones/Umlautadaptarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
 | 
				
			||||||
 | 
					  if [[ ! -f /opt/UmlautAdaptarr_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/UmlautAdaptarr_version.txt)" ]]; then
 | 
				
			||||||
 | 
					    msg_info "Stopping Service"
 | 
				
			||||||
 | 
					    systemctl stop umlautadaptarr
 | 
				
			||||||
 | 
					    msg_ok "Stopped Service"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Updating ${APP}"
 | 
				
			||||||
 | 
					    temp_file=$(mktemp)
 | 
				
			||||||
 | 
					    curl -fsSL "https://github.com/PCJones/Umlautadaptarr/releases/download/${RELEASE}/linux-x64.zip" -o $temp_file
 | 
				
			||||||
 | 
					    $STD unzip -u $temp_file '*/**' -d /opt/UmlautAdaptarr
 | 
				
			||||||
 | 
					    msg_ok "Updated ${APP}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    msg_info "Starting Service"
 | 
				
			||||||
 | 
					    systemctl start umlautadaptarr
 | 
				
			||||||
 | 
					    msg_ok "Started Service"
 | 
				
			||||||
 | 
					    msg_ok "$APP has been updated to ${RELEASE}."
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    msg_ok "No update required. ${APP} is already at ${RELEASE}"
 | 
				
			||||||
 | 
					  fi
 | 
				
			||||||
 | 
					  exit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
start
 | 
					start
 | 
				
			||||||
build_container
 | 
					build_container
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								frontend/components.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								frontend/components.json
									
									
									
										generated
									
									
									
								
							@@ -13,5 +13,8 @@
 | 
				
			|||||||
  "aliases": {
 | 
					  "aliases": {
 | 
				
			||||||
    "components": "@/components",
 | 
					    "components": "@/components",
 | 
				
			||||||
    "utils": "@/lib/utils"
 | 
					    "utils": "@/lib/utils"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "registries": {
 | 
				
			||||||
 | 
					    "@animate-ui": "https://animate-ui.com/r/{name}.json"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,10 @@ const nextConfig = {
 | 
				
			|||||||
    BASE_PATH: "ProxmoxVE",
 | 
					    BASE_PATH: "ProxmoxVE",
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  eslint: {
 | 
				
			||||||
 | 
					    ignoreDuringBuilds: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  output: "export",
 | 
					  output: "export",
 | 
				
			||||||
  basePath: `/ProxmoxVE`,
 | 
					  basePath: `/ProxmoxVE`,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										95
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										95
									
								
								frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -31,8 +31,9 @@
 | 
				
			|||||||
        "date-fns": "^4.1.0",
 | 
					        "date-fns": "^4.1.0",
 | 
				
			||||||
        "framer-motion": "^11.18.2",
 | 
					        "framer-motion": "^11.18.2",
 | 
				
			||||||
        "fuse.js": "^7.1.0",
 | 
					        "fuse.js": "^7.1.0",
 | 
				
			||||||
        "lucide-react": "^0.453.0",
 | 
					        "lucide-react": "^0.542.0",
 | 
				
			||||||
        "mini-svg-data-uri": "^1.4.4",
 | 
					        "mini-svg-data-uri": "^1.4.4",
 | 
				
			||||||
 | 
					        "motion": "^12.23.12",
 | 
				
			||||||
        "next": "15.5.2",
 | 
					        "next": "15.5.2",
 | 
				
			||||||
        "next-themes": "^0.4.4",
 | 
					        "next-themes": "^0.4.4",
 | 
				
			||||||
        "nuqs": "^2.4.1",
 | 
					        "nuqs": "^2.4.1",
 | 
				
			||||||
@@ -46,6 +47,7 @@
 | 
				
			|||||||
        "react-dom": "19.0.0",
 | 
					        "react-dom": "19.0.0",
 | 
				
			||||||
        "react-icons": "^5.5.0",
 | 
					        "react-icons": "^5.5.0",
 | 
				
			||||||
        "react-simple-typewriter": "^5.0.1",
 | 
					        "react-simple-typewriter": "^5.0.1",
 | 
				
			||||||
 | 
					        "react-use-measure": "^2.1.7",
 | 
				
			||||||
        "sharp": "^0.33.5",
 | 
					        "sharp": "^0.33.5",
 | 
				
			||||||
        "simple-icons": "^13.21.0",
 | 
					        "simple-icons": "^13.21.0",
 | 
				
			||||||
        "sonner": "^1.7.4",
 | 
					        "sonner": "^1.7.4",
 | 
				
			||||||
@@ -9293,12 +9295,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/lucide-react": {
 | 
					    "node_modules/lucide-react": {
 | 
				
			||||||
      "version": "0.453.0",
 | 
					      "version": "0.542.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.453.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==",
 | 
					      "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==",
 | 
				
			||||||
      "license": "ISC",
 | 
					      "license": "ISC",
 | 
				
			||||||
      "peerDependencies": {
 | 
					      "peerDependencies": {
 | 
				
			||||||
        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
 | 
					        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/magic-string": {
 | 
					    "node_modules/magic-string": {
 | 
				
			||||||
@@ -10339,6 +10341,32 @@
 | 
				
			|||||||
        "pathe": "^2.0.1"
 | 
					        "pathe": "^2.0.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "framer-motion": "^12.23.12",
 | 
				
			||||||
 | 
					        "tslib": "^2.4.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": "*",
 | 
				
			||||||
 | 
					        "react": "^18.0.0 || ^19.0.0",
 | 
				
			||||||
 | 
					        "react-dom": "^18.0.0 || ^19.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/motion-dom": {
 | 
					    "node_modules/motion-dom": {
 | 
				
			||||||
      "version": "11.18.1",
 | 
					      "version": "11.18.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
 | 
				
			||||||
@@ -10354,6 +10382,48 @@
 | 
				
			|||||||
      "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
 | 
					      "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/framer-motion": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "motion-dom": "^12.23.12",
 | 
				
			||||||
 | 
					        "motion-utils": "^12.23.6",
 | 
				
			||||||
 | 
					        "tslib": "^2.4.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": "*",
 | 
				
			||||||
 | 
					        "react": "^18.0.0 || ^19.0.0",
 | 
				
			||||||
 | 
					        "react-dom": "^18.0.0 || ^19.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "@emotion/is-prop-valid": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/motion-dom": {
 | 
				
			||||||
 | 
					      "version": "12.23.12",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "motion-utils": "^12.23.6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/motion/node_modules/motion-utils": {
 | 
				
			||||||
 | 
					      "version": "12.23.6",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
 | 
				
			||||||
 | 
					      "license": "MIT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/ms": {
 | 
					    "node_modules/ms": {
 | 
				
			||||||
      "version": "2.1.3",
 | 
					      "version": "2.1.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
 | 
				
			||||||
@@ -11990,6 +12060,21 @@
 | 
				
			|||||||
        "react": ">= 0.14.0"
 | 
					        "react": ">= 0.14.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/react-use-measure": {
 | 
				
			||||||
 | 
					      "version": "2.1.7",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "react": ">=16.13",
 | 
				
			||||||
 | 
					        "react-dom": ">=16.13"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependenciesMeta": {
 | 
				
			||||||
 | 
					        "react-dom": {
 | 
				
			||||||
 | 
					          "optional": true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/read-cache": {
 | 
					    "node_modules/read-cache": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								frontend/package.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								frontend/package.json
									
									
									
										generated
									
									
									
								
							@@ -38,8 +38,9 @@
 | 
				
			|||||||
    "date-fns": "^4.1.0",
 | 
					    "date-fns": "^4.1.0",
 | 
				
			||||||
    "framer-motion": "^11.18.2",
 | 
					    "framer-motion": "^11.18.2",
 | 
				
			||||||
    "fuse.js": "^7.1.0",
 | 
					    "fuse.js": "^7.1.0",
 | 
				
			||||||
    "lucide-react": "^0.453.0",
 | 
					    "lucide-react": "^0.542.0",
 | 
				
			||||||
    "mini-svg-data-uri": "^1.4.4",
 | 
					    "mini-svg-data-uri": "^1.4.4",
 | 
				
			||||||
 | 
					    "motion": "^12.23.12",
 | 
				
			||||||
    "next": "15.5.2",
 | 
					    "next": "15.5.2",
 | 
				
			||||||
    "next-themes": "^0.4.4",
 | 
					    "next-themes": "^0.4.4",
 | 
				
			||||||
    "nuqs": "^2.4.1",
 | 
					    "nuqs": "^2.4.1",
 | 
				
			||||||
@@ -53,6 +54,7 @@
 | 
				
			|||||||
    "react-dom": "19.0.0",
 | 
					    "react-dom": "19.0.0",
 | 
				
			||||||
    "react-icons": "^5.5.0",
 | 
					    "react-icons": "^5.5.0",
 | 
				
			||||||
    "react-simple-typewriter": "^5.0.1",
 | 
					    "react-simple-typewriter": "^5.0.1",
 | 
				
			||||||
 | 
					    "react-use-measure": "^2.1.7",
 | 
				
			||||||
    "sharp": "^0.33.5",
 | 
					    "sharp": "^0.33.5",
 | 
				
			||||||
    "simple-icons": "^13.21.0",
 | 
					    "simple-icons": "^13.21.0",
 | 
				
			||||||
    "sonner": "^1.7.4",
 | 
					    "sonner": "^1.7.4",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								frontend/public/json/leantime.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								frontend/public/json/leantime.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "Leantime",
 | 
				
			||||||
 | 
					  "slug": "leantime",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    12
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-06",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "interface_port": 80,
 | 
				
			||||||
 | 
					  "documentation": "https://docs.leantime.io/",
 | 
				
			||||||
 | 
					  "config_path": "/opt/Leantime/config/.env",
 | 
				
			||||||
 | 
					  "website": "https://leantime.io",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/leantime.webp",
 | 
				
			||||||
 | 
					  "description": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/leantime.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 2,
 | 
				
			||||||
 | 
					        "ram": 2048,
 | 
				
			||||||
 | 
					        "hdd": 20,
 | 
				
			||||||
 | 
					        "os": "Debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": null,
 | 
				
			||||||
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": []
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -11,7 +11,7 @@
 | 
				
			|||||||
  "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
  "interface_port": 8000,
 | 
					  "interface_port": 8000,
 | 
				
			||||||
  "documentation": "https://maxdorninger.github.io/MediaManager/introduction.html",
 | 
					  "documentation": "https://maxdorninger.github.io/MediaManager/introduction.html",
 | 
				
			||||||
  "config_path": "/opt/mm_data/config.toml",
 | 
					  "config_path": "/opt/mm/config/config.toml",
 | 
				
			||||||
  "website": "https://github.com/maxdorninger/MediaManager",
 | 
					  "website": "https://github.com/maxdorninger/MediaManager",
 | 
				
			||||||
  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/mediamanager.webp",
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/mediamanager.webp",
 | 
				
			||||||
  "description": "A modern selfhosted media management system for your media library",
 | 
					  "description": "A modern selfhosted media management system for your media library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,44 +1,44 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "Paperless-ngx",
 | 
					  "name": "Paperless-ngx",
 | 
				
			||||||
    "slug": "paperless-ngx",
 | 
					  "slug": "paperless-ngx",
 | 
				
			||||||
    "categories": [
 | 
					  "categories": [
 | 
				
			||||||
        12
 | 
					    12
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
    "date_created": "2024-05-02",
 | 
					  "date_created": "2024-05-02",
 | 
				
			||||||
    "type": "ct",
 | 
					  "type": "ct",
 | 
				
			||||||
    "updateable": true,
 | 
					  "updateable": true,
 | 
				
			||||||
    "privileged": false,
 | 
					  "privileged": false,
 | 
				
			||||||
    "interface_port": 8000,
 | 
					  "interface_port": 8000,
 | 
				
			||||||
    "documentation": "https://docs.paperless-ngx.com/",
 | 
					  "documentation": "https://docs.paperless-ngx.com/",
 | 
				
			||||||
    "website": "https://docs.paperless-ngx.com/",
 | 
					  "website": "https://docs.paperless-ngx.com/",
 | 
				
			||||||
    "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp",
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp",
 | 
				
			||||||
    "config_path": "/opt/paperless/paperless.conf",
 | 
					  "config_path": "/opt/paperless/paperless.conf",
 | 
				
			||||||
    "description": "Paperless-ngx is a software tool designed for digitizing and organizing paper documents. It provides a web-based interface for scanning, uploading, and organizing paper documents, making it easier to manage, search, and access important information. Paperless-ngx uses the OCR (Optical Character Recognition) technology to extract text from scanned images and makes it searchable, thus increasing the efficiency of document management.",
 | 
					  "description": "Paperless-ngx is a software tool designed for digitizing and organizing paper documents. It provides a web-based interface for scanning, uploading, and organizing paper documents, making it easier to manage, search, and access important information. Paperless-ngx uses the OCR (Optical Character Recognition) technology to extract text from scanned images and makes it searchable, thus increasing the efficiency of document management.",
 | 
				
			||||||
    "install_methods": [
 | 
					  "install_methods": [
 | 
				
			||||||
        {
 | 
					    {
 | 
				
			||||||
            "type": "default",
 | 
					      "type": "default",
 | 
				
			||||||
            "script": "ct/paperless-ngx.sh",
 | 
					      "script": "ct/paperless-ngx.sh",
 | 
				
			||||||
            "resources": {
 | 
					      "resources": {
 | 
				
			||||||
                "cpu": 2,
 | 
					        "cpu": 2,
 | 
				
			||||||
                "ram": 2048,
 | 
					        "ram": 2048,
 | 
				
			||||||
                "hdd": 12,
 | 
					        "hdd": 12,
 | 
				
			||||||
                "os": "debian",
 | 
					        "os": "debian",
 | 
				
			||||||
                "version": "12"
 | 
					        "version": "12"
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
    "default_credentials": {
 | 
					  "default_credentials": {
 | 
				
			||||||
        "username": "admin",
 | 
					    "username": null,
 | 
				
			||||||
        "password": null
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "Show Login Credentials, type `cat ~/paperless-ngx.creds` in the LXC console",
 | 
				
			||||||
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "notes": [
 | 
					    {
 | 
				
			||||||
        {
 | 
					      "text": "Script installs English as default OCR language. To install additional languages, use `apt-get install tesseract-ocr-[lang]`, where [lang] is the language code (e.g. `apt-get install tesseract-ocr-deu`).",
 | 
				
			||||||
            "text": "Show Login Credentials, type `cat ~/paperless-ngx.creds` in the LXC console",
 | 
					      "type": "info"
 | 
				
			||||||
            "type": "info"
 | 
					    }
 | 
				
			||||||
        },
 | 
					  ]
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "text": "Script installs English as default OCR language. To install additional languages, use `apt-get install tesseract-ocr-[lang]`, where [lang] is the language code (e.g. `apt-get install tesseract-ocr-deu`).",
 | 
					 | 
				
			||||||
            "type": "info"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								frontend/public/json/resiliosync.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								frontend/public/json/resiliosync.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "Resilio Sync",
 | 
				
			||||||
 | 
					  "slug": "resiliosync",
 | 
				
			||||||
 | 
					  "categories": [
 | 
				
			||||||
 | 
					    11
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "date_created": "2025-09-06",
 | 
				
			||||||
 | 
					  "type": "ct",
 | 
				
			||||||
 | 
					  "updateable": true,
 | 
				
			||||||
 | 
					  "privileged": false,
 | 
				
			||||||
 | 
					  "config_path": "/etc/resilio-sync/config.json",
 | 
				
			||||||
 | 
					  "interface_port": 8888,
 | 
				
			||||||
 | 
					  "documentation": "https://help.resilio.com/",
 | 
				
			||||||
 | 
					  "website": "https://www.resilio.com/sync",
 | 
				
			||||||
 | 
					  "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/resilio-sync.webp",
 | 
				
			||||||
 | 
					  "description": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.",
 | 
				
			||||||
 | 
					  "install_methods": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "type": "default",
 | 
				
			||||||
 | 
					      "script": "ct/resilio-sync.sh",
 | 
				
			||||||
 | 
					      "resources": {
 | 
				
			||||||
 | 
					        "cpu": 2,
 | 
				
			||||||
 | 
					        "ram": 2048,
 | 
				
			||||||
 | 
					        "hdd": 8,
 | 
				
			||||||
 | 
					        "os": "debian",
 | 
				
			||||||
 | 
					        "version": "12"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "default_credentials": {
 | 
				
			||||||
 | 
					    "username": null,
 | 
				
			||||||
 | 
					    "password": null
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "notes": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "After free registration, you will receive a license keyfile to your email address. Upload it into any LXC directory and select on first run.",
 | 
				
			||||||
 | 
					      "type": "info"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -48,7 +48,11 @@
 | 
				
			|||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "text": "Login credentials: `cat ~/rustdesk.creds`",
 | 
					      "text": "To set admin password on Debian, type `cd /var/lib/rustdesk-api && rustdesk-api reset-admin-pwd <yournewpasswordhere>` inside LXC.",
 | 
				
			||||||
 | 
					      "type": "info"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "text": "To see admin password on Alpine, type `cat ~/rustdesk.creds` inside LXC.",
 | 
				
			||||||
      "type": "info"
 | 
					      "type": "info"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,284 @@
 | 
				
			|||||||
[
 | 
					[
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "YunoHost/yunohost",
 | 
				
			||||||
 | 
					    "version": "debian/12.1.20",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T23:47:21Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "mongodb/mongo",
 | 
				
			||||||
 | 
					    "version": "r8.0.14-rc1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T22:50:53Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "diced/zipline",
 | 
				
			||||||
 | 
					    "version": "v4.3.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T22:26:23Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "cross-seed/cross-seed",
 | 
				
			||||||
 | 
					    "version": "v6.13.3",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T21:45:15Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "HabitRPG/habitica",
 | 
				
			||||||
 | 
					    "version": "v5.40.2",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T20:59:44Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "booklore-app/booklore",
 | 
				
			||||||
 | 
					    "version": "v1.2.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T19:31:07Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "mattermost/mattermost",
 | 
				
			||||||
 | 
					    "version": "server/public/v0.1.18",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T18:04:10Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "immich-app/immich",
 | 
				
			||||||
 | 
					    "version": "v1.141.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T17:15:33Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "msgbyte/tianji",
 | 
				
			||||||
 | 
					    "version": "v1.24.27",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T16:23:37Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Paymenter/Paymenter",
 | 
				
			||||||
 | 
					    "version": "v1.3.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T15:26:01Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "n8n-io/n8n",
 | 
				
			||||||
 | 
					    "version": "n8n@1.109.2",
 | 
				
			||||||
 | 
					    "date": "2025-09-03T07:50:21Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "apache/tomcat",
 | 
				
			||||||
 | 
					    "version": "10.1.46",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T14:29:54Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "home-assistant/operating-system",
 | 
				
			||||||
 | 
					    "version": "16.2",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T14:03:25Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "zitadel/zitadel",
 | 
				
			||||||
 | 
					    "version": "v4.1.3",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T13:36:08Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "theonedev/onedev",
 | 
				
			||||||
 | 
					    "version": "v12.0.10",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T13:20:16Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "evcc-io/evcc",
 | 
				
			||||||
 | 
					    "version": "0.207.6",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T11:52:00Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "autobrr/autobrr",
 | 
				
			||||||
 | 
					    "version": "v1.66.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T10:49:03Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "meilisearch/meilisearch",
 | 
				
			||||||
 | 
					    "version": "latest",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T10:03:11Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "syncthing/syncthing",
 | 
				
			||||||
 | 
					    "version": "v2.0.8",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T08:07:18Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "nzbgetcom/nzbget",
 | 
				
			||||||
 | 
					    "version": "v25.3",
 | 
				
			||||||
 | 
					    "date": "2025-09-01T09:47:06Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "TandoorRecipes/recipes",
 | 
				
			||||||
 | 
					    "version": "2.1.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T06:39:30Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "firefly-iii/firefly-iii",
 | 
				
			||||||
 | 
					    "version": "v6.3.2",
 | 
				
			||||||
 | 
					    "date": "2025-08-19T04:08:36Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Jackett/Jackett",
 | 
				
			||||||
 | 
					    "version": "v0.22.2441",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T06:01:57Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "webmin/webmin",
 | 
				
			||||||
 | 
					    "version": "2.501",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T04:50:25Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "jeedom/core",
 | 
				
			||||||
 | 
					    "version": "4.4.19",
 | 
				
			||||||
 | 
					    "date": "2025-09-08T00:27:05Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "steveiliop56/tinyauth",
 | 
				
			||||||
 | 
					    "version": "v3.6.2",
 | 
				
			||||||
 | 
					    "date": "2025-07-17T12:08:03Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "paperless-ngx/paperless-ngx",
 | 
				
			||||||
 | 
					    "version": "v2.18.4",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T23:57:32Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "9001/copyparty",
 | 
				
			||||||
 | 
					    "version": "v1.19.8",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T23:36:42Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Part-DB/Part-DB-server",
 | 
				
			||||||
 | 
					    "version": "v2.1.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T21:59:53Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "minio/minio",
 | 
				
			||||||
 | 
					    "version": "RELEASE.2025-09-07T16-13-09Z",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T18:53:04Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "MediaBrowser/Emby.Releases",
 | 
				
			||||||
 | 
					    "version": "4.9.1.2",
 | 
				
			||||||
 | 
					    "date": "2025-06-26T22:08:00Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "karakeep-app/karakeep",
 | 
				
			||||||
 | 
					    "version": "sdk/v0.27.0",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T17:49:04Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "dgtlmoon/changedetection.io",
 | 
				
			||||||
 | 
					    "version": "0.50.12",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T14:16:07Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "runtipi/runtipi",
 | 
				
			||||||
 | 
					    "version": "nightly",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T12:25:06Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "semaphoreui/semaphore",
 | 
				
			||||||
 | 
					    "version": "v2.17.0-beta1",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T08:56:50Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Radarr/Radarr",
 | 
				
			||||||
 | 
					    "version": "v5.27.5.10198",
 | 
				
			||||||
 | 
					    "date": "2025-09-03T12:08:43Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Prowlarr/Prowlarr",
 | 
				
			||||||
 | 
					    "version": "v2.0.5.5160",
 | 
				
			||||||
 | 
					    "date": "2025-08-23T21:23:11Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "pocketbase/pocketbase",
 | 
				
			||||||
 | 
					    "version": "v0.30.0",
 | 
				
			||||||
 | 
					    "date": "2025-09-07T05:25:44Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Lidarr/Lidarr",
 | 
				
			||||||
 | 
					    "version": "v2.13.3.4711",
 | 
				
			||||||
 | 
					    "date": "2025-08-28T20:06:24Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "rcourtman/Pulse",
 | 
				
			||||||
 | 
					    "version": "v4.14.0",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T18:28:28Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "bunkerity/bunkerweb",
 | 
				
			||||||
 | 
					    "version": "v1.6.4",
 | 
				
			||||||
 | 
					    "date": "2025-08-18T20:22:07Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "fuma-nama/fumadocs",
 | 
				
			||||||
 | 
					    "version": "create-fumadocs-app@15.7.10",
 | 
				
			||||||
 | 
					    "date": "2025-09-06T10:13:43Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Luligu/matterbridge",
 | 
				
			||||||
 | 
					    "version": "3.2.6",
 | 
				
			||||||
 | 
					    "date": "2025-09-06T07:50:15Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "forgejo/forgejo",
 | 
				
			||||||
 | 
					    "version": "v12.0.3",
 | 
				
			||||||
 | 
					    "date": "2025-09-06T07:01:44Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "moghtech/komodo",
 | 
				
			||||||
 | 
					    "version": "v1.19.3",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T21:32:38Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "homarr-labs/homarr",
 | 
				
			||||||
 | 
					    "version": "v1.36.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T21:14:40Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "gtsteffaniak/filebrowser",
 | 
				
			||||||
 | 
					    "version": "v0.8.4-beta",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T19:34:44Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Stirling-Tools/Stirling-PDF",
 | 
				
			||||||
 | 
					    "version": "v1.3.2",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T18:44:15Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "henrygd/beszel",
 | 
				
			||||||
 | 
					    "version": "v0.12.7",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T18:11:36Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "keycloak/keycloak",
 | 
				
			||||||
 | 
					    "version": "26.0.15",
 | 
				
			||||||
 | 
					    "date": "2025-08-27T12:12:03Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "Brandawg93/PeaNUT",
 | 
				
			||||||
 | 
					    "version": "v5.14.2",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T17:24:12Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "wizarrrr/wizarr",
 | 
				
			||||||
 | 
					    "version": "2025.9.0",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T14:21:34Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "chrisvel/tududi",
 | 
				
			||||||
 | 
					    "version": "v0.80",
 | 
				
			||||||
 | 
					    "date": "2025-07-24T14:12:39Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "home-assistant/core",
 | 
				
			||||||
 | 
					    "version": "2025.9.1",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T11:15:21Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "CrazyWolf13/streamlink-webui",
 | 
				
			||||||
 | 
					    "version": "0.6",
 | 
				
			||||||
 | 
					    "date": "2025-09-05T06:05:04Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "name": "louislam/uptime-kuma",
 | 
				
			||||||
 | 
					    "version": "2.0.0-beta.2-temp",
 | 
				
			||||||
 | 
					    "date": "2025-03-28T08:45:58Z"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "wazuh/wazuh",
 | 
					    "name": "wazuh/wazuh",
 | 
				
			||||||
    "version": "coverity-w36-4.13.0",
 | 
					    "version": "coverity-w36-4.13.0",
 | 
				
			||||||
@@ -19,66 +299,26 @@
 | 
				
			|||||||
    "version": "v1.0.0-beta17",
 | 
					    "version": "v1.0.0-beta17",
 | 
				
			||||||
    "date": "2025-09-04T21:30:14Z"
 | 
					    "date": "2025-09-04T21:30:14Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "rcourtman/Pulse",
 | 
					 | 
				
			||||||
    "version": "v4.13.0",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T13:48:17Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "TandoorRecipes/recipes",
 | 
					 | 
				
			||||||
    "version": "2.1.0",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T20:24:47Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "mongodb/mongo",
 | 
					 | 
				
			||||||
    "version": "r7.0.24",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T19:50:49Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "immich-app/immich",
 | 
					 | 
				
			||||||
    "version": "v1.141.0",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T19:42:02Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "Cleanuparr/Cleanuparr",
 | 
					    "name": "Cleanuparr/Cleanuparr",
 | 
				
			||||||
    "version": "v2.2.3",
 | 
					    "version": "v2.2.3",
 | 
				
			||||||
    "date": "2025-09-04T19:24:39Z"
 | 
					    "date": "2025-09-04T19:24:39Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "HabitRPG/habitica",
 | 
					 | 
				
			||||||
    "version": "v5.40.1",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T19:10:45Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "AdguardTeam/AdGuardHome",
 | 
					    "name": "AdguardTeam/AdGuardHome",
 | 
				
			||||||
    "version": "v0.107.65",
 | 
					    "version": "v0.107.65",
 | 
				
			||||||
    "date": "2025-08-20T14:02:28Z"
 | 
					    "date": "2025-08-20T14:02:28Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "webmin/webmin",
 | 
					 | 
				
			||||||
    "version": "2.500",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T17:44:27Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "ollama/ollama",
 | 
					    "name": "ollama/ollama",
 | 
				
			||||||
    "version": "v0.11.10",
 | 
					    "version": "v0.11.10",
 | 
				
			||||||
    "date": "2025-09-04T17:27:40Z"
 | 
					    "date": "2025-09-04T17:27:40Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "MediaBrowser/Emby.Releases",
 | 
					 | 
				
			||||||
    "version": "4.9.1.2",
 | 
					 | 
				
			||||||
    "date": "2025-06-26T22:08:00Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "NodeBB/NodeBB",
 | 
					    "name": "NodeBB/NodeBB",
 | 
				
			||||||
    "version": "v4.5.1",
 | 
					    "version": "v4.5.1",
 | 
				
			||||||
    "date": "2025-09-04T16:02:49Z"
 | 
					    "date": "2025-09-04T16:02:49Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "semaphoreui/semaphore",
 | 
					 | 
				
			||||||
    "version": "v2.16.19",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T14:22:02Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "raydak-labs/configarr",
 | 
					    "name": "raydak-labs/configarr",
 | 
				
			||||||
    "version": "v1.15.1",
 | 
					    "version": "v1.15.1",
 | 
				
			||||||
@@ -114,56 +354,21 @@
 | 
				
			|||||||
    "version": "v1.5.3-beta.10",
 | 
					    "version": "v1.5.3-beta.10",
 | 
				
			||||||
    "date": "2025-07-15T06:07:03Z"
 | 
					    "date": "2025-07-15T06:07:03Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Jackett/Jackett",
 | 
					 | 
				
			||||||
    "version": "v0.22.2418",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T05:53:46Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "actualbudget/actual",
 | 
					    "name": "actualbudget/actual",
 | 
				
			||||||
    "version": "v25.9.0",
 | 
					    "version": "v25.9.0",
 | 
				
			||||||
    "date": "2025-09-04T01:12:37Z"
 | 
					    "date": "2025-09-04T01:12:37Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "jeedom/core",
 | 
					 | 
				
			||||||
    "version": "4.4.19",
 | 
					 | 
				
			||||||
    "date": "2025-09-04T00:27:08Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "hyperion-project/hyperion.ng",
 | 
					    "name": "hyperion-project/hyperion.ng",
 | 
				
			||||||
    "version": "2.1.1",
 | 
					    "version": "2.1.1",
 | 
				
			||||||
    "date": "2025-06-14T17:45:06Z"
 | 
					    "date": "2025-06-14T17:45:06Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "steveiliop56/tinyauth",
 | 
					 | 
				
			||||||
    "version": "v3.6.2",
 | 
					 | 
				
			||||||
    "date": "2025-07-17T12:08:03Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "VictoriaMetrics/VictoriaMetrics",
 | 
					    "name": "VictoriaMetrics/VictoriaMetrics",
 | 
				
			||||||
    "version": "pmm-6401-v1.125.1",
 | 
					    "version": "pmm-6401-v1.125.1",
 | 
				
			||||||
    "date": "2025-09-03T20:17:18Z"
 | 
					    "date": "2025-09-03T20:17:18Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "keycloak/keycloak",
 | 
					 | 
				
			||||||
    "version": "26.0.15",
 | 
					 | 
				
			||||||
    "date": "2025-08-27T12:12:03Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "homarr-labs/homarr",
 | 
					 | 
				
			||||||
    "version": "v1.35.1",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T18:53:36Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "home-assistant/core",
 | 
					 | 
				
			||||||
    "version": "2025.9.0",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T17:58:41Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "zitadel/zitadel",
 | 
					 | 
				
			||||||
    "version": "v4.1.1",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T15:12:56Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "Graylog2/graylog2-server",
 | 
					    "name": "Graylog2/graylog2-server",
 | 
				
			||||||
    "version": "6.1.15",
 | 
					    "version": "6.1.15",
 | 
				
			||||||
@@ -174,31 +379,16 @@
 | 
				
			|||||||
    "version": "10.0.19",
 | 
					    "version": "10.0.19",
 | 
				
			||||||
    "date": "2025-07-16T09:45:14Z"
 | 
					    "date": "2025-07-16T09:45:14Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Radarr/Radarr",
 | 
					 | 
				
			||||||
    "version": "v5.27.5.10198",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T12:08:43Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "neo4j/neo4j",
 | 
					    "name": "neo4j/neo4j",
 | 
				
			||||||
    "version": "5.26.12",
 | 
					    "version": "5.26.12",
 | 
				
			||||||
    "date": "2025-09-03T12:03:22Z"
 | 
					    "date": "2025-09-03T12:03:22Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "n8n-io/n8n",
 | 
					 | 
				
			||||||
    "version": "n8n@1.109.2",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T07:50:21Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "Checkmk/checkmk",
 | 
					    "name": "Checkmk/checkmk",
 | 
				
			||||||
    "version": "v2.4.0p11",
 | 
					    "version": "v2.4.0p11",
 | 
				
			||||||
    "date": "2025-09-03T09:58:14Z"
 | 
					    "date": "2025-09-03T09:58:14Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "mattermost/mattermost",
 | 
					 | 
				
			||||||
    "version": "server/public/v0.1.17",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T21:38:40Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "cockpit-project/cockpit",
 | 
					    "name": "cockpit-project/cockpit",
 | 
				
			||||||
    "version": "346",
 | 
					    "version": "346",
 | 
				
			||||||
@@ -214,31 +404,11 @@
 | 
				
			|||||||
    "version": "cassandra-4.1.10",
 | 
					    "version": "cassandra-4.1.10",
 | 
				
			||||||
    "date": "2025-09-03T08:46:02Z"
 | 
					    "date": "2025-09-03T08:46:02Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "syncthing/syncthing",
 | 
					 | 
				
			||||||
    "version": "v2.0.6",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T07:55:14Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "booklore-app/booklore",
 | 
					 | 
				
			||||||
    "version": "v1.1.2",
 | 
					 | 
				
			||||||
    "date": "2025-09-03T03:18:53Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "crafty-controller/crafty-4",
 | 
					    "name": "crafty-controller/crafty-4",
 | 
				
			||||||
    "version": "v4.5.3",
 | 
					    "version": "v4.5.3",
 | 
				
			||||||
    "date": "2025-09-02T23:52:26Z"
 | 
					    "date": "2025-09-02T23:52:26Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "runtipi/runtipi",
 | 
					 | 
				
			||||||
    "version": "nightly",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T19:26:18Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Luligu/matterbridge",
 | 
					 | 
				
			||||||
    "version": "3.2.5",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T17:25:13Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "netbox-community/netbox",
 | 
					    "name": "netbox-community/netbox",
 | 
				
			||||||
    "version": "v4.4.0",
 | 
					    "version": "v4.4.0",
 | 
				
			||||||
@@ -254,16 +424,6 @@
 | 
				
			|||||||
    "version": "v4.1.4",
 | 
					    "version": "v4.1.4",
 | 
				
			||||||
    "date": "2025-09-02T14:26:24Z"
 | 
					    "date": "2025-09-02T14:26:24Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "apache/tomcat",
 | 
					 | 
				
			||||||
    "version": "10.1.45",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T12:46:34Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "fuma-nama/fumadocs",
 | 
					 | 
				
			||||||
    "version": "fumadocs-ui@15.7.8",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T11:29:00Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "Dolibarr/dolibarr",
 | 
					    "name": "Dolibarr/dolibarr",
 | 
				
			||||||
    "version": "22.0.1",
 | 
					    "version": "22.0.1",
 | 
				
			||||||
@@ -274,16 +434,6 @@
 | 
				
			|||||||
    "version": "4.7.0",
 | 
					    "version": "4.7.0",
 | 
				
			||||||
    "date": "2025-09-02T06:02:43Z"
 | 
					    "date": "2025-09-02T06:02:43Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "firefly-iii/firefly-iii",
 | 
					 | 
				
			||||||
    "version": "v6.3.2",
 | 
					 | 
				
			||||||
    "date": "2025-08-19T04:08:36Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "paperless-ngx/paperless-ngx",
 | 
					 | 
				
			||||||
    "version": "v2.18.3",
 | 
					 | 
				
			||||||
    "date": "2025-09-02T01:16:49Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "ErsatzTV/ErsatzTV",
 | 
					    "name": "ErsatzTV/ErsatzTV",
 | 
				
			||||||
    "version": "v25.5.0",
 | 
					    "version": "v25.5.0",
 | 
				
			||||||
@@ -314,11 +464,6 @@
 | 
				
			|||||||
    "version": "v0.87.3",
 | 
					    "version": "v0.87.3",
 | 
				
			||||||
    "date": "2025-09-01T16:25:43Z"
 | 
					    "date": "2025-09-01T16:25:43Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "home-assistant/operating-system",
 | 
					 | 
				
			||||||
    "version": "16.1",
 | 
					 | 
				
			||||||
    "date": "2025-08-13T07:58:10Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "seanmorley15/AdventureLog",
 | 
					    "name": "seanmorley15/AdventureLog",
 | 
				
			||||||
    "version": "v0.11.0",
 | 
					    "version": "v0.11.0",
 | 
				
			||||||
@@ -334,11 +479,6 @@
 | 
				
			|||||||
    "version": "preview-issue-description",
 | 
					    "version": "preview-issue-description",
 | 
				
			||||||
    "date": "2025-09-01T12:21:58Z"
 | 
					    "date": "2025-09-01T12:21:58Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "nzbgetcom/nzbget",
 | 
					 | 
				
			||||||
    "version": "v25.3",
 | 
					 | 
				
			||||||
    "date": "2025-09-01T09:47:06Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "grokability/snipe-it",
 | 
					    "name": "grokability/snipe-it",
 | 
				
			||||||
    "version": "v8.3.1",
 | 
					    "version": "v8.3.1",
 | 
				
			||||||
@@ -354,71 +494,16 @@
 | 
				
			|||||||
    "version": "0.20.1",
 | 
					    "version": "0.20.1",
 | 
				
			||||||
    "date": "2025-09-01T07:35:48Z"
 | 
					    "date": "2025-09-01T07:35:48Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "moghtech/komodo",
 | 
					 | 
				
			||||||
    "version": "v1.19.2",
 | 
					 | 
				
			||||||
    "date": "2025-09-01T02:33:52Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "YunoHost/yunohost",
 | 
					 | 
				
			||||||
    "version": "debian/12.1.17.1",
 | 
					 | 
				
			||||||
    "date": "2025-08-31T21:38:21Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "wizarrrr/wizarr",
 | 
					 | 
				
			||||||
    "version": "2025.8.5",
 | 
					 | 
				
			||||||
    "date": "2025-08-31T17:56:37Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "karakeep-app/karakeep",
 | 
					 | 
				
			||||||
    "version": "ios/v1.8.0-1",
 | 
					 | 
				
			||||||
    "date": "2025-08-31T16:06:00Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "LibreTranslate/LibreTranslate",
 | 
					    "name": "LibreTranslate/LibreTranslate",
 | 
				
			||||||
    "version": "v1.7.3",
 | 
					    "version": "v1.7.3",
 | 
				
			||||||
    "date": "2025-08-31T15:59:43Z"
 | 
					    "date": "2025-08-31T15:59:43Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Part-DB/Part-DB-server",
 | 
					 | 
				
			||||||
    "version": "v2.0.2",
 | 
					 | 
				
			||||||
    "date": "2025-08-31T13:52:49Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "forgejo/forgejo",
 | 
					 | 
				
			||||||
    "version": "v12.0.2",
 | 
					 | 
				
			||||||
    "date": "2025-08-31T13:15:47Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Lidarr/Lidarr",
 | 
					 | 
				
			||||||
    "version": "v2.13.3.4711",
 | 
					 | 
				
			||||||
    "date": "2025-08-28T20:06:24Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "jhuckaby/Cronicle",
 | 
					    "name": "jhuckaby/Cronicle",
 | 
				
			||||||
    "version": "v0.9.91",
 | 
					    "version": "v0.9.91",
 | 
				
			||||||
    "date": "2025-08-30T21:49:57Z"
 | 
					    "date": "2025-08-30T21:49:57Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Brandawg93/PeaNUT",
 | 
					 | 
				
			||||||
    "version": "v5.14.0",
 | 
					 | 
				
			||||||
    "date": "2025-08-30T18:41:03Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "bunkerity/bunkerweb",
 | 
					 | 
				
			||||||
    "version": "v1.6.4",
 | 
					 | 
				
			||||||
    "date": "2025-08-18T20:22:07Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "henrygd/beszel",
 | 
					 | 
				
			||||||
    "version": "v0.12.6",
 | 
					 | 
				
			||||||
    "date": "2025-08-29T21:40:09Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "msgbyte/tianji",
 | 
					 | 
				
			||||||
    "version": "v1.24.26",
 | 
					 | 
				
			||||||
    "date": "2025-08-29T20:26:08Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "silverbulletmd/silverbullet",
 | 
					    "name": "silverbulletmd/silverbullet",
 | 
				
			||||||
    "version": "2.0.0",
 | 
					    "version": "2.0.0",
 | 
				
			||||||
@@ -434,21 +519,11 @@
 | 
				
			|||||||
    "version": "v3007.7",
 | 
					    "version": "v3007.7",
 | 
				
			||||||
    "date": "2025-08-29T01:19:08Z"
 | 
					    "date": "2025-08-29T01:19:08Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "9001/copyparty",
 | 
					 | 
				
			||||||
    "version": "v1.19.7",
 | 
					 | 
				
			||||||
    "date": "2025-08-28T20:42:58Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "linkwarden/linkwarden",
 | 
					    "name": "linkwarden/linkwarden",
 | 
				
			||||||
    "version": "v2.12.2",
 | 
					    "version": "v2.12.2",
 | 
				
			||||||
    "date": "2025-08-28T20:34:30Z"
 | 
					    "date": "2025-08-28T20:34:30Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "dgtlmoon/changedetection.io",
 | 
					 | 
				
			||||||
    "version": "0.50.11",
 | 
					 | 
				
			||||||
    "date": "2025-08-28T20:12:30Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "benjaminjonard/koillection",
 | 
					    "name": "benjaminjonard/koillection",
 | 
				
			||||||
    "version": "1.7.0",
 | 
					    "version": "1.7.0",
 | 
				
			||||||
@@ -499,41 +574,16 @@
 | 
				
			|||||||
    "version": "v0.37.0",
 | 
					    "version": "v0.37.0",
 | 
				
			||||||
    "date": "2025-08-27T20:03:52Z"
 | 
					    "date": "2025-08-27T20:03:52Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "gtsteffaniak/filebrowser",
 | 
					 | 
				
			||||||
    "version": "v0.8.3-beta",
 | 
					 | 
				
			||||||
    "date": "2025-08-27T18:18:48Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "zwave-js/zwave-js-ui",
 | 
					    "name": "zwave-js/zwave-js-ui",
 | 
				
			||||||
    "version": "v11.2.1",
 | 
					    "version": "v11.2.1",
 | 
				
			||||||
    "date": "2025-08-27T15:19:02Z"
 | 
					    "date": "2025-08-27T15:19:02Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Paymenter/Paymenter",
 | 
					 | 
				
			||||||
    "version": "v1.2.11",
 | 
					 | 
				
			||||||
    "date": "2025-08-27T11:23:09Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "traefik/traefik",
 | 
					    "name": "traefik/traefik",
 | 
				
			||||||
    "version": "v3.5.1",
 | 
					    "version": "v3.5.1",
 | 
				
			||||||
    "date": "2025-08-27T09:21:19Z"
 | 
					    "date": "2025-08-27T09:21:19Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "theonedev/onedev",
 | 
					 | 
				
			||||||
    "version": "v12.0.8",
 | 
					 | 
				
			||||||
    "date": "2025-08-27T06:22:32Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "meilisearch/meilisearch",
 | 
					 | 
				
			||||||
    "version": "latest",
 | 
					 | 
				
			||||||
    "date": "2025-08-26T14:14:42Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "evcc-io/evcc",
 | 
					 | 
				
			||||||
    "version": "0.207.5",
 | 
					 | 
				
			||||||
    "date": "2025-08-26T06:57:07Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "documenso/documenso",
 | 
					    "name": "documenso/documenso",
 | 
				
			||||||
    "version": "v1.12.2-rc.6",
 | 
					    "version": "v1.12.2-rc.6",
 | 
				
			||||||
@@ -584,11 +634,6 @@
 | 
				
			|||||||
    "version": "2.1",
 | 
					    "version": "2.1",
 | 
				
			||||||
    "date": "2025-08-24T15:42:19Z"
 | 
					    "date": "2025-08-24T15:42:19Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Prowlarr/Prowlarr",
 | 
					 | 
				
			||||||
    "version": "v2.0.5.5160",
 | 
					 | 
				
			||||||
    "date": "2025-08-23T21:23:11Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "janeczku/calibre-web",
 | 
					    "name": "janeczku/calibre-web",
 | 
				
			||||||
    "version": "0.6.25",
 | 
					    "version": "0.6.25",
 | 
				
			||||||
@@ -604,11 +649,6 @@
 | 
				
			|||||||
    "version": "v0.306.0-rc.0",
 | 
					    "version": "v0.306.0-rc.0",
 | 
				
			||||||
    "date": "2025-08-21T13:31:03Z"
 | 
					    "date": "2025-08-21T13:31:03Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "pocketbase/pocketbase",
 | 
					 | 
				
			||||||
    "version": "v0.29.3",
 | 
					 | 
				
			||||||
    "date": "2025-08-23T06:37:44Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "caddyserver/caddy",
 | 
					    "name": "caddyserver/caddy",
 | 
				
			||||||
    "version": "v2.10.2",
 | 
					    "version": "v2.10.2",
 | 
				
			||||||
@@ -679,21 +719,11 @@
 | 
				
			|||||||
    "version": "v5.23.2",
 | 
					    "version": "v5.23.2",
 | 
				
			||||||
    "date": "2025-08-19T21:24:45Z"
 | 
					    "date": "2025-08-19T21:24:45Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "cross-seed/cross-seed",
 | 
					 | 
				
			||||||
    "version": "v6.13.2",
 | 
					 | 
				
			||||||
    "date": "2025-08-19T18:18:40Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "karlomikus/bar-assistant",
 | 
					    "name": "karlomikus/bar-assistant",
 | 
				
			||||||
    "version": "v5.8.0",
 | 
					    "version": "v5.8.0",
 | 
				
			||||||
    "date": "2025-08-19T16:46:00Z"
 | 
					    "date": "2025-08-19T16:46:00Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "chrisvel/tududi",
 | 
					 | 
				
			||||||
    "version": "v0.80",
 | 
					 | 
				
			||||||
    "date": "2025-07-24T14:12:39Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "oauth2-proxy/oauth2-proxy",
 | 
					    "name": "oauth2-proxy/oauth2-proxy",
 | 
				
			||||||
    "version": "v7.12.0",
 | 
					    "version": "v7.12.0",
 | 
				
			||||||
@@ -794,16 +824,6 @@
 | 
				
			|||||||
    "version": "1012-08-09",
 | 
					    "version": "1012-08-09",
 | 
				
			||||||
    "date": "2025-08-10T13:50:58Z"
 | 
					    "date": "2025-08-10T13:50:58Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "diced/zipline",
 | 
					 | 
				
			||||||
    "version": "v4.2.3",
 | 
					 | 
				
			||||||
    "date": "2025-08-10T05:53:43Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "Stirling-Tools/Stirling-PDF",
 | 
					 | 
				
			||||||
    "version": "v1.2.0",
 | 
					 | 
				
			||||||
    "date": "2025-08-09T14:30:52Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "kimai/kimai",
 | 
					    "name": "kimai/kimai",
 | 
				
			||||||
    "version": "2.38.0",
 | 
					    "version": "2.38.0",
 | 
				
			||||||
@@ -839,11 +859,6 @@
 | 
				
			|||||||
    "version": "v1.28.3",
 | 
					    "version": "v1.28.3",
 | 
				
			||||||
    "date": "2025-08-06T12:32:02Z"
 | 
					    "date": "2025-08-06T12:32:02Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "autobrr/autobrr",
 | 
					 | 
				
			||||||
    "version": "v1.65.0",
 | 
					 | 
				
			||||||
    "date": "2025-08-05T21:12:35Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "WordPress/WordPress",
 | 
					    "name": "WordPress/WordPress",
 | 
				
			||||||
    "version": "4.7.30",
 | 
					    "version": "4.7.30",
 | 
				
			||||||
@@ -944,11 +959,6 @@
 | 
				
			|||||||
    "version": "2.9.0",
 | 
					    "version": "2.9.0",
 | 
				
			||||||
    "date": "2025-07-25T14:20:25Z"
 | 
					    "date": "2025-07-25T14:20:25Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "minio/minio",
 | 
					 | 
				
			||||||
    "version": "RELEASE.2025-07-23T15-54-02Z",
 | 
					 | 
				
			||||||
    "date": "2025-07-23T20:35:39Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "TasmoAdmin/TasmoAdmin",
 | 
					    "name": "TasmoAdmin/TasmoAdmin",
 | 
				
			||||||
    "version": "v4.3.1",
 | 
					    "version": "v4.3.1",
 | 
				
			||||||
@@ -1134,11 +1144,6 @@
 | 
				
			|||||||
    "version": "v0.14.1",
 | 
					    "version": "v0.14.1",
 | 
				
			||||||
    "date": "2025-06-04T08:57:15Z"
 | 
					    "date": "2025-06-04T08:57:15Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "louislam/uptime-kuma",
 | 
					 | 
				
			||||||
    "version": "2.0.0-beta.2-temp",
 | 
					 | 
				
			||||||
    "date": "2025-03-28T08:45:58Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "Pf2eToolsOrg/Pf2eTools",
 | 
					    "name": "Pf2eToolsOrg/Pf2eTools",
 | 
				
			||||||
    "version": "v0.9.0",
 | 
					    "version": "v0.9.0",
 | 
				
			||||||
@@ -1159,11 +1164,6 @@
 | 
				
			|||||||
    "version": "v0.26.2",
 | 
					    "version": "v0.26.2",
 | 
				
			||||||
    "date": "2025-05-22T05:24:42Z"
 | 
					    "date": "2025-05-22T05:24:42Z"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "name": "CrazyWolf13/streamlink-webui",
 | 
					 | 
				
			||||||
    "version": "0.5",
 | 
					 | 
				
			||||||
    "date": "2025-05-21T20:19:14Z"
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    "name": "hansmi/prometheus-paperless-exporter",
 | 
					    "name": "hansmi/prometheus-paperless-exporter",
 | 
				
			||||||
    "version": "v0.0.8",
 | 
					    "version": "v0.0.8",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,13 @@ function CategoryView() {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleScriptClick = (scriptSlug: string) => {
 | 
					  const handleScriptClick = (scriptSlug: string) => {
 | 
				
			||||||
    router.push(`/scripts?id=${scriptSlug}`);
 | 
					    // Include category context when navigating to scripts
 | 
				
			||||||
 | 
					    const categoryName = selectedCategoryIndex !== null ? categories[selectedCategoryIndex]?.name : null;
 | 
				
			||||||
 | 
					    const queryParams = new URLSearchParams({ id: scriptSlug });
 | 
				
			||||||
 | 
					    if (categoryName) {
 | 
				
			||||||
 | 
					      queryParams.append("category", categoryName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    router.push(`/scripts?${queryParams.toString()}`);
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const navigateCategory = (direction: "prev" | "next") => {
 | 
					  const navigateCategory = (direction: "prev" | "next") => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,7 @@ const DataFetcher: React.FC = () => {
 | 
				
			|||||||
  const [currentPage, setCurrentPage] = useState(1);
 | 
					  const [currentPage, setCurrentPage] = useState(1);
 | 
				
			||||||
  const [itemsPerPage, setItemsPerPage] = useState(25);
 | 
					  const [itemsPerPage, setItemsPerPage] = useState(25);
 | 
				
			||||||
  const [sortConfig, setSortConfig] = useState<{ key: string; direction: "ascending" | "descending" } | null>(null);
 | 
					  const [sortConfig, setSortConfig] = useState<{ key: string; direction: "ascending" | "descending" } | null>(null);
 | 
				
			||||||
 | 
					  const nf = new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const fetchSummary = async () => {
 | 
					    const fetchSummary = async () => {
 | 
				
			||||||
@@ -129,19 +130,24 @@ const DataFetcher: React.FC = () => {
 | 
				
			|||||||
      <p className="text-lg font-bold mt-4"> </p>
 | 
					      <p className="text-lg font-bold mt-4"> </p>
 | 
				
			||||||
      <div className="mb-4 flex justify-between items-center">
 | 
					      <div className="mb-4 flex justify-between items-center">
 | 
				
			||||||
        <p className="text-lg font-bold">
 | 
					        <p className="text-lg font-bold">
 | 
				
			||||||
          {summary?.total_entries}
 | 
					          {nf.format(
 | 
				
			||||||
 | 
					            summary?.total_entries ?? 0,
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          results found
 | 
					          results found
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
        <p className="text-lg font">
 | 
					        <p className="text-lg font">
 | 
				
			||||||
          Status Legend: 🔄 installing
 | 
					          Status Legend: 🔄 installing
 | 
				
			||||||
          {summary?.status_count.installing ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.installing ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ✔️ completed
 | 
					          | ✔️ completed
 | 
				
			||||||
          {summary?.status_count.done ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.done ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ❌ failed
 | 
					          | ❌ failed
 | 
				
			||||||
          {summary?.status_count.failed ?? 0}
 | 
					          {" "}
 | 
				
			||||||
 | 
					          {nf.format(summary?.status_count.failed ?? 0)}
 | 
				
			||||||
          {" "}
 | 
					          {" "}
 | 
				
			||||||
          | ❓ unknown
 | 
					          | ❓ unknown
 | 
				
			||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,7 @@ import Link from "next/link";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import type { Category } from "@/lib/types";
 | 
					import type { Category } from "@/lib/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
 | 
				
			||||||
  Accordion,
 | 
					 | 
				
			||||||
  AccordionContent,
 | 
					 | 
				
			||||||
  AccordionItem,
 | 
					 | 
				
			||||||
  AccordionTrigger,
 | 
					 | 
				
			||||||
} from "@/components/ui/accordion";
 | 
					 | 
				
			||||||
import { formattedBadge } from "@/components/command-menu";
 | 
					import { formattedBadge } from "@/components/command-menu";
 | 
				
			||||||
import { basePath } from "@/config/site-config";
 | 
					import { basePath } from "@/config/site-config";
 | 
				
			||||||
import { cn } from "@/lib/utils";
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
@@ -18,14 +13,16 @@ export default function ScriptAccordion({
 | 
				
			|||||||
  items,
 | 
					  items,
 | 
				
			||||||
  selectedScript,
 | 
					  selectedScript,
 | 
				
			||||||
  setSelectedScript,
 | 
					  setSelectedScript,
 | 
				
			||||||
 | 
					  selectedCategory,
 | 
				
			||||||
 | 
					  setSelectedCategory,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  items: Category[];
 | 
					  items: Category[];
 | 
				
			||||||
  selectedScript: string | null;
 | 
					  selectedScript: string | null;
 | 
				
			||||||
  setSelectedScript: (script: string | null) => void;
 | 
					  setSelectedScript: (script: string | null) => void;
 | 
				
			||||||
 | 
					  selectedCategory: string | null;
 | 
				
			||||||
 | 
					  setSelectedCategory: (category: string | null) => void;
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
  const [expandedItem, setExpandedItem] = useState<string | undefined>(
 | 
					  const [expandedItem, setExpandedItem] = useState<string | undefined>(undefined);
 | 
				
			||||||
    undefined,
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  const linkRefs = useRef<{ [key: string]: HTMLAnchorElement | null }>({});
 | 
					  const linkRefs = useRef<{ [key: string]: HTMLAnchorElement | null }>({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleAccordionChange = (value: string | undefined) => {
 | 
					  const handleAccordionChange = (value: string | undefined) => {
 | 
				
			||||||
@@ -41,15 +38,27 @@ export default function ScriptAccordion({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    if (selectedScript) {
 | 
					    if (selectedScript) {
 | 
				
			||||||
      const category = items.find(category =>
 | 
					      let category;
 | 
				
			||||||
        category.scripts.some(script => script.slug === selectedScript),
 | 
					
 | 
				
			||||||
      );
 | 
					      // If we have a selected category, try to find the script in that specific category
 | 
				
			||||||
 | 
					      if (selectedCategory) {
 | 
				
			||||||
 | 
					        category = items.find(
 | 
				
			||||||
 | 
					          cat => cat.name === selectedCategory && cat.scripts.some(script => script.slug === selectedScript),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Fallback: if no category is selected or script not found in selected category,
 | 
				
			||||||
 | 
					      // use the first category containing the script (backward compatibility)
 | 
				
			||||||
 | 
					      if (!category) {
 | 
				
			||||||
 | 
					        category = items.find(category => category.scripts.some(script => script.slug === selectedScript));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (category) {
 | 
					      if (category) {
 | 
				
			||||||
        setExpandedItem(category.name);
 | 
					        setExpandedItem(category.name);
 | 
				
			||||||
        handleSelected(selectedScript);
 | 
					        handleSelected(selectedScript);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, [selectedScript, items, handleSelected]);
 | 
					  }, [selectedScript, selectedCategory, items, handleSelected]);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Accordion
 | 
					    <Accordion
 | 
				
			||||||
      type="single"
 | 
					      type="single"
 | 
				
			||||||
@@ -82,10 +91,7 @@ export default function ScriptAccordion({
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            {" "}
 | 
					            {" "}
 | 
				
			||||||
          </AccordionTrigger>
 | 
					          </AccordionTrigger>
 | 
				
			||||||
          <AccordionContent
 | 
					          <AccordionContent data-state={expandedItem === category.name ? "open" : "closed"} className="pt-0">
 | 
				
			||||||
            data-state={expandedItem === category.name ? "open" : "closed"}
 | 
					 | 
				
			||||||
            className="pt-0"
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
            {category.scripts
 | 
					            {category.scripts
 | 
				
			||||||
              .slice()
 | 
					              .slice()
 | 
				
			||||||
              .sort((a, b) => a.name.localeCompare(b.name))
 | 
					              .sort((a, b) => a.name.localeCompare(b.name))
 | 
				
			||||||
@@ -94,7 +100,7 @@ export default function ScriptAccordion({
 | 
				
			|||||||
                  <Link
 | 
					                  <Link
 | 
				
			||||||
                    href={{
 | 
					                    href={{
 | 
				
			||||||
                      pathname: "/scripts",
 | 
					                      pathname: "/scripts",
 | 
				
			||||||
                      query: { id: script.slug },
 | 
					                      query: { id: script.slug, category: category.name },
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                    prefetch={false}
 | 
					                    prefetch={false}
 | 
				
			||||||
                    className={`flex cursor-pointer items-center justify-between gap-1 px-1 py-1 text-muted-foreground hover:rounded-lg hover:bg-accent/60 hover:dark:bg-accent/20 ${
 | 
					                    className={`flex cursor-pointer items-center justify-between gap-1 px-1 py-1 text-muted-foreground hover:rounded-lg hover:bg-accent/60 hover:dark:bg-accent/20 ${
 | 
				
			||||||
@@ -102,7 +108,10 @@ export default function ScriptAccordion({
 | 
				
			|||||||
                        ? "rounded-lg bg-accent font-semibold dark:bg-accent/30 dark:text-white"
 | 
					                        ? "rounded-lg bg-accent font-semibold dark:bg-accent/30 dark:text-white"
 | 
				
			||||||
                        : ""
 | 
					                        : ""
 | 
				
			||||||
                    }`}
 | 
					                    }`}
 | 
				
			||||||
                    onClick={() => handleSelected(script.slug)}
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      handleSelected(script.slug);
 | 
				
			||||||
 | 
					                      setSelectedCategory(category.name);
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
                    ref={(el) => {
 | 
					                    ref={(el) => {
 | 
				
			||||||
                      linkRefs.current[script.slug] = el;
 | 
					                      linkRefs.current[script.slug] = el;
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
@@ -113,15 +122,11 @@ export default function ScriptAccordion({
 | 
				
			|||||||
                        height={16}
 | 
					                        height={16}
 | 
				
			||||||
                        width={16}
 | 
					                        width={16}
 | 
				
			||||||
                        unoptimized
 | 
					                        unoptimized
 | 
				
			||||||
                        onError={e =>
 | 
					                        onError={e => ((e.currentTarget as HTMLImageElement).src = `/${basePath}/logo.png`)}
 | 
				
			||||||
                          ((e.currentTarget as HTMLImageElement).src
 | 
					 | 
				
			||||||
                            = `/${basePath}/logo.png`)}
 | 
					 | 
				
			||||||
                        alt={script.name}
 | 
					                        alt={script.name}
 | 
				
			||||||
                        className="mr-1 w-4 h-4 rounded-full"
 | 
					                        className="mr-1 w-4 h-4 rounded-full"
 | 
				
			||||||
                      />
 | 
					                      />
 | 
				
			||||||
                      <span className="flex items-center gap-2">
 | 
					                      <span className="flex items-center gap-2">{script.name}</span>
 | 
				
			||||||
                        {script.name}
 | 
					 | 
				
			||||||
                      </span>
 | 
					 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    {formattedBadge(script.type)}
 | 
					                    {formattedBadge(script.type)}
 | 
				
			||||||
                  </Link>
 | 
					                  </Link>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,14 @@ function Sidebar({
 | 
				
			|||||||
  items,
 | 
					  items,
 | 
				
			||||||
  selectedScript,
 | 
					  selectedScript,
 | 
				
			||||||
  setSelectedScript,
 | 
					  setSelectedScript,
 | 
				
			||||||
 | 
					  selectedCategory,
 | 
				
			||||||
 | 
					  setSelectedCategory,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  items: Category[];
 | 
					  items: Category[];
 | 
				
			||||||
  selectedScript: string | null;
 | 
					  selectedScript: string | null;
 | 
				
			||||||
  setSelectedScript: (script: string | null) => void;
 | 
					  setSelectedScript: (script: string | null) => void;
 | 
				
			||||||
 | 
					  selectedCategory: string | null;
 | 
				
			||||||
 | 
					  setSelectedCategory: (category: string | null) => void;
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
  const uniqueScripts = items.reduce((acc, category) => {
 | 
					  const uniqueScripts = items.reduce((acc, category) => {
 | 
				
			||||||
    for (const script of category.scripts) {
 | 
					    for (const script of category.scripts) {
 | 
				
			||||||
@@ -37,6 +41,8 @@ function Sidebar({
 | 
				
			|||||||
          items={items}
 | 
					          items={items}
 | 
				
			||||||
          selectedScript={selectedScript}
 | 
					          selectedScript={selectedScript}
 | 
				
			||||||
          setSelectedScript={setSelectedScript}
 | 
					          setSelectedScript={setSelectedScript}
 | 
				
			||||||
 | 
					          selectedCategory={selectedCategory}
 | 
				
			||||||
 | 
					          setSelectedCategory={setSelectedCategory}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,16 +8,14 @@ import type { Category, Script } from "@/lib/types";
 | 
				
			|||||||
import { ScriptItem } from "@/app/scripts/_components/script-item";
 | 
					import { ScriptItem } from "@/app/scripts/_components/script-item";
 | 
				
			||||||
import { fetchCategories } from "@/lib/data";
 | 
					import { fetchCategories } from "@/lib/data";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import { LatestScripts, MostViewedScripts } from "./_components/script-info-blocks";
 | 
				
			||||||
  LatestScripts,
 | 
					 | 
				
			||||||
  MostViewedScripts,
 | 
					 | 
				
			||||||
} from "./_components/script-info-blocks";
 | 
					 | 
				
			||||||
import Sidebar from "./_components/sidebar";
 | 
					import Sidebar from "./_components/sidebar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const dynamic = "force-static";
 | 
					export const dynamic = "force-static";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function ScriptContent() {
 | 
					function ScriptContent() {
 | 
				
			||||||
  const [selectedScript, setSelectedScript] = useQueryState("id");
 | 
					  const [selectedScript, setSelectedScript] = useQueryState("id");
 | 
				
			||||||
 | 
					  const [selectedCategory, setSelectedCategory] = useQueryState("category");
 | 
				
			||||||
  const [links, setLinks] = useState<Category[]>([]);
 | 
					  const [links, setLinks] = useState<Category[]>([]);
 | 
				
			||||||
  const [item, setItem] = useState<Script>();
 | 
					  const [item, setItem] = useState<Script>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,6 +45,8 @@ function ScriptContent() {
 | 
				
			|||||||
            items={links}
 | 
					            items={links}
 | 
				
			||||||
            selectedScript={selectedScript}
 | 
					            selectedScript={selectedScript}
 | 
				
			||||||
            setSelectedScript={setSelectedScript}
 | 
					            setSelectedScript={setSelectedScript}
 | 
				
			||||||
 | 
					            selectedCategory={selectedCategory}
 | 
				
			||||||
 | 
					            setSelectedCategory={setSelectedCategory}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div className="mx-4 w-full sm:mx-0 sm:ml-4">
 | 
					        <div className="mx-4 w-full sm:mx-0 sm:ml-4">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { VariantProps } from "class-variance-authority";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cva } from "class-variance-authority";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { ButtonProps as ButtonPrimitiveProps } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Button as ButtonPrimitive,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonVariants = cva(
 | 
				
			||||||
 | 
					  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
 | 
				
			||||||
 | 
					        accent: "bg-accent text-accent-foreground shadow-xs hover:bg-accent/90",
 | 
				
			||||||
 | 
					        destructive:
 | 
				
			||||||
 | 
					          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
 | 
				
			||||||
 | 
					        outline:
 | 
				
			||||||
 | 
					          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
 | 
				
			||||||
 | 
					        secondary:
 | 
				
			||||||
 | 
					          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
 | 
				
			||||||
 | 
					        ghost:
 | 
				
			||||||
 | 
					          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
 | 
				
			||||||
 | 
					        link: "text-primary underline-offset-4 hover:underline",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      size: {
 | 
				
			||||||
 | 
					        "default": "h-9 px-4 py-2 has-[>svg]:px-3",
 | 
				
			||||||
 | 
					        "sm": "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
 | 
				
			||||||
 | 
					        "lg": "h-10 rounded-md px-6 has-[>svg]:px-4",
 | 
				
			||||||
 | 
					        "icon": "size-9",
 | 
				
			||||||
 | 
					        "icon-sm": "size-8 rounded-md",
 | 
				
			||||||
 | 
					        "icon-lg": "size-10 rounded-md",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					      size: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ButtonProps = ButtonPrimitiveProps & VariantProps<typeof buttonVariants>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Button({ className, variant, size, ...props }: ButtonProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ButtonPrimitive
 | 
				
			||||||
 | 
					      className={cn(buttonVariants({ variant, size, className }))}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Button, type ButtonProps, buttonVariants };
 | 
				
			||||||
@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					import type { VariantProps } from "class-variance-authority";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cva } from "class-variance-authority";
 | 
				
			||||||
 | 
					import { StarIcon } from "lucide-react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { ButtonProps as ButtonPrimitiveProps } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import type { GithubStarsProps } from "@/components/animate-ui/primitives/animate/github-stars";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  GithubStars,
 | 
				
			||||||
 | 
					  GithubStarsIcon,
 | 
				
			||||||
 | 
					  GithubStarsLogo,
 | 
				
			||||||
 | 
					  GithubStarsNumber,
 | 
				
			||||||
 | 
					  GithubStarsParticles,
 | 
				
			||||||
 | 
					} from "@/components/animate-ui/primitives/animate/github-stars";
 | 
				
			||||||
 | 
					import { Button as ButtonPrimitive } from "@/components/animate-ui/primitives/buttons/button";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonVariants = cva(
 | 
				
			||||||
 | 
					  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    variants: {
 | 
				
			||||||
 | 
					      variant: {
 | 
				
			||||||
 | 
					        default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
 | 
				
			||||||
 | 
					        accent: "bg-accent text-accent-foreground shadow-xs hover:bg-accent/90",
 | 
				
			||||||
 | 
					        outline:
 | 
				
			||||||
 | 
					          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
 | 
				
			||||||
 | 
					        ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      size: {
 | 
				
			||||||
 | 
					        default: "h-9 px-4 py-2 has-[>svg]:px-3",
 | 
				
			||||||
 | 
					        sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
 | 
				
			||||||
 | 
					        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    defaultVariants: {
 | 
				
			||||||
 | 
					      variant: "default",
 | 
				
			||||||
 | 
					      size: "default",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const buttonStarVariants = cva("", {
 | 
				
			||||||
 | 
					  variants: {
 | 
				
			||||||
 | 
					    variant: {
 | 
				
			||||||
 | 
					      default: "fill-neutral-700 stroke-neutral-700 dark:fill-neutral-300 dark:stroke-neutral-300",
 | 
				
			||||||
 | 
					      accent: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					      outline: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					      ghost: "fill-neutral-300 stroke-neutral-300 dark:fill-neutral-700 dark:stroke-neutral-700",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  defaultVariants: {
 | 
				
			||||||
 | 
					    variant: "default",
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GitHubStarsButtonProps = Omit<ButtonPrimitiveProps & GithubStarsProps, "asChild" | "children">
 | 
				
			||||||
 | 
					  & VariantProps<typeof buttonVariants>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GitHubStarsButton({
 | 
				
			||||||
 | 
					  className,
 | 
				
			||||||
 | 
					  username,
 | 
				
			||||||
 | 
					  repo,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  delay,
 | 
				
			||||||
 | 
					  inView,
 | 
				
			||||||
 | 
					  inViewMargin,
 | 
				
			||||||
 | 
					  inViewOnce,
 | 
				
			||||||
 | 
					  variant,
 | 
				
			||||||
 | 
					  size,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GitHubStarsButtonProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <GithubStars
 | 
				
			||||||
 | 
					      asChild
 | 
				
			||||||
 | 
					      username={username}
 | 
				
			||||||
 | 
					      repo={repo}
 | 
				
			||||||
 | 
					      value={value}
 | 
				
			||||||
 | 
					      delay={delay}
 | 
				
			||||||
 | 
					      inView={inView}
 | 
				
			||||||
 | 
					      inViewMargin={inViewMargin}
 | 
				
			||||||
 | 
					      inViewOnce={inViewOnce}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <ButtonPrimitive className={cn(buttonVariants({ variant, size, className }))} {...props}>
 | 
				
			||||||
 | 
					        <GithubStarsLogo />
 | 
				
			||||||
 | 
					        <GithubStarsNumber />
 | 
				
			||||||
 | 
					        <GithubStarsParticles className="text-yellow-500">
 | 
				
			||||||
 | 
					          <GithubStarsIcon
 | 
				
			||||||
 | 
					            icon={StarIcon}
 | 
				
			||||||
 | 
					            data-variant={variant}
 | 
				
			||||||
 | 
					            className={cn(buttonStarVariants({ variant }))}
 | 
				
			||||||
 | 
					            activeClassName="text-yellow-500"
 | 
				
			||||||
 | 
					            size={18}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </GithubStarsParticles>
 | 
				
			||||||
 | 
					      </ButtonPrimitive>
 | 
				
			||||||
 | 
					    </GithubStars>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { GitHubStarsButton, type GitHubStarsButtonProps };
 | 
				
			||||||
@@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { SlidingNumberProps } from "@/components/animate-ui/primitives/texts/sliding-number";
 | 
				
			||||||
 | 
					import type { ParticlesEffectProps } from "@/components/animate-ui/primitives/effects/particles";
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Particles, ParticlesEffect } from "@/components/animate-ui/primitives/effects/particles";
 | 
				
			||||||
 | 
					import { SlidingNumber } from "@/components/animate-ui/primitives/texts/sliding-number";
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import { getStrictContext } from "@/lib/get-strict-context";
 | 
				
			||||||
 | 
					import { useIsInView } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsContextType = {
 | 
				
			||||||
 | 
					  stars: number;
 | 
				
			||||||
 | 
					  setStars: (stars: number) => void;
 | 
				
			||||||
 | 
					  currentStars: number;
 | 
				
			||||||
 | 
					  setCurrentStars: (stars: number) => void;
 | 
				
			||||||
 | 
					  isCompleted: boolean;
 | 
				
			||||||
 | 
					  isLoading: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [GithubStarsProvider, useGithubStars] = getStrictContext<GithubStarsContextType>("GithubStarsContext");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsProps = WithAsChild<
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    children: React.ReactNode;
 | 
				
			||||||
 | 
					    username?: string;
 | 
				
			||||||
 | 
					    repo?: string;
 | 
				
			||||||
 | 
					    value?: number;
 | 
				
			||||||
 | 
					    delay?: number;
 | 
				
			||||||
 | 
					  } & UseIsInViewOptions
 | 
				
			||||||
 | 
					  & HTMLMotionProps<"div">
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStars({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  username,
 | 
				
			||||||
 | 
					  repo,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GithubStarsProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(ref as React.Ref<HTMLDivElement>, {
 | 
				
			||||||
 | 
					    inView,
 | 
				
			||||||
 | 
					    inViewOnce,
 | 
				
			||||||
 | 
					    inViewMargin,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [stars, setStars] = React.useState(value ?? 0);
 | 
				
			||||||
 | 
					  const [currentStars, setCurrentStars] = React.useState(0);
 | 
				
			||||||
 | 
					  const [isLoading, setIsLoading] = React.useState(true);
 | 
				
			||||||
 | 
					  const isCompleted = React.useMemo(() => currentStars === stars, [currentStars, stars]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (value !== undefined && username && repo)
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    if (!isInView) {
 | 
				
			||||||
 | 
					      setStars(0);
 | 
				
			||||||
 | 
					      setIsLoading(true);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const timeout = setTimeout(() => {
 | 
				
			||||||
 | 
					      fetch(`https://api.github.com/repos/${username}/${repo}`)
 | 
				
			||||||
 | 
					        .then(response => response.json())
 | 
				
			||||||
 | 
					        .then((data) => {
 | 
				
			||||||
 | 
					          if (data && typeof data.stargazers_count === "number") {
 | 
				
			||||||
 | 
					            setStars(data.stargazers_count);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(console.error)
 | 
				
			||||||
 | 
					        .finally(() => setIsLoading(false));
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeout);
 | 
				
			||||||
 | 
					  }, [username, repo, value, isInView, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <GithubStarsProvider
 | 
				
			||||||
 | 
					      value={{
 | 
				
			||||||
 | 
					        stars,
 | 
				
			||||||
 | 
					        currentStars,
 | 
				
			||||||
 | 
					        isCompleted,
 | 
				
			||||||
 | 
					        isLoading,
 | 
				
			||||||
 | 
					        setStars,
 | 
				
			||||||
 | 
					        setCurrentStars,
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {!isLoading && (
 | 
				
			||||||
 | 
					        <Component ref={localRef} {...props}>
 | 
				
			||||||
 | 
					          {children}
 | 
				
			||||||
 | 
					        </Component>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </GithubStarsProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsNumberProps = Omit<SlidingNumberProps, "number" | "fromNumber">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsNumber({ padStart = true, ...props }: GithubStarsNumberProps) {
 | 
				
			||||||
 | 
					  const { stars, setCurrentStars } = useGithubStars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SlidingNumber number={stars} fromNumber={0} onNumberChange={setCurrentStars} padStart={padStart} {...props} />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsIconProps<T extends React.ElementType> = {
 | 
				
			||||||
 | 
					  icon: React.ReactElement<T>;
 | 
				
			||||||
 | 
					  color?: string;
 | 
				
			||||||
 | 
					  activeClassName?: string;
 | 
				
			||||||
 | 
					} & React.ComponentProps<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsIcon<T extends React.ElementType>({
 | 
				
			||||||
 | 
					  icon: Icon,
 | 
				
			||||||
 | 
					  color = "currentColor",
 | 
				
			||||||
 | 
					  activeClassName,
 | 
				
			||||||
 | 
					  className,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: GithubStarsIconProps<T>) {
 | 
				
			||||||
 | 
					  const { stars, currentStars, isCompleted } = useGithubStars();
 | 
				
			||||||
 | 
					  const fillPercentage = (currentStars / stars) * 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div style={{ position: "relative" }}>
 | 
				
			||||||
 | 
					      <Icon aria-hidden="true" className={cn(className)} {...props} />
 | 
				
			||||||
 | 
					      <Icon
 | 
				
			||||||
 | 
					        aria-hidden="true"
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          position: "absolute",
 | 
				
			||||||
 | 
					          top: 0,
 | 
				
			||||||
 | 
					          left: 0,
 | 
				
			||||||
 | 
					          fill: color,
 | 
				
			||||||
 | 
					          stroke: color,
 | 
				
			||||||
 | 
					          clipPath: `inset(${100 - (isCompleted ? fillPercentage : fillPercentage - 10)}% 0 0 0)`,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        className={cn(className, activeClassName)}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsParticlesProps = ParticlesEffectProps & {
 | 
				
			||||||
 | 
					  children: React.ReactElement;
 | 
				
			||||||
 | 
					  size?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsParticles({ children, size = 4, style, ...props }: GithubStarsParticlesProps) {
 | 
				
			||||||
 | 
					  const { isCompleted } = useGithubStars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Particles animate={isCompleted}>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					      <ParticlesEffect
 | 
				
			||||||
 | 
					        style={{
 | 
				
			||||||
 | 
					          backgroundColor: "currentcolor",
 | 
				
			||||||
 | 
					          borderRadius: "50%",
 | 
				
			||||||
 | 
					          width: size,
 | 
				
			||||||
 | 
					          height: size,
 | 
				
			||||||
 | 
					          ...style,
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </Particles>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GithubStarsLogoProps = React.SVGProps<SVGSVGElement>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function GithubStarsLogo(props: GithubStarsLogoProps) {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <svg role="img" viewBox="0 0 24 24" fill="currentColor" aria-label="GitHub" {...props}>
 | 
				
			||||||
 | 
					      <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path>
 | 
				
			||||||
 | 
					    </svg>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  GithubStars,
 | 
				
			||||||
 | 
					  type GithubStarsContextType,
 | 
				
			||||||
 | 
					  GithubStarsIcon,
 | 
				
			||||||
 | 
					  type GithubStarsIconProps,
 | 
				
			||||||
 | 
					  GithubStarsLogo,
 | 
				
			||||||
 | 
					  type GithubStarsLogoProps,
 | 
				
			||||||
 | 
					  GithubStarsNumber,
 | 
				
			||||||
 | 
					  type GithubStarsNumberProps,
 | 
				
			||||||
 | 
					  GithubStarsParticles,
 | 
				
			||||||
 | 
					  type GithubStarsParticlesProps,
 | 
				
			||||||
 | 
					  type GithubStarsProps,
 | 
				
			||||||
 | 
					  useGithubStars,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										101
									
								
								frontend/src/components/animate-ui/primitives/animate/slot.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								frontend/src/components/animate-ui/primitives/animate/slot.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { isMotionComponent, motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { cn } from "@/lib/utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AnyProps = Record<string, unknown>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DOMMotionProps<T extends HTMLElement = HTMLElement> = Omit<
 | 
				
			||||||
 | 
					  HTMLMotionProps<keyof HTMLElementTagNameMap>,
 | 
				
			||||||
 | 
					  "ref"
 | 
				
			||||||
 | 
					> & { ref?: React.Ref<T> };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WithAsChild<Base extends object>
 | 
				
			||||||
 | 
					  = | (Base & { asChild: true; children: React.ReactElement })
 | 
				
			||||||
 | 
					    | (Base & { asChild?: false | undefined });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlotProps<T extends HTMLElement = HTMLElement> = {
 | 
				
			||||||
 | 
					  children?: any;
 | 
				
			||||||
 | 
					} & DOMMotionProps<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeRefs<T>(
 | 
				
			||||||
 | 
					  ...refs: (React.Ref<T> | undefined)[]
 | 
				
			||||||
 | 
					): React.RefCallback<T> {
 | 
				
			||||||
 | 
					  return (node) => {
 | 
				
			||||||
 | 
					    refs.forEach((ref) => {
 | 
				
			||||||
 | 
					      if (!ref)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      if (typeof ref === "function") {
 | 
				
			||||||
 | 
					        ref(node);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else {
 | 
				
			||||||
 | 
					        (ref as React.RefObject<T | null>).current = node;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function mergeProps<T extends HTMLElement>(
 | 
				
			||||||
 | 
					  childProps: AnyProps,
 | 
				
			||||||
 | 
					  slotProps: DOMMotionProps<T>,
 | 
				
			||||||
 | 
					): AnyProps {
 | 
				
			||||||
 | 
					  const merged: AnyProps = { ...childProps, ...slotProps };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (childProps.className || slotProps.className) {
 | 
				
			||||||
 | 
					    merged.className = cn(
 | 
				
			||||||
 | 
					      childProps.className as string,
 | 
				
			||||||
 | 
					      slotProps.className as string,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (childProps.style || slotProps.style) {
 | 
				
			||||||
 | 
					    merged.style = {
 | 
				
			||||||
 | 
					      ...(childProps.style as React.CSSProperties),
 | 
				
			||||||
 | 
					      ...(slotProps.style as React.CSSProperties),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return merged;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Slot<T extends HTMLElement = HTMLElement>({
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: SlotProps<T>) {
 | 
				
			||||||
 | 
					  const isAlreadyMotion
 | 
				
			||||||
 | 
					    = typeof children.type === "object"
 | 
				
			||||||
 | 
					      && children.type !== null
 | 
				
			||||||
 | 
					      && isMotionComponent(children.type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Base = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      isAlreadyMotion
 | 
				
			||||||
 | 
					        ? (children.type as React.ElementType)
 | 
				
			||||||
 | 
					        : motion.create(children.type as React.ElementType),
 | 
				
			||||||
 | 
					    [isAlreadyMotion, children.type],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!React.isValidElement(children))
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { ref: childRef, ...childProps } = children.props as AnyProps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const mergedProps = mergeProps(childProps, props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Base {...mergedProps} ref={mergeRefs(childRef as React.Ref<T>, ref)} />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  type AnyProps,
 | 
				
			||||||
 | 
					  type DOMMotionProps,
 | 
				
			||||||
 | 
					  Slot,
 | 
				
			||||||
 | 
					  type SlotProps,
 | 
				
			||||||
 | 
					  type WithAsChild,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ButtonProps = WithAsChild<
 | 
				
			||||||
 | 
					  HTMLMotionProps<"button"> & {
 | 
				
			||||||
 | 
					    hoverScale?: number;
 | 
				
			||||||
 | 
					    tapScale?: number;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Button({
 | 
				
			||||||
 | 
					  hoverScale = 1.05,
 | 
				
			||||||
 | 
					  tapScale = 0.95,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ButtonProps) {
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.button;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Component
 | 
				
			||||||
 | 
					      whileTap={{ scale: tapScale }}
 | 
				
			||||||
 | 
					      whileHover={{ scale: hoverScale }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Button, type ButtonProps };
 | 
				
			||||||
@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { HTMLMotionProps } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { AnimatePresence, motion } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Slot } from "@/components/animate-ui/primitives/animate/slot";
 | 
				
			||||||
 | 
					import { getStrictContext } from "@/lib/get-strict-context";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  useIsInView,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Side = "top" | "bottom" | "left" | "right";
 | 
				
			||||||
 | 
					type Align = "start" | "center" | "end";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesContextType = {
 | 
				
			||||||
 | 
					  animate: boolean;
 | 
				
			||||||
 | 
					  isInView: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const [ParticlesProvider, useParticles]
 | 
				
			||||||
 | 
					  = getStrictContext<ParticlesContextType>("ParticlesContext");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesProps = WithAsChild<
 | 
				
			||||||
 | 
					  Omit<HTMLMotionProps<"div">, "children"> & {
 | 
				
			||||||
 | 
					    animate?: boolean;
 | 
				
			||||||
 | 
					    children: React.ReactNode;
 | 
				
			||||||
 | 
					  } & UseIsInViewOptions
 | 
				
			||||||
 | 
					>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function Particles({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  animate = true,
 | 
				
			||||||
 | 
					  asChild = false,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  style,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ParticlesProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(
 | 
				
			||||||
 | 
					    ref as React.Ref<HTMLDivElement>,
 | 
				
			||||||
 | 
					    { inView, inViewOnce, inViewMargin },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Component = asChild ? Slot : motion.div;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <ParticlesProvider value={{ animate, isInView }}>
 | 
				
			||||||
 | 
					      <Component
 | 
				
			||||||
 | 
					        ref={localRef}
 | 
				
			||||||
 | 
					        style={{ position: "relative", ...style }}
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {children}
 | 
				
			||||||
 | 
					      </Component>
 | 
				
			||||||
 | 
					    </ParticlesProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ParticlesEffectProps = Omit<HTMLMotionProps<"div">, "children"> & {
 | 
				
			||||||
 | 
					  side?: Side;
 | 
				
			||||||
 | 
					  align?: Align;
 | 
				
			||||||
 | 
					  count?: number;
 | 
				
			||||||
 | 
					  radius?: number;
 | 
				
			||||||
 | 
					  spread?: number;
 | 
				
			||||||
 | 
					  duration?: number;
 | 
				
			||||||
 | 
					  holdDelay?: number;
 | 
				
			||||||
 | 
					  sideOffset?: number;
 | 
				
			||||||
 | 
					  alignOffset?: number;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ParticlesEffect({
 | 
				
			||||||
 | 
					  side = "top",
 | 
				
			||||||
 | 
					  align = "center",
 | 
				
			||||||
 | 
					  count = 6,
 | 
				
			||||||
 | 
					  radius = 30,
 | 
				
			||||||
 | 
					  spread = 360,
 | 
				
			||||||
 | 
					  duration = 0.8,
 | 
				
			||||||
 | 
					  holdDelay = 0.05,
 | 
				
			||||||
 | 
					  sideOffset = 0,
 | 
				
			||||||
 | 
					  alignOffset = 0,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					  style,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: ParticlesEffectProps) {
 | 
				
			||||||
 | 
					  const { animate, isInView } = useParticles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const isVertical = side === "top" || side === "bottom";
 | 
				
			||||||
 | 
					  const alignPct = align === "start" ? "0%" : align === "end" ? "100%" : "50%";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const top = isVertical
 | 
				
			||||||
 | 
					    ? side === "top"
 | 
				
			||||||
 | 
					      ? `calc(0% - ${sideOffset}px)`
 | 
				
			||||||
 | 
					      : `calc(100% + ${sideOffset}px)`
 | 
				
			||||||
 | 
					    : `calc(${alignPct} + ${alignOffset}px)`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const left = isVertical
 | 
				
			||||||
 | 
					    ? `calc(${alignPct} + ${alignOffset}px)`
 | 
				
			||||||
 | 
					    : side === "left"
 | 
				
			||||||
 | 
					      ? `calc(0% - ${sideOffset}px)`
 | 
				
			||||||
 | 
					      : `calc(100% + ${sideOffset}px)`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const containerStyle: React.CSSProperties = {
 | 
				
			||||||
 | 
					    position: "absolute",
 | 
				
			||||||
 | 
					    top,
 | 
				
			||||||
 | 
					    left,
 | 
				
			||||||
 | 
					    transform: "translate(-50%, -50%)",
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const angleStep = (spread * (Math.PI / 180)) / Math.max(1, count - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AnimatePresence>
 | 
				
			||||||
 | 
					      {animate
 | 
				
			||||||
 | 
					        && isInView
 | 
				
			||||||
 | 
					        && Array.from({ length: count }).map((_, i) => {
 | 
				
			||||||
 | 
					          const angle = i * angleStep;
 | 
				
			||||||
 | 
					          const x = Math.cos(angle) * radius;
 | 
				
			||||||
 | 
					          const y = Math.sin(angle) * radius;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <motion.div
 | 
				
			||||||
 | 
					              key={i}
 | 
				
			||||||
 | 
					              style={{ ...containerStyle, ...style }}
 | 
				
			||||||
 | 
					              initial={{ scale: 0, opacity: 0 }}
 | 
				
			||||||
 | 
					              animate={{
 | 
				
			||||||
 | 
					                x: `${x}px`,
 | 
				
			||||||
 | 
					                y: `${y}px`,
 | 
				
			||||||
 | 
					                scale: [0, 1, 0],
 | 
				
			||||||
 | 
					                opacity: [0, 1, 0],
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              transition={{
 | 
				
			||||||
 | 
					                duration,
 | 
				
			||||||
 | 
					                delay: delay + i * holdDelay,
 | 
				
			||||||
 | 
					                ease: "easeOut",
 | 
				
			||||||
 | 
					                ...transition,
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					              {...props}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
 | 
					    </AnimatePresence>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  Particles,
 | 
				
			||||||
 | 
					  ParticlesEffect,
 | 
				
			||||||
 | 
					  type ParticlesEffectProps,
 | 
				
			||||||
 | 
					  type ParticlesProps,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,338 @@
 | 
				
			|||||||
 | 
					"use client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { MotionValue, SpringOptions } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  motion,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useMotionValue,
 | 
				
			||||||
 | 
					  useSpring,
 | 
				
			||||||
 | 
					  useTransform,
 | 
				
			||||||
 | 
					} from "motion/react";
 | 
				
			||||||
 | 
					import useMeasure from "react-use-measure";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type { UseIsInViewOptions } from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  useIsInView,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} from "@/hooks/use-is-in-view";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberRollerProps = {
 | 
				
			||||||
 | 
					  prevValue: number;
 | 
				
			||||||
 | 
					  value: number;
 | 
				
			||||||
 | 
					  place: number;
 | 
				
			||||||
 | 
					  transition: SpringOptions;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumberRoller({
 | 
				
			||||||
 | 
					  prevValue,
 | 
				
			||||||
 | 
					  value,
 | 
				
			||||||
 | 
					  place,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					}: SlidingNumberRollerProps) {
 | 
				
			||||||
 | 
					  const startNumber = Math.floor(prevValue / place) % 10;
 | 
				
			||||||
 | 
					  const targetNumber = Math.floor(value / place) % 10;
 | 
				
			||||||
 | 
					  const animatedValue = useSpring(startNumber, transition);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    const timeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					      animatedValue.set(targetNumber);
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeoutId);
 | 
				
			||||||
 | 
					  }, [targetNumber, animatedValue, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [measureRef, { height }] = useMeasure();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      ref={measureRef}
 | 
				
			||||||
 | 
					      data-slot="sliding-number-roller"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        position: "relative",
 | 
				
			||||||
 | 
					        display: "inline-block",
 | 
				
			||||||
 | 
					        width: "1ch",
 | 
				
			||||||
 | 
					        overflowX: "visible",
 | 
				
			||||||
 | 
					        overflowY: "clip",
 | 
				
			||||||
 | 
					        lineHeight: 1,
 | 
				
			||||||
 | 
					        fontVariantNumeric: "tabular-nums",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <span style={{ visibility: "hidden" }}>0</span>
 | 
				
			||||||
 | 
					      {Array.from({ length: 10 }, (_, i) => (
 | 
				
			||||||
 | 
					        <SlidingNumberDisplay
 | 
				
			||||||
 | 
					          key={i}
 | 
				
			||||||
 | 
					          motionValue={animatedValue}
 | 
				
			||||||
 | 
					          number={i}
 | 
				
			||||||
 | 
					          height={height}
 | 
				
			||||||
 | 
					          transition={transition}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ))}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberDisplayProps = {
 | 
				
			||||||
 | 
					  motionValue: MotionValue<number>;
 | 
				
			||||||
 | 
					  number: number;
 | 
				
			||||||
 | 
					  height: number;
 | 
				
			||||||
 | 
					  transition: SpringOptions;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumberDisplay({
 | 
				
			||||||
 | 
					  motionValue,
 | 
				
			||||||
 | 
					  number,
 | 
				
			||||||
 | 
					  height,
 | 
				
			||||||
 | 
					  transition,
 | 
				
			||||||
 | 
					}: SlidingNumberDisplayProps) {
 | 
				
			||||||
 | 
					  const y = useTransform(motionValue, (latest) => {
 | 
				
			||||||
 | 
					    if (!height)
 | 
				
			||||||
 | 
					      return 0;
 | 
				
			||||||
 | 
					    const currentNumber = latest % 10;
 | 
				
			||||||
 | 
					    const offset = (10 + number - currentNumber) % 10;
 | 
				
			||||||
 | 
					    let translateY = offset * height;
 | 
				
			||||||
 | 
					    if (offset > 5)
 | 
				
			||||||
 | 
					      translateY -= 10 * height;
 | 
				
			||||||
 | 
					    return translateY;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!height) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <span style={{ visibility: "hidden", position: "absolute" }}>
 | 
				
			||||||
 | 
					        {number}
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <motion.span
 | 
				
			||||||
 | 
					      data-slot="sliding-number-display"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        y,
 | 
				
			||||||
 | 
					        position: "absolute",
 | 
				
			||||||
 | 
					        inset: 0,
 | 
				
			||||||
 | 
					        display: "flex",
 | 
				
			||||||
 | 
					        alignItems: "center",
 | 
				
			||||||
 | 
					        justifyContent: "center",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      transition={{ ...transition, type: "spring" }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {number}
 | 
				
			||||||
 | 
					    </motion.span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SlidingNumberProps = Omit<React.ComponentProps<"span">, "children"> & {
 | 
				
			||||||
 | 
					  number: number;
 | 
				
			||||||
 | 
					  fromNumber?: number;
 | 
				
			||||||
 | 
					  onNumberChange?: (number: number) => void;
 | 
				
			||||||
 | 
					  padStart?: boolean;
 | 
				
			||||||
 | 
					  decimalSeparator?: string;
 | 
				
			||||||
 | 
					  decimalPlaces?: number;
 | 
				
			||||||
 | 
					  thousandSeparator?: string;
 | 
				
			||||||
 | 
					  transition?: SpringOptions;
 | 
				
			||||||
 | 
					  delay?: number;
 | 
				
			||||||
 | 
					} & UseIsInViewOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function SlidingNumber({
 | 
				
			||||||
 | 
					  ref,
 | 
				
			||||||
 | 
					  number,
 | 
				
			||||||
 | 
					  fromNumber,
 | 
				
			||||||
 | 
					  onNumberChange,
 | 
				
			||||||
 | 
					  inView = false,
 | 
				
			||||||
 | 
					  inViewMargin = "0px",
 | 
				
			||||||
 | 
					  inViewOnce = true,
 | 
				
			||||||
 | 
					  padStart = false,
 | 
				
			||||||
 | 
					  decimalSeparator = ".",
 | 
				
			||||||
 | 
					  decimalPlaces = 0,
 | 
				
			||||||
 | 
					  thousandSeparator,
 | 
				
			||||||
 | 
					  transition = { stiffness: 200, damping: 20, mass: 0.4 },
 | 
				
			||||||
 | 
					  delay = 0,
 | 
				
			||||||
 | 
					  ...props
 | 
				
			||||||
 | 
					}: SlidingNumberProps) {
 | 
				
			||||||
 | 
					  const { ref: localRef, isInView } = useIsInView(
 | 
				
			||||||
 | 
					    ref as React.Ref<HTMLElement>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      inView,
 | 
				
			||||||
 | 
					      inViewOnce,
 | 
				
			||||||
 | 
					      inViewMargin,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const prevNumberRef = React.useRef<number>(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const hasAnimated = fromNumber !== undefined;
 | 
				
			||||||
 | 
					  const motionVal = useMotionValue(fromNumber ?? 0);
 | 
				
			||||||
 | 
					  const springVal = useSpring(motionVal, { stiffness: 90, damping: 50 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (!hasAnimated)
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    const timeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					      if (isInView)
 | 
				
			||||||
 | 
					        motionVal.set(number);
 | 
				
			||||||
 | 
					    }, delay);
 | 
				
			||||||
 | 
					    return () => clearTimeout(timeoutId);
 | 
				
			||||||
 | 
					  }, [hasAnimated, isInView, number, motionVal, delay]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [effectiveNumber, setEffectiveNumber] = React.useState(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (hasAnimated) {
 | 
				
			||||||
 | 
					      const inferredDecimals
 | 
				
			||||||
 | 
					        = typeof decimalPlaces === "number" && decimalPlaces >= 0
 | 
				
			||||||
 | 
					          ? decimalPlaces
 | 
				
			||||||
 | 
					          : (() => {
 | 
				
			||||||
 | 
					              const s = String(number);
 | 
				
			||||||
 | 
					              const idx = s.indexOf(".");
 | 
				
			||||||
 | 
					              return idx >= 0 ? s.length - idx - 1 : 0;
 | 
				
			||||||
 | 
					            })();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const factor = 10 ** inferredDecimals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const unsubscribe = springVal.on("change", (latest: number) => {
 | 
				
			||||||
 | 
					        const newValue
 | 
				
			||||||
 | 
					          = inferredDecimals > 0
 | 
				
			||||||
 | 
					            ? Math.round(latest * factor) / factor
 | 
				
			||||||
 | 
					            : Math.round(latest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (effectiveNumber !== newValue) {
 | 
				
			||||||
 | 
					          setEffectiveNumber(newValue);
 | 
				
			||||||
 | 
					          onNumberChange?.(newValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return () => unsubscribe();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					      setEffectiveNumber(!isInView ? 0 : Math.abs(Number(number)));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [
 | 
				
			||||||
 | 
					    hasAnimated,
 | 
				
			||||||
 | 
					    springVal,
 | 
				
			||||||
 | 
					    isInView,
 | 
				
			||||||
 | 
					    number,
 | 
				
			||||||
 | 
					    decimalPlaces,
 | 
				
			||||||
 | 
					    onNumberChange,
 | 
				
			||||||
 | 
					    effectiveNumber,
 | 
				
			||||||
 | 
					  ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const formatNumber = React.useCallback(
 | 
				
			||||||
 | 
					    (num: number) =>
 | 
				
			||||||
 | 
					      decimalPlaces != null ? num.toFixed(decimalPlaces) : num.toString(),
 | 
				
			||||||
 | 
					    [decimalPlaces],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const numberStr = formatNumber(effectiveNumber);
 | 
				
			||||||
 | 
					  const [newIntStrRaw, newDecStrRaw = ""] = numberStr.split(".");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const finalIntLength = padStart
 | 
				
			||||||
 | 
					    ? Math.max(
 | 
				
			||||||
 | 
					        Math.floor(Math.abs(number)).toString().length,
 | 
				
			||||||
 | 
					        newIntStrRaw.length,
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    : newIntStrRaw.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const newIntStr = padStart
 | 
				
			||||||
 | 
					    ? newIntStrRaw.padStart(finalIntLength, "0")
 | 
				
			||||||
 | 
					    : newIntStrRaw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const prevFormatted = formatNumber(prevNumberRef.current);
 | 
				
			||||||
 | 
					  const [prevIntStrRaw = "", prevDecStrRaw = ""] = prevFormatted.split(".");
 | 
				
			||||||
 | 
					  const prevIntStr = padStart
 | 
				
			||||||
 | 
					    ? prevIntStrRaw.padStart(finalIntLength, "0")
 | 
				
			||||||
 | 
					    : prevIntStrRaw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const adjustedPrevInt = React.useMemo(() => {
 | 
				
			||||||
 | 
					    return prevIntStr.length > finalIntLength
 | 
				
			||||||
 | 
					      ? prevIntStr.slice(-finalIntLength)
 | 
				
			||||||
 | 
					      : prevIntStr.padStart(finalIntLength, "0");
 | 
				
			||||||
 | 
					  }, [prevIntStr, finalIntLength]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const adjustedPrevDec = React.useMemo(() => {
 | 
				
			||||||
 | 
					    if (!newDecStrRaw)
 | 
				
			||||||
 | 
					      return "";
 | 
				
			||||||
 | 
					    return prevDecStrRaw.length > newDecStrRaw.length
 | 
				
			||||||
 | 
					      ? prevDecStrRaw.slice(0, newDecStrRaw.length)
 | 
				
			||||||
 | 
					      : prevDecStrRaw.padEnd(newDecStrRaw.length, "0");
 | 
				
			||||||
 | 
					  }, [prevDecStrRaw, newDecStrRaw]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  React.useEffect(() => {
 | 
				
			||||||
 | 
					    if (isInView)
 | 
				
			||||||
 | 
					      prevNumberRef.current = effectiveNumber;
 | 
				
			||||||
 | 
					  }, [effectiveNumber, isInView]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const intPlaces = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      Array.from({ length: finalIntLength }, (_, i) =>
 | 
				
			||||||
 | 
					        10 ** (finalIntLength - i - 1)),
 | 
				
			||||||
 | 
					    [finalIntLength],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  const decPlaces = React.useMemo(
 | 
				
			||||||
 | 
					    () =>
 | 
				
			||||||
 | 
					      newDecStrRaw
 | 
				
			||||||
 | 
					        ? Array.from({ length: newDecStrRaw.length }, (_, i) =>
 | 
				
			||||||
 | 
					            10 ** (newDecStrRaw.length - i - 1))
 | 
				
			||||||
 | 
					        : [],
 | 
				
			||||||
 | 
					    [newDecStrRaw],
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const newDecValue = newDecStrRaw ? Number.parseInt(newDecStrRaw, 10) : 0;
 | 
				
			||||||
 | 
					  const prevDecValue = adjustedPrevDec ? Number.parseInt(adjustedPrevDec, 10) : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <span
 | 
				
			||||||
 | 
					      ref={localRef}
 | 
				
			||||||
 | 
					      data-slot="sliding-number"
 | 
				
			||||||
 | 
					      style={{
 | 
				
			||||||
 | 
					        display: "inline-flex",
 | 
				
			||||||
 | 
					        alignItems: "center",
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      {...props}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {isInView && Number(number) < 0 && (
 | 
				
			||||||
 | 
					        <span style={{ marginRight: "0.25rem" }}>-</span>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {intPlaces.map((place, idx) => {
 | 
				
			||||||
 | 
					        const digitsToRight = intPlaces.length - idx - 1;
 | 
				
			||||||
 | 
					        const isSeparatorPosition
 | 
				
			||||||
 | 
					          = typeof thousandSeparator !== "undefined"
 | 
				
			||||||
 | 
					            && digitsToRight > 0
 | 
				
			||||||
 | 
					            && digitsToRight % 3 === 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <React.Fragment key={`int-${place}`}>
 | 
				
			||||||
 | 
					            <SlidingNumberRoller
 | 
				
			||||||
 | 
					              prevValue={Number.parseInt(adjustedPrevInt, 10)}
 | 
				
			||||||
 | 
					              value={Number.parseInt(newIntStr ?? "0", 10)}
 | 
				
			||||||
 | 
					              place={place}
 | 
				
			||||||
 | 
					              transition={transition}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            {isSeparatorPosition && <span>{thousandSeparator}</span>}
 | 
				
			||||||
 | 
					          </React.Fragment>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      })}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {newDecStrRaw && (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					          <span>{decimalSeparator}</span>
 | 
				
			||||||
 | 
					          {decPlaces.map(place => (
 | 
				
			||||||
 | 
					            <SlidingNumberRoller
 | 
				
			||||||
 | 
					              key={`dec-${place}`}
 | 
				
			||||||
 | 
					              prevValue={prevDecValue}
 | 
				
			||||||
 | 
					              value={newDecValue}
 | 
				
			||||||
 | 
					              place={place}
 | 
				
			||||||
 | 
					              transition={transition}
 | 
				
			||||||
 | 
					              delay={delay}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          ))}
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { SlidingNumber, type SlidingNumberProps };
 | 
				
			||||||
@@ -4,10 +4,10 @@ import Image from "next/image";
 | 
				
			|||||||
import Link from "next/link";
 | 
					import Link from "next/link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { navbarLinks } from "@/config/site-config";
 | 
					import { navbarLinks } from "@/config/site-config";
 | 
				
			||||||
import { Button } from "@/components/ui/button";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
 | 
					import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
 | 
				
			||||||
import StarOnGithubButton from "./ui/star-on-github-button";
 | 
					import { GitHubStarsButton } from "./animate-ui/components/buttons/github-stars";
 | 
				
			||||||
 | 
					import { Button } from "./animate-ui/components/buttons/button";
 | 
				
			||||||
import { ThemeToggle } from "./ui/theme-toggle";
 | 
					import { ThemeToggle } from "./ui/theme-toggle";
 | 
				
			||||||
import CommandMenu from "./command-menu";
 | 
					import CommandMenu from "./command-menu";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,31 +39,18 @@ function Navbar() {
 | 
				
			|||||||
            href="/"
 | 
					            href="/"
 | 
				
			||||||
            className="flex cursor-pointer w-full justify-center sm:justify-start flex-row-reverse items-center gap-2 font-semibold sm:flex-row"
 | 
					            className="flex cursor-pointer w-full justify-center sm:justify-start flex-row-reverse items-center gap-2 font-semibold sm:flex-row"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            <Image
 | 
					            <Image height={18} unoptimized width={18} alt="logo" src="/ProxmoxVE/logo.png" className="" />
 | 
				
			||||||
              height={18}
 | 
					 | 
				
			||||||
              unoptimized
 | 
					 | 
				
			||||||
              width={18}
 | 
					 | 
				
			||||||
              alt="logo"
 | 
					 | 
				
			||||||
              src="/ProxmoxVE/logo.png"
 | 
					 | 
				
			||||||
              className=""
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            <span className="hidden md:block">Proxmox VE Helper-Scripts</span>
 | 
					            <span className="hidden md:block">Proxmox VE Helper-Scripts</span>
 | 
				
			||||||
          </Link>
 | 
					          </Link>
 | 
				
			||||||
          <div className="flex gap-2">
 | 
					          <div className="flex gap-2">
 | 
				
			||||||
            <CommandMenu />
 | 
					            <CommandMenu />
 | 
				
			||||||
            <StarOnGithubButton />
 | 
					            <GitHubStarsButton username="community-scripts" repo="ProxmoxVE" />
 | 
				
			||||||
            {navbarLinks.map(({ href, event, icon, text, mobileHidden }) => (
 | 
					            {navbarLinks.map(({ href, event, icon, text, mobileHidden }) => (
 | 
				
			||||||
              <TooltipProvider key={event}>
 | 
					              <TooltipProvider key={event}>
 | 
				
			||||||
                <Tooltip delayDuration={100}>
 | 
					                <Tooltip delayDuration={100}>
 | 
				
			||||||
                  <TooltipTrigger
 | 
					                  <TooltipTrigger className={mobileHidden ? "hidden lg:block" : ""}>
 | 
				
			||||||
                    className={mobileHidden ? "hidden lg:block" : ""}
 | 
					 | 
				
			||||||
                  >
 | 
					 | 
				
			||||||
                    <Button variant="ghost" size="icon" asChild>
 | 
					                    <Button variant="ghost" size="icon" asChild>
 | 
				
			||||||
                      <Link
 | 
					                      <Link target="_blank" href={href} data-umami-event={event}>
 | 
				
			||||||
                        target="_blank"
 | 
					 | 
				
			||||||
                        href={href}
 | 
					 | 
				
			||||||
                        data-umami-event={event}
 | 
					 | 
				
			||||||
                      >
 | 
					 | 
				
			||||||
                        {icon}
 | 
					                        {icon}
 | 
				
			||||||
                        <span className="sr-only">{text}</span>
 | 
					                        <span className="sr-only">{text}</span>
 | 
				
			||||||
                      </Link>
 | 
					                      </Link>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								frontend/src/hooks/use-is-in-view.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								frontend/src/hooks/use-is-in-view.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import type { UseInViewOptions } from "motion/react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useInView } from "motion/react";
 | 
				
			||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UseIsInViewOptions = {
 | 
				
			||||||
 | 
					  inView?: boolean;
 | 
				
			||||||
 | 
					  inViewOnce?: boolean;
 | 
				
			||||||
 | 
					  inViewMargin?: UseInViewOptions["margin"];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function useIsInView<T extends HTMLElement = HTMLElement>(
 | 
				
			||||||
 | 
					  ref: React.Ref<T>,
 | 
				
			||||||
 | 
					  options: UseIsInViewOptions = {},
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const { inView, inViewOnce = false, inViewMargin = "0px" } = options;
 | 
				
			||||||
 | 
					  const localRef = React.useRef<T>(null);
 | 
				
			||||||
 | 
					  React.useImperativeHandle(ref, () => localRef.current as T);
 | 
				
			||||||
 | 
					  const inViewResult = useInView(localRef, {
 | 
				
			||||||
 | 
					    once: inViewOnce,
 | 
				
			||||||
 | 
					    margin: inViewMargin,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const isInView = !inView || inViewResult;
 | 
				
			||||||
 | 
					  return { ref: localRef, isInView };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { useIsInView, type UseIsInViewOptions };
 | 
				
			||||||
							
								
								
									
										36
									
								
								frontend/src/lib/get-strict-context.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								frontend/src/lib/get-strict-context.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import * as React from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getStrictContext<T>(
 | 
				
			||||||
 | 
					  name?: string,
 | 
				
			||||||
 | 
					): readonly [
 | 
				
			||||||
 | 
					    ({
 | 
				
			||||||
 | 
					      value,
 | 
				
			||||||
 | 
					      children,
 | 
				
			||||||
 | 
					    }: {
 | 
				
			||||||
 | 
					      value: T;
 | 
				
			||||||
 | 
					      children?: React.ReactNode;
 | 
				
			||||||
 | 
					    }) => React.JSX.Element,
 | 
				
			||||||
 | 
					    () => T,
 | 
				
			||||||
 | 
					  ] {
 | 
				
			||||||
 | 
					  const Context = React.createContext<T | undefined>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const Provider = ({
 | 
				
			||||||
 | 
					    value,
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					  }: {
 | 
				
			||||||
 | 
					    value: T;
 | 
				
			||||||
 | 
					    children?: React.ReactNode;
 | 
				
			||||||
 | 
					  }) => <Context.Provider value={value}>{children}</Context.Provider>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const useSafeContext = () => {
 | 
				
			||||||
 | 
					    const ctx = React.useContext(Context);
 | 
				
			||||||
 | 
					    if (ctx === undefined) {
 | 
				
			||||||
 | 
					      throw new Error(`useContext must be used within ${name ?? "a Provider"}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ctx;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return [Provider, useSafeContext] as const;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { getStrictContext };
 | 
				
			||||||
@@ -17,7 +17,9 @@ msg_info "Installing Dependencies"
 | 
				
			|||||||
$STD apt-get install -y \
 | 
					$STD apt-get install -y \
 | 
				
			||||||
  gdal-bin \
 | 
					  gdal-bin \
 | 
				
			||||||
  libgdal-dev \
 | 
					  libgdal-dev \
 | 
				
			||||||
  git
 | 
					  git \
 | 
				
			||||||
 | 
					  memcached \
 | 
				
			||||||
 | 
					  libmemcached-tools
 | 
				
			||||||
msg_ok "Installed Dependencies"
 | 
					msg_ok "Installed Dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PYTHON_VERSION="3.12" setup_uv
 | 
					PYTHON_VERSION="3.12" setup_uv
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -285,7 +285,7 @@ GEO_DIR="${INSTALL_DIR}/geodata"
 | 
				
			|||||||
mkdir -p "$INSTALL_DIR"
 | 
					mkdir -p "$INSTALL_DIR"
 | 
				
			||||||
mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
 | 
					mkdir -p {"${APP_DIR}","${UPLOAD_DIR}","${GEO_DIR}","${INSTALL_DIR}"/cache}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v1.140.1" "$SRC_DIR"
 | 
					fetch_and_deploy_gh_release "immich" "immich-app/immich" "tarball" "v1.141.1" "$SRC_DIR"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Installing ${APPLICATION} (more patience please)"
 | 
					msg_info "Installing ${APPLICATION} (more patience please)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -158,7 +158,7 @@ Wants=network.target karakeep-browser.service meilisearch.service
 | 
				
			|||||||
After=network.target karakeep-browser.service meilisearch.service
 | 
					After=network.target karakeep-browser.service meilisearch.service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
ExecStart=/usr/bin/node dist/index.mjs
 | 
					ExecStart=/usr/bin/node dist/index.js
 | 
				
			||||||
WorkingDirectory=/opt/karakeep/apps/workers
 | 
					WorkingDirectory=/opt/karakeep/apps/workers
 | 
				
			||||||
EnvironmentFile=/etc/karakeep/karakeep.env
 | 
					EnvironmentFile=/etc/karakeep/karakeep.env
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								install/leantime-install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								install/leantime-install.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: Stroopwafe1
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://leantime.io
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					verb_ip6
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					setting_up_container
 | 
				
			||||||
 | 
					network_check
 | 
				
			||||||
 | 
					update_os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" PHP_FPM="YES" setup_php
 | 
				
			||||||
 | 
					setup_mariadb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Setting up Database"
 | 
				
			||||||
 | 
					DB_NAME=leantime
 | 
				
			||||||
 | 
					DB_USER=leantime
 | 
				
			||||||
 | 
					DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
 | 
				
			||||||
 | 
					$STD mysql -u root -e "CREATE DATABASE $DB_NAME;"
 | 
				
			||||||
 | 
					$STD mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED WITH mysql_native_password AS PASSWORD('$DB_PASS');"
 | 
				
			||||||
 | 
					$STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;"
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  echo "Leantime Credentials"
 | 
				
			||||||
 | 
					  echo "Database User: $DB_USER"
 | 
				
			||||||
 | 
					  echo "Database Password: $DB_PASS"
 | 
				
			||||||
 | 
					  echo "Database Name: $DB_NAME"
 | 
				
			||||||
 | 
					} >>~/leantime.creds
 | 
				
			||||||
 | 
					msg_ok "Set up Database"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Setup Leantime"
 | 
				
			||||||
 | 
					chown -R www-data:www-data "/opt/leantime"
 | 
				
			||||||
 | 
					chmod -R 750 "/opt/leantime"
 | 
				
			||||||
 | 
					cat <<EOF >/etc/apache2/sites-enabled/000-default.conf
 | 
				
			||||||
 | 
					<VirtualHost *:80>
 | 
				
			||||||
 | 
					  ServerAdmin webmaster@localhost
 | 
				
			||||||
 | 
					  DocumentRoot /opt/leantime/public
 | 
				
			||||||
 | 
					  DirectoryIndex index.php index.html index.cgi index.pl index.xhtml
 | 
				
			||||||
 | 
					  Options +ExecCGI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <Directory /opt/leantime/>
 | 
				
			||||||
 | 
					    Options FollowSymLinks
 | 
				
			||||||
 | 
					    Require all granted
 | 
				
			||||||
 | 
					    AllowOverride All
 | 
				
			||||||
 | 
					  </Directory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <Location />
 | 
				
			||||||
 | 
					    Require all granted
 | 
				
			||||||
 | 
					  </Location>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ErrorLog /var/log/apache2/error.log
 | 
				
			||||||
 | 
					  CustomLog /var/log/apache2/access.log combined
 | 
				
			||||||
 | 
					</VirtualHost>
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env"
 | 
				
			||||||
 | 
					sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \
 | 
				
			||||||
 | 
					  -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \
 | 
				
			||||||
 | 
					  -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \
 | 
				
			||||||
 | 
					  -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \
 | 
				
			||||||
 | 
					  "/opt/leantime/config/.env"
 | 
				
			||||||
 | 
					$STD a2enmod -q proxy_fcgi setenvif rewrite
 | 
				
			||||||
 | 
					$STD a2enconf -q "php8.4-fpm"
 | 
				
			||||||
 | 
					sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini"
 | 
				
			||||||
 | 
					systemctl restart apache2
 | 
				
			||||||
 | 
					msg_ok "Setup leantime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					motd_ssh
 | 
				
			||||||
 | 
					customize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Cleaning up"
 | 
				
			||||||
 | 
					$STD apt-get -y autoremove
 | 
				
			||||||
 | 
					$STD apt-get -y autoclean
 | 
				
			||||||
 | 
					msg_ok "Cleaned"
 | 
				
			||||||
@@ -15,12 +15,10 @@ update_os
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
msg_info "Installing Mosquitto MQTT Broker"
 | 
					msg_info "Installing Mosquitto MQTT Broker"
 | 
				
			||||||
source /etc/os-release
 | 
					source /etc/os-release
 | 
				
			||||||
curl -fsSL http://repo.mosquitto.org/debian/mosquitto-repo.gpg >/usr/share/keyrings/mosquitto-repo.gpg.key
 | 
					 | 
				
			||||||
chmod go+r /usr/share/keyrings/mosquitto-repo.gpg.key
 | 
					 | 
				
			||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mosquitto-repo.gpg.key] http://repo.mosquitto.org/debian ${VERSION_CODENAME} main" >/etc/apt/sources.list.d/mosquitto.list
 | 
					 | 
				
			||||||
$STD apt-get update
 | 
					$STD apt-get update
 | 
				
			||||||
$STD apt-get -y install mosquitto
 | 
					$STD apt-get -y install mosquitto
 | 
				
			||||||
$STD apt-get -y install mosquitto-clients
 | 
					$STD apt-get -y install mosquitto-clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cat <<EOF >/etc/mosquitto/conf.d/default.conf
 | 
					cat <<EOF >/etc/mosquitto/conf.d/default.conf
 | 
				
			||||||
allow_anonymous false
 | 
					allow_anonymous false
 | 
				
			||||||
persistence true
 | 
					persistence true
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								install/resiliosync-install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								install/resiliosync-install.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2025 community-scripts ORG
 | 
				
			||||||
 | 
					# Author: David Bennett (dbinit)
 | 
				
			||||||
 | 
					# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
 | 
				
			||||||
 | 
					# Source: https://www.resilio.com/sync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
 | 
				
			||||||
 | 
					color
 | 
				
			||||||
 | 
					verb_ip6
 | 
				
			||||||
 | 
					catch_errors
 | 
				
			||||||
 | 
					setting_up_container
 | 
				
			||||||
 | 
					network_check
 | 
				
			||||||
 | 
					update_os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Setting up Resilio Sync Repository"
 | 
				
			||||||
 | 
					curl -fsSL "https://linux-packages.resilio.com/resilio-sync/key.asc" >/etc/apt/trusted.gpg.d/resilio-sync.asc
 | 
				
			||||||
 | 
					echo "deb [signed-by=/etc/apt/trusted.gpg.d/resilio-sync.asc] http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free" >/etc/apt/sources.list.d/resilio-sync.list
 | 
				
			||||||
 | 
					$STD apt-get update
 | 
				
			||||||
 | 
					msg_ok "Resilio Sync Repository Setup"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Installing Resilio Sync"
 | 
				
			||||||
 | 
					$STD apt-get install -y resilio-sync
 | 
				
			||||||
 | 
					sed -i "s/127.0.0.1:8888/0.0.0.0:8888/g" /etc/resilio-sync/config.json
 | 
				
			||||||
 | 
					systemctl enable -q resilio-sync
 | 
				
			||||||
 | 
					systemctl restart resilio-sync
 | 
				
			||||||
 | 
					msg_ok "Installed Resilio Sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					motd_ssh
 | 
				
			||||||
 | 
					customize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msg_info "Cleaning up"
 | 
				
			||||||
 | 
					$STD apt-get -y autoremove
 | 
				
			||||||
 | 
					$STD apt-get -y autoclean
 | 
				
			||||||
 | 
					msg_ok "Cleaned"
 | 
				
			||||||
@@ -18,18 +18,6 @@ fetch_and_deploy_gh_release "rustdesk-hbbs" "rustdesk/rustdesk-server" "binary"
 | 
				
			|||||||
fetch_and_deploy_gh_release "rustdesk-utils" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*amd64.deb"
 | 
					fetch_and_deploy_gh_release "rustdesk-utils" "rustdesk/rustdesk-server" "binary" "latest" "/opt/rustdesk" "rustdesk-server-utils*amd64.deb"
 | 
				
			||||||
fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*amd64.deb"
 | 
					fetch_and_deploy_gh_release "rustdesk-api" "lejianwen/rustdesk-api" "binary" "latest" "/opt/rustdesk" "rustdesk-api-server*amd64.deb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Configuring RustDesk Server"
 | 
					 | 
				
			||||||
ADMINPASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
 | 
					 | 
				
			||||||
cd /var/lib/rustdesk-api
 | 
					 | 
				
			||||||
$STD rustdesk-api reset-admin-pwd $ADMINPASS
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  echo "RustDesk WebUI"
 | 
					 | 
				
			||||||
  echo ""
 | 
					 | 
				
			||||||
  echo "Username: admin"
 | 
					 | 
				
			||||||
  echo "Password: $ADMINPASS"
 | 
					 | 
				
			||||||
} >>~/rustdesk.creds
 | 
					 | 
				
			||||||
msg_ok "Configured RustDesk Server"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
motd_ssh
 | 
					motd_ssh
 | 
				
			||||||
customize
 | 
					customize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ msg_info "Creating Python virtual environment"
 | 
				
			|||||||
sudo -H -u searxng bash -c '
 | 
					sudo -H -u searxng bash -c '
 | 
				
			||||||
  python3 -m venv /usr/local/searxng/searx-pyenv &&
 | 
					  python3 -m venv /usr/local/searxng/searx-pyenv &&
 | 
				
			||||||
  . /usr/local/searxng/searx-pyenv/bin/activate &&
 | 
					  . /usr/local/searxng/searx-pyenv/bin/activate &&
 | 
				
			||||||
  pip install -U pip setuptools wheel pyyaml &&
 | 
					  pip install -U pip setuptools wheel pyyaml lxml &&
 | 
				
			||||||
  pip install --use-pep517 --no-build-isolation -e /usr/local/searxng/searxng-src
 | 
					  pip install --use-pep517 --no-build-isolation -e /usr/local/searxng/searxng-src
 | 
				
			||||||
'
 | 
					'
 | 
				
			||||||
msg_ok "Python environment ready"
 | 
					msg_ok "Python environment ready"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ $STD apt-get install -y \
 | 
				
			|||||||
msg_ok "Installed Dependencies"
 | 
					msg_ok "Installed Dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NODE_VERSION="20" setup_nodejs
 | 
					NODE_VERSION="20" setup_nodejs
 | 
				
			||||||
fetch_and_deploy_gh_release "tududi" "chrisvel/tududi"
 | 
					fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" "tarball" "v0.80" "/opt/tududi"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msg_info "Configuring Tududi"
 | 
					msg_info "Configuring Tududi"
 | 
				
			||||||
cd /opt/tududi
 | 
					cd /opt/tududi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ elif command -v wget >/dev/null 2>&1; then
 | 
				
			|||||||
fi
 | 
					fi
 | 
				
			||||||
# This function enables error handling in the script by setting options and defining a trap for the ERR signal.
 | 
					# This function enables error handling in the script by setting options and defining a trap for the ERR signal.
 | 
				
			||||||
catch_errors() {
 | 
					catch_errors() {
 | 
				
			||||||
  set -Eeuo pipefail
 | 
					  set -Eeo pipefail
 | 
				
			||||||
  trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
 | 
					  trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -200,41 +200,32 @@ ssh_check() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
base_settings() {
 | 
					base_settings() {
 | 
				
			||||||
  # Default Settings
 | 
					  # Default Settings
 | 
				
			||||||
  CT_TYPE="1"
 | 
					  CT_TYPE=${var_unprivileged:-"1"}
 | 
				
			||||||
  DISK_SIZE="4"
 | 
					  DISK_SIZE=${var_disk:-"4"}
 | 
				
			||||||
  CORE_COUNT="1"
 | 
					  CORE_COUNT=${var_cpu:-"1"}
 | 
				
			||||||
  RAM_SIZE="1024"
 | 
					  RAM_SIZE=${var_ram:-"1024"}
 | 
				
			||||||
  VERBOSE="${1:-no}"
 | 
					  VERBOSE=${var_verbose:-"${1:-no}"}
 | 
				
			||||||
  PW=""
 | 
					  PW=${var_pw:-""}
 | 
				
			||||||
  CT_ID=$NEXTID
 | 
					  CT_ID=${var_ctid:-$NEXTID}
 | 
				
			||||||
  HN=$NSAPP
 | 
					  HN=${var_hostname:-$NSAPP}
 | 
				
			||||||
  BRG="vmbr0"
 | 
					  BRG=${var_brg:-"vmbr0"}
 | 
				
			||||||
  NET="dhcp"
 | 
					  NET=${var_net:-"dhcp"}
 | 
				
			||||||
  IPV6_METHOD="none"
 | 
					  IPV6_METHOD=${var_ipv6_method:-"none"}
 | 
				
			||||||
  IPV6_STATIC=""
 | 
					  IPV6_STATIC=${var_ipv6_static:-""}
 | 
				
			||||||
  GATE=""
 | 
					  GATE=${var_gateway:-""}
 | 
				
			||||||
  APT_CACHER=""
 | 
					  APT_CACHER=${var_apt_cacher:-""}
 | 
				
			||||||
  APT_CACHER_IP=""
 | 
					  APT_CACHER_IP=${var_apt_cacher_ip:-""}
 | 
				
			||||||
  MTU=""
 | 
					  MTU=${var_mtu:-""}
 | 
				
			||||||
  SD=""
 | 
					  SD=${var_storage:-""}
 | 
				
			||||||
  NS=""
 | 
					  NS=${var_ns:-""}
 | 
				
			||||||
  MAC=""
 | 
					  MAC=${var_mac:-""}
 | 
				
			||||||
  VLAN=""
 | 
					  VLAN=${var_vlan:-""}
 | 
				
			||||||
  SSH="no"
 | 
					  SSH=${var_ssh:-"no"}
 | 
				
			||||||
  SSH_AUTHORIZED_KEY=""
 | 
					  SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""}
 | 
				
			||||||
  TAGS="community-script;"
 | 
					  UDHCPC_FIX=${var_udhcpc_fix:-""}
 | 
				
			||||||
  ENABLE_FUSE="${1:-no}"
 | 
					  TAGS="community-script;${var_tags:-}"
 | 
				
			||||||
  ENABLE_TUN="${1:-no}"
 | 
					  ENABLE_FUSE=${var_fuse:-"${1:-no}"}
 | 
				
			||||||
 | 
					  ENABLE_TUN=${var_tun:-"${1:-no}"}
 | 
				
			||||||
  # Override default settings with variables from ct script
 | 
					 | 
				
			||||||
  CT_TYPE=${var_unprivileged:-$CT_TYPE}
 | 
					 | 
				
			||||||
  DISK_SIZE=${var_disk:-$DISK_SIZE}
 | 
					 | 
				
			||||||
  CORE_COUNT=${var_cpu:-$CORE_COUNT}
 | 
					 | 
				
			||||||
  RAM_SIZE=${var_ram:-$RAM_SIZE}
 | 
					 | 
				
			||||||
  VERB=${var_verbose:-$VERBOSE}
 | 
					 | 
				
			||||||
  TAGS="${TAGS}${var_tags:-}"
 | 
					 | 
				
			||||||
  ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}"
 | 
					 | 
				
			||||||
  ENABLE_TUN="${var_tun:-$ENABLE_TUN}"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
 | 
					  # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts
 | 
				
			||||||
  if [ -z "$var_os" ]; then
 | 
					  if [ -z "$var_os" ]; then
 | 
				
			||||||
@@ -244,6 +235,7 @@ base_settings() {
 | 
				
			|||||||
    var_version="12"
 | 
					    var_version="12"
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
write_config() {
 | 
					write_config() {
 | 
				
			||||||
  mkdir -p /opt/community-scripts
 | 
					  mkdir -p /opt/community-scripts
 | 
				
			||||||
  # This function writes the configuration to a file.
 | 
					  # This function writes the configuration to a file.
 | 
				
			||||||
@@ -354,7 +346,7 @@ echo_default() {
 | 
				
			|||||||
  echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
 | 
					  echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
 | 
				
			||||||
  echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
 | 
					  echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
 | 
				
			||||||
  echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
 | 
					  echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
 | 
				
			||||||
  if [ "$VERB" == "yes" ]; then
 | 
					  if [ "$VERBOSE" == "yes" ]; then
 | 
				
			||||||
    echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}"
 | 
					    echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}"
 | 
				
			||||||
  fi
 | 
					  fi
 | 
				
			||||||
  echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}"
 | 
					  echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -514,7 +514,42 @@ post_routines_common() {
 | 
				
			|||||||
  yes)
 | 
					  yes)
 | 
				
			||||||
    whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58
 | 
					    whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58
 | 
				
			||||||
    msg_info "Disabling subscription nag"
 | 
					    msg_info "Disabling subscription nag"
 | 
				
			||||||
    echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then echo 'Removing subscription nag from UI...'; sed -i '/data\.status/{s/\\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script
 | 
					    # Create external script, this is needed because DPkg::Post-Invoke is fidly with quote interpretation
 | 
				
			||||||
 | 
					    cat >/usr/local/bin/pve-remove-nag.sh <<'EOF'
 | 
				
			||||||
 | 
					#!/bin/sh
 | 
				
			||||||
 | 
					WEB_JS=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
 | 
				
			||||||
 | 
					if [ -s "$WEB_JS" ] && ! grep -q NoMoreNagging "$WEB_JS"; then
 | 
				
			||||||
 | 
					    echo "Patching Web UI nag..."
 | 
				
			||||||
 | 
					    sed -i -e "/data\.status/ s/!//" -e "/data\.status/ s/active/NoMoreNagging/" "$WEB_JS"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MOBILE_TPL=/usr/share/pve-yew-mobile-gui/index.html.tpl
 | 
				
			||||||
 | 
					MARKER="<!-- MANAGED BLOCK FOR MOBILE NAG -->"
 | 
				
			||||||
 | 
					if [ -f "$MOBILE_TPL" ] && ! grep -q "$MARKER" "$MOBILE_TPL"; then
 | 
				
			||||||
 | 
					    echo "Patching Mobile UI nag..."
 | 
				
			||||||
 | 
					    printf "%s\n" \
 | 
				
			||||||
 | 
					      "$MARKER" \
 | 
				
			||||||
 | 
					      "<script>" \
 | 
				
			||||||
 | 
					      "  function watchAndRemoveDialog() {" \
 | 
				
			||||||
 | 
					      "    const observer = new MutationObserver(() => {" \
 | 
				
			||||||
 | 
					      "      const dialog = document.querySelector('dialog[aria-label=\"No valid subscription\"]');" \
 | 
				
			||||||
 | 
					      "      if (dialog) { dialog.remove(); console.log('Removed dialog: No valid subscription'); observer.disconnect(); }" \
 | 
				
			||||||
 | 
					      "    });" \
 | 
				
			||||||
 | 
					      "    observer.observe(document.body, { childList: true, subtree: true });" \
 | 
				
			||||||
 | 
					      "  }" \
 | 
				
			||||||
 | 
					      "  setTimeout(watchAndRemoveDialog, 100);" \
 | 
				
			||||||
 | 
					      "</script>" \
 | 
				
			||||||
 | 
					      "" >> "$MOBILE_TPL"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    chmod 755 /usr/local/bin/pve-remove-nag.sh
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    cat >/etc/apt/apt.conf.d/no-nag-script <<'EOF'
 | 
				
			||||||
 | 
					DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh"; };
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					    chmod 644 /etc/apt/apt.conf.d/no-nag-script
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    msg_ok "Disabled subscription nag (Delete browser cache)"
 | 
					    msg_ok "Disabled subscription nag (Delete browser cache)"
 | 
				
			||||||
    ;;
 | 
					    ;;
 | 
				
			||||||
  no)
 | 
					  no)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user