diff --git a/package-lock.json b/package-lock.json index c8ca24e..f9d446c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "pve-scripts-local", "version": "0.1.0", "dependencies": { + "@radix-ui/react-slot": "^1.2.3", "@t3-oss/env-nextjs": "^0.13.8", "@tanstack/react-query": "^5.87.4", "@trpc/client": "^11.0.0", @@ -19,6 +20,8 @@ "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", "better-sqlite3": "^9.6.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "next": "^15.5.3", "node-pty": "^1.0.0", "react": "^19.0.0", @@ -28,6 +31,7 @@ "server-only": "^0.0.1", "strip-ansi": "^7.1.2", "superjson": "^2.2.1", + "tailwind-merge": "^3.3.1", "ws": "^8.18.3", "zod": "^3.24.2" }, @@ -1927,6 +1931,39 @@ "dev": true, "license": "MIT" }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.34", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.34.tgz", @@ -4353,12 +4390,33 @@ "node": ">=18" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -9606,6 +9664,16 @@ "dev": true, "license": "MIT" }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", diff --git a/package.json b/package.json index 01096c1..45cfab9 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@radix-ui/react-slot": "^1.2.3", "@t3-oss/env-nextjs": "^0.13.8", "@tanstack/react-query": "^5.87.4", "@trpc/client": "^11.0.0", @@ -33,6 +34,8 @@ "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", "better-sqlite3": "^9.6.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "next": "^15.5.3", "node-pty": "^1.0.0", "react": "^19.0.0", @@ -42,6 +45,7 @@ "server-only": "^0.0.1", "strip-ansi": "^7.1.2", "superjson": "^2.2.1", + "tailwind-merge": "^3.3.1", "ws": "^8.18.3", "zod": "^3.24.2" }, diff --git a/scripts/ct/2fauth.sh b/scripts/ct/2fauth.sh new file mode 100644 index 0000000..b301cf2 --- /dev/null +++ b/scripts/ct/2fauth.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(dirname "$0")" +source "$SCRIPT_DIR/../core/build.func" +# Copyright (c) 2021-2025 community-scripts ORG +# Author: jkrgr0 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.2fauth.app/ + +APP="2FAuth" +var_tags="${var_tags:-2fa;authenticator}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +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/2fauth" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "2fauth" "Bubka/2FAuth"; then + $STD apt update + $STD apt -y upgrade + + msg_info "Creating Backup" + mv "/opt/2fauth" "/opt/2fauth-backup" + if ! dpkg -l | grep -q 'php8.3'; then + cp /etc/nginx/conf.d/2fauth.conf /etc/nginx/conf.d/2fauth.conf.bak + fi + msg_ok "Backup Created" + + if ! dpkg -l | grep -q 'php8.3'; then + $STD apt-get install -y \ + lsb-release \ + gnupg2 + PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli" PHP_FPM="YES" setup_php + sed -i 's/php8.2/php8.3/g' /etc/nginx/conf.d/2fauth.conf + fi + fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" + setup_composer + mv "/opt/2fauth-backup/.env" "/opt/2fauth/.env" + mv "/opt/2fauth-backup/storage" "/opt/2fauth/storage" + cd "/opt/2fauth" || return + chown -R www-data: "/opt/2fauth" + chmod -R 755 "/opt/2fauth" + export COMPOSER_ALLOW_SUPERUSER=1 + $STD composer install --no-dev --prefer-source + php artisan 2fauth:install + $STD systemctl restart nginx + + msg_info "Cleaning Up" + if dpkg -l | grep -q 'php8.2'; then + $STD apt remove --purge -y php8.2* + fi + $STD apt -y autoremove + $STD apt -y autoclean + $STD apt -y clean + msg_ok "Cleanup Completed" + 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}:80${CL}" diff --git a/scripts/install/2fauth-install.sh b/scripts/install/2fauth-install.sh new file mode 100644 index 0000000..2ef85c8 --- /dev/null +++ b/scripts/install/2fauth-install.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: jkrgr0 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.2fauth.app/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + lsb-release \ + nginx +msg_ok "Installed Dependencies" + +PHP_VERSION="8.3" PHP_MODULE="common,ctype,fileinfo,mysql,cli" PHP_FPM="YES" setup_php +setup_composer +setup_mariadb + +msg_info "Setting up Database" +DB_NAME=2fauth_db +DB_USER=2fauth +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "2FAuth Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" +} >>~/2FAuth.creds +msg_ok "Set up Database" + +fetch_and_deploy_gh_release "2fauth" "Bubka/2FAuth" + +msg_info "Setup 2FAuth" +cd /opt/2fauth || exit +cp .env.example .env +IPADDRESS=$(hostname -I | awk '{print $1}') +sed -i -e "s|^APP_URL=.*|APP_URL=http://$IPADDRESS|" \ + -e "s|^DB_CONNECTION=$|DB_CONNECTION=mysql|" \ + -e "s|^DB_DATABASE=$|DB_DATABASE=$DB_NAME|" \ + -e "s|^DB_HOST=$|DB_HOST=127.0.0.1|" \ + -e "s|^DB_PORT=$|DB_PORT=3306|" \ + -e "s|^DB_USERNAME=$|DB_USERNAME=$DB_USER|" \ + -e "s|^DB_PASSWORD=$|DB_PASSWORD=$DB_PASS|" .env +export COMPOSER_ALLOW_SUPERUSER=1 +$STD composer update --no-plugins --no-scripts +$STD composer install --no-dev --prefer-source --no-plugins --no-scripts +$STD php artisan key:generate --force +$STD php artisan migrate:refresh +$STD php artisan passport:install -q -n +$STD php artisan storage:link +$STD php artisan config:cache +chown -R www-data: /opt/2fauth +chmod -R 755 /opt/2fauth +msg_ok "Setup 2fauth" + +msg_info "Configure Service" +cat </etc/nginx/conf.d/2fauth.conf +server { + listen 80; + root /opt/2fauth/public; + server_name $IPADDRESS; + index index.php; + charset utf-8; + + location / { + try_files \$uri \$uri/ /index.php?\$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php\$ { + fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; + fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; + include fastcgi_params; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} +EOF +systemctl reload nginx +msg_ok "Configured Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" diff --git a/scripts/json/undefined.json b/scripts/json/undefined.json index 397772e..b2c068a 100644 --- a/scripts/json/undefined.json +++ b/scripts/json/undefined.json @@ -1,4 +1,84 @@ [ + { + "name": "sassanix/Warracker", + "version": "0.10.1.14", + "date": "2025-10-06T23:35:16Z" + }, + { + "name": "outline/outline", + "version": "v1.0.0-1", + "date": "2025-10-06T23:16:32Z" + }, + { + "name": "Ombi-app/Ombi", + "version": "v4.47.1", + "date": "2025-01-05T21:14:23Z" + }, + { + "name": "Kometa-Team/Kometa", + "version": "v2.2.2", + "date": "2025-10-06T21:31:07Z" + }, + { + "name": "booklore-app/booklore", + "version": "v1.5.0", + "date": "2025-10-06T20:56:57Z" + }, + { + "name": "grokability/snipe-it", + "version": "v8.3.3", + "date": "2025-10-06T19:57:17Z" + }, + { + "name": "meilisearch/meilisearch", + "version": "prototype-shorten-snapshot-creation-2", + "date": "2025-10-06T19:36:54Z" + }, + { + "name": "TwiN/gatus", + "version": "v5.26.0", + "date": "2025-10-06T17:57:27Z" + }, + { + "name": "seerr-team/seerr", + "version": "preview-seerr", + "date": "2025-10-06T16:50:29Z" + }, + { + "name": "zwave-js/zwave-js-ui", + "version": "v11.4.0", + "date": "2025-10-06T16:08:51Z" + }, + { + "name": "fuma-nama/fumadocs", + "version": "fumadocs-ui@15.8.4", + "date": "2025-10-06T15:41:49Z" + }, + { + "name": "bunkerity/bunkerweb", + "version": "v1.6.5", + "date": "2025-10-06T15:25:17Z" + }, + { + "name": "bastienwirtz/homer", + "version": "v25.10.1", + "date": "2025-10-06T14:23:20Z" + }, + { + "name": "chrisvel/tududi", + "version": "v0.83", + "date": "2025-10-06T13:49:52Z" + }, + { + "name": "dgtlmoon/changedetection.io", + "version": "0.50.16", + "date": "2025-10-06T13:40:13Z" + }, + { + "name": "n8n-io/n8n", + "version": "n8n@1.114.3", + "date": "2025-10-06T12:22:22Z" + }, { "name": "Graylog2/graylog2-server", "version": "7.0.0-beta.3", @@ -29,11 +109,6 @@ "version": "v0.24.82", "date": "2025-10-06T07:56:13Z" }, - { - "name": "dgtlmoon/changedetection.io", - "version": "0.50.15", - "date": "2025-10-06T07:15:01Z" - }, { "name": "firefly-iii/firefly-iii", "version": "v6.4.0", @@ -74,11 +149,6 @@ "version": "4.5.3", "date": "2025-08-25T13:59:56Z" }, - { - "name": "outline/outline", - "version": "v1.0.0-0", - "date": "2025-10-05T20:30:31Z" - }, { "name": "plankanban/planka", "version": "planka-1.0.5", @@ -101,19 +171,14 @@ }, { "name": "runtipi/runtipi", - "version": "v4.4.0", - "date": "2025-09-02T19:26:18Z" + "version": "nightly", + "date": "2025-10-05T14:13:25Z" }, { "name": "Prowlarr/Prowlarr", "version": "v2.0.5.5160", "date": "2025-08-23T21:23:11Z" }, - { - "name": "chrisvel/tududi", - "version": "v0.82-rc5", - "date": "2025-09-23T07:31:12Z" - }, { "name": "TandoorRecipes/recipes", "version": "2.3.0", @@ -159,11 +224,6 @@ "version": "2.520", "date": "2025-10-05T00:51:34Z" }, - { - "name": "Ombi-app/Ombi", - "version": "v4.47.1", - "date": "2025-01-05T21:14:23Z" - }, { "name": "ollama/ollama", "version": "v0.12.4-rc5", @@ -224,16 +284,6 @@ "version": "2025.10.1", "date": "2025-10-03T18:10:59Z" }, - { - "name": "fuma-nama/fumadocs", - "version": "@fumadocs/mdx-remote@1.4.2", - "date": "2025-10-03T17:01:32Z" - }, - { - "name": "bunkerity/bunkerweb", - "version": "v1.6.5", - "date": "2025-10-03T16:43:34Z" - }, { "name": "immich-app/immich", "version": "v2.0.1", @@ -259,11 +309,6 @@ "version": "v0.30.1", "date": "2025-10-03T06:55:25Z" }, - { - "name": "booklore-app/booklore", - "version": "v1.4.1", - "date": "2025-10-03T06:52:35Z" - }, { "name": "redis/redis", "version": "8.2.2", @@ -279,16 +324,6 @@ "version": "v0.9.95", "date": "2025-10-02T16:07:18Z" }, - { - "name": "meilisearch/meilisearch", - "version": "prototype-shorten-snapshot-creation-0", - "date": "2025-10-02T15:16:05Z" - }, - { - "name": "n8n-io/n8n", - "version": "n8n@1.112.6", - "date": "2025-09-26T10:56:27Z" - }, { "name": "theonedev/onedev", "version": "v13.0.7", @@ -389,11 +424,6 @@ "version": "v4.4.2", "date": "2025-09-30T20:16:13Z" }, - { - "name": "TwiN/gatus", - "version": "v5.25.2", - "date": "2025-09-30T18:32:35Z" - }, { "name": "WordPress/WordPress", "version": "4.7.31", @@ -414,11 +444,6 @@ "version": "4.4.46", "date": "2025-09-30T13:21:24Z" }, - { - "name": "fallenbagel/jellyseerr", - "version": "preview-rename-tags", - "date": "2025-09-30T12:50:15Z" - }, { "name": "emqx/emqx", "version": "e6.0.0", @@ -459,11 +484,6 @@ "version": "v2.7.12", "date": "2025-05-29T17:08:26Z" }, - { - "name": "sassanix/Warracker", - "version": "0.10.1.13", - "date": "2025-09-29T17:11:25Z" - }, { "name": "AdguardTeam/AdGuardHome", "version": "v0.107.67", @@ -536,8 +556,8 @@ }, { "name": "javedh-dev/tracktor", - "version": "0.3.17", - "date": "2025-09-27T07:00:36Z" + "version": "0.3.18", + "date": "2025-09-27T10:32:09Z" }, { "name": "Dolibarr/dolibarr", @@ -554,11 +574,6 @@ "version": "v4.104.2", "date": "2025-09-26T22:34:32Z" }, - { - "name": "bastienwirtz/homer", - "version": "v25.09.1", - "date": "2025-09-26T19:22:16Z" - }, { "name": "traefik/traefik", "version": "v3.5.3", @@ -624,11 +639,6 @@ "version": "v1.9.10", "date": "2025-09-24T13:49:53Z" }, - { - "name": "zwave-js/zwave-js-ui", - "version": "v11.3.1", - "date": "2025-09-24T11:58:00Z" - }, { "name": "syncthing/syncthing", "version": "v2.0.10", @@ -719,11 +729,6 @@ "version": "v0.23.2", "date": "2025-09-18T17:18:59Z" }, - { - "name": "grokability/snipe-it", - "version": "v8.3.2", - "date": "2025-09-18T13:55:58Z" - }, { "name": "NLnetLabs/unbound", "version": "release-1.24.0", @@ -1039,11 +1044,6 @@ "version": "latest", "date": "2025-08-15T15:33:51Z" }, - { - "name": "Kometa-Team/Kometa", - "version": "v2.2.1", - "date": "2025-08-13T19:49:01Z" - }, { "name": "swapplications/uhf-server-dist", "version": "1.5.1", diff --git a/scripts/json/zigbee2mqtt.json b/scripts/json/zigbee2mqtt.json index 7cd8847..d8ac5fc 100644 --- a/scripts/json/zigbee2mqtt.json +++ b/scripts/json/zigbee2mqtt.json @@ -12,7 +12,7 @@ "documentation": "https://www.zigbee2mqtt.io/guide/getting-started/", "website": "https://www.zigbee2mqtt.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/zigbee2mqtt.webp", - "config_path": "/opt/zigbee2mqtt/data/configuration.yaml", + "config_path": "debian: /opt/zigbee2mqtt/data/configuration.yaml | alpine: /var/lib/zigbee2mqtt/configuration.yaml", "description": "Zigbee2MQTT is an open-source software project that allows you to use Zigbee-based smart home devices (such as those sold under the Philips Hue and Ikea Tradfri brands) with MQTT-based home automation systems, like Home Assistant, Node-RED, and others. The software acts as a bridge between your Zigbee devices and MQTT, allowing you to control and monitor these devices from your home automation system.", "install_methods": [ { diff --git a/src/app/_components/Badge.tsx b/src/app/_components/Badge.tsx index b9cae13..8fcd68e 100644 --- a/src/app/_components/Badge.tsx +++ b/src/app/_components/Badge.tsx @@ -16,15 +16,15 @@ export function Badge({ variant, type, noteType, status, executionMode, children const getTypeStyles = (scriptType: string) => { switch (scriptType.toLowerCase()) { case 'ct': - return 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border-blue-200 dark:border-blue-700'; + return 'bg-primary/10 text-primary border-primary/20'; case 'addon': - return 'bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-200 border-purple-200 dark:border-purple-700'; + return 'bg-purple-500/10 text-purple-400 border-purple-500/20'; case 'vm': - return 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border-green-200 dark:border-green-700'; + return 'bg-green-500/10 text-green-400 border-green-500/20'; case 'pve': - return 'bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-200 border-orange-200 dark:border-orange-700'; + return 'bg-orange-500/10 text-orange-400 border-orange-500/20'; default: - return 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 border-gray-200 dark:border-gray-600'; + return 'bg-muted text-muted-foreground border-border'; } }; @@ -34,45 +34,45 @@ export function Badge({ variant, type, noteType, status, executionMode, children return `inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border ${type ? getTypeStyles(type) : getTypeStyles('unknown')}`; case 'updateable': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border border-green-200 dark:border-green-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-500/10 text-green-400 border border-green-500/20'; case 'privileged': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-200 border border-red-200 dark:border-red-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-destructive/10 text-destructive border border-destructive/20'; case 'status': switch (status) { case 'success': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border border-green-200 dark:border-green-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-500/10 text-green-400 border border-green-500/20'; case 'failed': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-200 border border-red-200 dark:border-red-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-destructive/10 text-destructive border border-destructive/20'; case 'in_progress': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-500/10 text-yellow-400 border border-yellow-500/20'; default: - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 border border-gray-200 dark:border-gray-600'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground border border-border'; } case 'execution-mode': switch (executionMode) { case 'local': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border border-blue-200 dark:border-blue-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary border border-primary/20'; case 'ssh': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-800 dark:text-purple-200 border border-purple-200 dark:border-purple-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-500/10 text-purple-400 border border-purple-500/20'; default: - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 border border-gray-200 dark:border-gray-600'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground border border-border'; } case 'note': switch (noteType) { case 'warning': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 dark:bg-yellow-900/30 text-yellow-800 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-500/10 text-yellow-400 border border-yellow-500/20'; case 'error': - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-200 border border-red-200 dark:border-red-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-destructive/10 text-destructive border border-destructive/20'; default: - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border border-blue-200 dark:border-blue-700'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary border border-primary/20'; } default: - return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 border border-gray-200 dark:border-gray-600'; + return 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-muted text-muted-foreground border border-border'; } }; diff --git a/src/app/_components/CategorySidebar.tsx b/src/app/_components/CategorySidebar.tsx index 29f107e..8ddb568 100644 --- a/src/app/_components/CategorySidebar.tsx +++ b/src/app/_components/CategorySidebar.tsx @@ -195,24 +195,24 @@ export function CategorySidebar({ }); return ( -
{/* Header */} -
+
{!isCollapsed && (
-

Categories

-

{totalScripts} Total scripts

+

Categories

+

{totalScripts} Total scripts

)} {/* Tooltip */} -
+
All Categories ({totalScripts})
@@ -332,25 +332,25 @@ export function CategorySidebar({ onClick={() => onCategorySelect(category)} className={`w-12 h-12 rounded-lg flex flex-col items-center justify-center transition-colors relative ${ isSelected - ? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-blue-800' - : 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300' + ? 'bg-primary/10 text-primary border border-primary/20' + : 'hover:bg-accent text-muted-foreground' }`} > {count} {/* Tooltip */} -
+
{category} ({count})
diff --git a/src/app/_components/DarkModeProvider.tsx b/src/app/_components/DarkModeProvider.tsx deleted file mode 100644 index 0700a95..0000000 --- a/src/app/_components/DarkModeProvider.tsx +++ /dev/null @@ -1,86 +0,0 @@ -'use client'; - -import { createContext, useContext, useEffect, useState } from 'react'; - -type Theme = 'light' | 'dark' | 'system'; - -interface DarkModeContextType { - theme: Theme; - setTheme: (theme: Theme) => void; - isDark: boolean; -} - -const DarkModeContext = createContext(undefined); - -export function DarkModeProvider({ children }: { children: React.ReactNode }) { - const [theme, setThemeState] = useState('system'); - const [isDark, setIsDark] = useState(false); - const [mounted, setMounted] = useState(false); - - // Initialize theme from localStorage after mount - useEffect(() => { - const stored = localStorage.getItem('theme') as Theme; - if (stored && ['light', 'dark', 'system'].includes(stored)) { - setThemeState(stored); - } - - // Set initial isDark state based on current DOM state - const currentlyDark = document.documentElement.classList.contains('dark'); - setIsDark(currentlyDark); - setMounted(true); - }, []); - - // Update dark mode state and DOM when theme changes - useEffect(() => { - if (!mounted) return; - - const updateDarkMode = () => { - const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - const shouldBeDark = theme === 'dark' || (theme === 'system' && systemDark); - - // Only update if there's actually a change - if (shouldBeDark !== isDark) { - setIsDark(shouldBeDark); - - // Apply to document - if (shouldBeDark) { - document.documentElement.classList.add('dark'); - } else { - document.documentElement.classList.remove('dark'); - } - } - }; - - updateDarkMode(); - - // Listen for system theme changes - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - const handleChange = () => { - if (theme === 'system') { - updateDarkMode(); - } - }; - - mediaQuery.addEventListener('change', handleChange); - return () => mediaQuery.removeEventListener('change', handleChange); - }, [theme, mounted, isDark]); - - const setTheme = (newTheme: Theme) => { - setThemeState(newTheme); - localStorage.setItem('theme', newTheme); - }; - - return ( - - {children} - - ); -} - -export function useDarkMode() { - const context = useContext(DarkModeContext); - if (context === undefined) { - throw new Error('useDarkMode must be used within a DarkModeProvider'); - } - return context; -} \ No newline at end of file diff --git a/src/app/_components/DarkModeToggle.tsx b/src/app/_components/DarkModeToggle.tsx deleted file mode 100644 index 5cccd61..0000000 --- a/src/app/_components/DarkModeToggle.tsx +++ /dev/null @@ -1,66 +0,0 @@ -'use client'; - -import { useDarkMode } from './DarkModeProvider'; - -export function DarkModeToggle() { - const { theme, setTheme, isDark } = useDarkMode(); - - const toggleTheme = () => { - if (theme === 'light') { - setTheme('dark'); - } else if (theme === 'dark') { - setTheme('system'); - } else { - setTheme('light'); - } - }; - - const getIcon = () => { - if (theme === 'light') { - return ( - - - - ); - } else if (theme === 'dark') { - return ( - - - - ); - } else { - // System theme icon - return ( - - - - ); - } - }; - - const getLabel = () => { - if (theme === 'light') return 'Light mode'; - if (theme === 'dark') return 'Dark mode'; - return 'System theme'; - }; - - return ( - - ); -} \ No newline at end of file diff --git a/src/app/_components/DiffViewer.tsx b/src/app/_components/DiffViewer.tsx index 0a79a7b..e2245bc 100644 --- a/src/app/_components/DiffViewer.tsx +++ b/src/app/_components/DiffViewer.tsx @@ -45,17 +45,17 @@ export function DiffViewer({ scriptSlug, filePath, isOpen, onClose }: DiffViewer key={index} className={`flex font-mono text-sm ${ isAdded - ? 'bg-green-50 text-green-800 border-l-4 border-green-400' + ? 'bg-green-500/10 text-green-400 border-l-4 border-green-500' : isRemoved - ? 'bg-red-50 text-red-800 border-l-4 border-red-400' - : 'bg-gray-50 text-gray-700' + ? 'bg-destructive/10 text-destructive border-l-4 border-destructive' + : 'bg-muted text-muted-foreground' }`} > -
+
{lineNumber}
- + {isAdded ? '+' : isRemoved ? '-' : ' '} {content} @@ -66,27 +66,27 @@ export function DiffViewer({ scriptSlug, filePath, isOpen, onClose }: DiffViewer return (
-
+
{/* Header */} -
+
-

Script Diff

-

{filePath}

+

Script Diff

+

{filePath}

{/* Legend */} -
+
-
- Added (Remote) +
+ Added (Remote)
-
- Removed (Local) +
+ Removed (Local)
-
- Unchanged +
+ Unchanged
@@ -117,14 +117,14 @@ export function DiffViewer({ scriptSlug, filePath, isOpen, onClose }: DiffViewer
{diffData?.success ? ( diffData.diff ? ( -
+
{diffData.diff.split('\n').map((line, index) => line.trim() ? renderDiffLine(line, index) : null )}
) : ( -
- +
+

No differences found

@@ -132,16 +132,16 @@ export function DiffViewer({ scriptSlug, filePath, isOpen, onClose }: DiffViewer
) ) : diffData?.error ? ( -
- +
+

Error loading diff

{diffData.error}

) : ( -
-
+
+

Loading diff...

)} diff --git a/src/app/_components/ExecutionModeModal.tsx b/src/app/_components/ExecutionModeModal.tsx index a3af4bc..bdbda8f 100644 --- a/src/app/_components/ExecutionModeModal.tsx +++ b/src/app/_components/ExecutionModeModal.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import type { Server } from '../../types/server'; +import { Button } from './ui/button'; interface ExecutionModeModalProps { isOpen: boolean; @@ -60,40 +61,42 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E if (!isOpen) return null; return ( -
-
+
+
{/* Header */} -
-

Execution Mode

- +
{/* Content */}
-

+

Where would you like to execute "{scriptName}"?

{error && ( -
+
- +
-

{error}

+

{error}

@@ -107,8 +110,8 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E
handleModeChange('ssh')} > @@ -120,20 +123,20 @@ export function ExecutionModeModal({ isOpen, onClose, onExecute, scriptName }: E value="ssh" checked={selectedMode === 'ssh'} onChange={() => handleModeChange('ssh')} - className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" + className="h-4 w-4 text-primary focus:ring-primary border-border" />