diff --git a/.gitignore b/.gitignore index 4fb5a51..be54252 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ venv/ *.pyc sdkconfig.old +# Local documentation (not for upstream) +UI修改.md + # =================== # PKI Server Sensitive Files # =================== diff --git a/OTA_Server/server.py b/OTA_Server/server.py index a6a5664..f5619db 100644 --- a/OTA_Server/server.py +++ b/OTA_Server/server.py @@ -76,7 +76,7 @@ def do_HEAD(self): self.end_headers() else: self.send_error(404, "Firmware not found") - elif path == '/www.bin': + elif path == '/www.bin' or path == '/www': www_path = BUILD_DIR / WWW_NAME if www_path.exists(): stat = www_path.stat() @@ -101,7 +101,7 @@ def do_GET(self): self.handle_version() elif path == '/firmware' or path == '/firmware.bin' or path == '/TianShanOS.bin': self.handle_firmware() - elif path == '/www.bin': + elif path == '/www.bin' or path == '/www': self.handle_www() elif path == '/info': self.handle_info() diff --git a/README.md b/README.md index e15f961..9d1d802 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # TianShanOS +[English](README_EN.md) | [中文](README.md) + [![Build Status](https://github.com/thomas-hiddenpeak/TianshanOS/actions/workflows/build.yml/badge.svg)](https://github.com/thomas-hiddenpeak/TianshanOS/actions/workflows/build.yml) [![Release](https://img.shields.io/github/v/release/thomas-hiddenpeak/TianshanOS)](https://github.com/thomas-hiddenpeak/TianshanOS/releases/latest) [![License](https://img.shields.io/github/license/thomas-hiddenpeak/TianshanOS)](LICENSE) diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..fb9bc2e --- /dev/null +++ b/README_EN.md @@ -0,0 +1,204 @@ +# TianShanOS + +[English](README_EN.md) | [中文](README.md) + +> TianShan Operating System - ESP32 Rack Management Operating System +> +> TianShan controls both the northern and southern basins — northward to AGX for AI computing power, southward to LPMU for general computing and storage services + +``` +╔══════════════════════════════════════════════════════════════════════╗ +║ ║ +║ ████████╗██╗ █████╗ ███╗ ██╗███████╗██╗ ██╗ █████╗ ███╗ ██╗ ║ +║ ╚══██╔══╝██║██╔══██╗████╗ ██║██╔════╝██║ ██║██╔══██╗████╗ ██║ ║ +║ ██║ ██║███████║██╔██╗ ██║███████╗███████║███████║██╔██╗ ██║ ║ +║ ██║ ██║██╔══██║██║╚██╗██║╚════██║██╔══██║██╔══██║██║╚██╗██║ ║ +║ ██║ ██║██║ ██║██║ ╚████║███████║██║ ██║██║ ██║██║ ╚████║ ║ +║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ║ +║ ║ +║ TianShanOS v0.3.0 ║ +║ ESP32 Rack Management Operating System ║ +║ ║ +╚══════════════════════════════════════════════════════════════════════╝ +``` + +--- + +## 🚀 Project Overview + +TianShanOS is a **configuration-oriented rather than code-oriented** embedded operating system framework, developed based on ESP-IDF v5.5+, designed for rack management of NVIDIA Jetson AGX + DFRobot LattePanda Mu carrier boards. + +### Core Features + +- **Fully Modular** - 220+ C source files, 110+ header files, 18 independent components +- **Configuration-Oriented** - Define hardware pins and system behavior through JSON configuration files +- **Unified Configuration System** - SD card priority + NVS backup dual-write, supports hot-swap sync, Schema version migration +- **Automation Engine** - Data source collection, rule engine, action execution, variable system +- **OTA Upgrade** - HTTPS/SD card dual-channel firmware upgrade with automatic rollback protection +- **Cross-Platform Design** - Supports ESP32-S3 and ESP32-P4 +- **Security First** - HTTPS/mTLS, SSH public key authentication, PKI certificate management, tiered permissions +- **Unified Interface** - CLI and WebUI share Core API with consistent behavior +- **Multi-Language Support** - Chinese, English, Japanese, and Korean interface support + +### System Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ User Interaction Layer (CLI / WebUI / HTTPS API) │ +├─────────────────────────────────────────────────────────────────┤ +│ Core API Layer (ts_api) │ +├─────────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ +│ │ Service │ │ Automation │ │ Security Module │ │ +│ │ Management │ │ Engine │ │ (SSH/PKI/mTLS) │ │ +│ │ (8-stage) │ │ (Data/Rules) │ │ │ │ +│ └──────────────┘ └──────────────┘ └──────────────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ Event/Message Bus (ts_event) │ +├─────────────────────────────────────────────────────────────────┤ +│ Config Management (NVS/SD/Defaults, Priority: Mem>SD>NVS>Def) │ +├─────────────────────────────────────────────────────────────────┤ +│ Hardware Abstraction Layer (GPIO/PWM/I2C/SPI/UART/ADC) │ +├─────────────────────────────────────────────────────────────────┤ +│ Platform Adaptation Layer (ESP32-S3 / ESP32-P4) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📦 Project Structure + +``` +TianShanOS/ +├── components/ # ESP-IDF Components (18 total) +│ ├── ts_core/ # Core Framework (config/event/service/log) +│ ├── ts_hal/ # Hardware Abstraction Layer (GPIO/PWM/I2C/SPI/UART/ADC) +│ ├── ts_console/ # Console (commands/multi-language/script engine) +│ ├── ts_api/ # Core API (unified interface layer) +│ ├── ts_automation/ # Automation Engine (datasource/rules/actions/variables) +│ ├── ts_led/ # LED System (WS2812/layers/effects/filters) +│ ├── ts_net/ # Networking (WiFi/Ethernet/DHCP/NAT/HTTP) +│ ├── ts_security/ # Security (SSH client/auth/encryption/SFTP) +│ ├── ts_https/ # HTTPS Server (mTLS/API gateway) +│ ├── ts_cert/ # Certificate Management (generate/issue/verify) +│ ├── ts_pki_client/ # PKI Client (certificate request/renewal) +│ ├── ts_webui/ # WebUI (REST API/WebSocket/dashboard) +│ ├── ts_storage/ # Storage (SPIFFS/SD card/file operations) +│ ├── ts_ota/ # OTA Upgrade (HTTPS/SD card/rollback) +│ ├── ts_drivers/ # Device Drivers (fan/power/AGX/temperature) +│ └── ts_jsonpath/ # JSONPath Parser +├── boards/ # Board Configurations (pins.json/services.json) +├── main/ # Main Program Entry +├── docs/ # Documentation +├── sdcard/ # SD Card Content Template +├── partitions.csv # Partition Table (factory 3MB + storage) +└── sdkconfig.defaults # Default Configuration +``` + +--- + +## 🛠️ Development Environment + +### Dependencies +- ESP-IDF v5.5.2+ +- Python 3.10+ +- CMake 3.16+ + +### Quick Start + +```bash +# Clone the repository +git clone https://github.com/thomas-hiddenpeak/TianshanOS.git +cd TianShanOS + +# Set up ESP-IDF environment +. $HOME/esp/v5.5/esp-idf/export.sh + +# Set target chip +idf.py set-target esp32s3 + +# Configure project (TianShanOS options are in the top-level menu) +idf.py menuconfig + +# Build +idf.py build + +# Flash and monitor +idf.py -p /dev/ttyACM0 flash monitor +``` + +### VS Code Development + +Using the ESP-IDF extension is recommended, which supports one-click build/flash/monitor, Menuconfig GUI, code completion and navigation. + +For detailed instructions, please refer to the [Quick Start Guide](docs/QUICK_START.md). + +--- + +## 📚 Documentation + +| Document | Description | +|----------|-------------| +| [Quick Start](docs/QUICK_START.md) | Environment setup and first run | +| [Architecture Design](docs/ARCHITECTURE_DESIGN.md) | System architecture and design decisions | +| [Config System Design](docs/CONFIG_SYSTEM_DESIGN.md) | Detailed design of unified configuration system | +| [Automation Engine](docs/AUTOMATION_ENGINE.md) | Trigger-condition-action system | +| [GPIO Pin Mapping](docs/GPIO_MAPPING.md) | Hardware pin definitions and PCB mapping | +| [API Design](docs/API_DESIGN.md) | REST API architecture and design philosophy | +| [API Reference](docs/API_REFERENCE.md) | REST API and CLI commands | +| [Command Specification](docs/COMMAND_SPECIFICATION.md) | CLI command format specification | +| [Board Configuration](docs/BOARD_CONFIGURATION.md) | Pin and service configuration guide | +| [LED Architecture](docs/LED_ARCHITECTURE.md) | LED system multi-device multi-layer architecture | +| [Security Implementation](docs/SECURITY_IMPLEMENTATION.md) | SSH, PKI, authentication mechanisms | +| [Development Progress](docs/DEVELOPMENT_PROGRESS.md) | Feature implementation status tracking | +| [Test Plan](docs/TEST_PLAN.md) | Testing strategy and use cases | +| [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues and solutions | + +--- + +## 🎯 Current Status + +**Version**: 0.3.0 +**Phase**: Phase 20 Complete - Automation Engine, SSH Remote Execution, Variable System + +### Completed Features + +| Module | Features | +|--------|----------| +| Core Framework | Configuration management, event bus, 8-stage service management, logging system | +| Configuration System | SD card priority + NVS backup dual-write, hot-swap sync, Schema version migration | +| Hardware Abstraction | GPIO, PWM, I2C, SPI, UART, ADC | +| LED System | WS2812 driver, multi-device multi-layer, effects engine, BMP/PNG/JPG/GIF | +| Console | Command system, multi-language, script engine, configuration persistence | +| Networking | WiFi, Ethernet W5500, HTTP/HTTPS server | +| Security | Session management, Token authentication, AES-GCM, RSA/EC, SSH client, PKI certificate management | +| Drivers | Fan control, power monitoring (ADC/INA3221/PZEM), AGX/LPMU power control, USB MUX | +| WebUI | REST API gateway, WebSocket broadcast, frontend dashboard | +| OTA | Dual-partition upgrade, version detection, integrity verification, automatic rollback | +| Automation Engine | Trigger-condition-action system, SSH remote execution, regex parsing, variable system | + +### Automation Engine Features + +- **Data Sources**: Timer, SSH command output, system metrics, GPIO, variables, HTTP +- **Triggers**: Threshold, regex matching, time expressions, change detection +- **Actions**: Variable setting, command execution, event publishing, logging +- **SSH Integration**: Supports continuous monitoring commands (e.g., `ping`), real-time variable updates + +### Unified Configuration System Features + +- **7 Configuration Modules**: Network, DHCP, WiFi, LED, Fan, Device, System +- **Dual-Write Sync Mechanism**: NVS + SD card simultaneous write ensures consistency +- **Hot-Swap Handling**: Mark as `pending_sync` during SD card removal, auto-sync on insertion +- **Priority Management**: Memory cache > SD card > NVS > Schema defaults +- **Sequence Number Mechanism**: No RTC required, uses monotonically increasing sequence numbers to determine configuration versions +- **Schema Migration**: Supports configuration format upgrades and backward compatibility +- **Explicit Persistence**: CLI modifications are temporary by default, use `--save` for explicit persistence + +See [Development Progress](docs/DEVELOPMENT_PROGRESS.md) for detailed status. + +--- + +## 👥 Contributors + +- Thomas (Project Lead) +- massif-01 diff --git a/components/ts_api/src/ts_api_device.c b/components/ts_api/src/ts_api_device.c index 9c71230..f63a02a 100644 --- a/components/ts_api/src/ts_api_device.c +++ b/components/ts_api/src/ts_api_device.c @@ -17,9 +17,10 @@ #include #include #include -#include #include +#include #include +#include "esp_timer.h" #include "lwip/inet_chksum.h" #include "lwip/ip.h" #include "lwip/icmp.h" @@ -581,7 +582,7 @@ static const ts_api_endpoint_t device_endpoints[] = { }, { .name = "device.ping", - .description = "Test network connectivity (TCP port check)", + .description = "Test network connectivity (ICMP ping)", .category = TS_API_CAT_DEVICE, .handler = api_device_ping, .requires_auth = false, diff --git a/components/ts_automation/src/ts_rule_engine.c b/components/ts_automation/src/ts_rule_engine.c index cba637d..4f10c42 100644 --- a/components/ts_automation/src/ts_rule_engine.c +++ b/components/ts_automation/src/ts_rule_engine.c @@ -959,8 +959,8 @@ static esp_err_t execute_gpio_action(const ts_auto_action_t *action) return ESP_ERR_INVALID_ARG; } - ESP_LOGI(TAG, "GPIO action: pin=%d, level=%d, pulse=%dms", - action->gpio.pin, action->gpio.level, action->gpio.pulse_ms); + ESP_LOGI(TAG, "GPIO action: pin=%u, level=%d, pulse=%ums", + (unsigned int)action->gpio.pin, action->gpio.level, (unsigned int)action->gpio.pulse_ms); // 使用 raw GPIO 方式(直接操作物理引脚) ts_gpio_handle_t handle = ts_gpio_create_raw(action->gpio.pin, "automation"); diff --git a/components/ts_drivers/include/ts_device_ctrl.h b/components/ts_drivers/include/ts_device_ctrl.h index 501e3e5..e16f2ea 100644 --- a/components/ts_drivers/include/ts_device_ctrl.h +++ b/components/ts_drivers/include/ts_device_ctrl.h @@ -155,6 +155,19 @@ esp_err_t ts_device_reset(ts_device_id_t device); */ esp_err_t ts_device_enter_recovery(ts_device_id_t device); +/** + * @brief Toggle device power (LPMU only - pulse trigger) + * @param device Device ID + * @return ESP_OK on success, ESP_ERR_NOT_SUPPORTED for non-LPMU + */ +esp_err_t ts_device_power_toggle(ts_device_id_t device); + +/** + * @brief Start LPMU detection task (detect if LPMU is already running) + * @return ESP_OK on success + */ +esp_err_t ts_device_lpmu_start_detection(void); + /*===========================================================================*/ /* Status */ /*===========================================================================*/ diff --git a/components/ts_drivers/src/ts_device_ctrl.c b/components/ts_drivers/src/ts_device_ctrl.c index a93f800..81cb327 100644 --- a/components/ts_drivers/src/ts_device_ctrl.c +++ b/components/ts_drivers/src/ts_device_ctrl.c @@ -792,6 +792,9 @@ const char *ts_device_state_to_str(ts_device_state_t state) /* LPMU Network Detection */ /*===========================================================================*/ +// LPMU 默认 IP 地址 +#define LPMU_DEFAULT_IP "10.10.99.99" + /** * @brief Ping an IP address using ICMP * @param ip IP address string diff --git a/components/ts_drivers/src/ts_drivers.c b/components/ts_drivers/src/ts_drivers.c index 07b70c1..a815c08 100644 --- a/components/ts_drivers/src/ts_drivers.c +++ b/components/ts_drivers/src/ts_drivers.c @@ -111,6 +111,9 @@ esp_err_t ts_drivers_init(void) } else { TS_LOGI(TAG, "LPMU configured (pwr=%d, rst=%d)", GPIO_LPMU_POWER, GPIO_LPMU_RESET); + + // 启动 LPMU 自动检测任务(检测是否在线,不在线则自动开机) + ts_device_lpmu_start_detection(); } } #endif diff --git a/components/ts_led/src/ts_led_font.c b/components/ts_led/src/ts_led_font.c index 48aab86..73abf50 100644 --- a/components/ts_led/src/ts_led_font.c +++ b/components/ts_led/src/ts_led_font.c @@ -228,7 +228,7 @@ ts_font_t *ts_font_load(const char *path, const ts_font_config_t *config) font->cache = TS_CALLOC_PSRAM(cfg.cache_size, sizeof(ts_font_glyph_cache_t)); if (font->cache) { font->cache_size = cfg.cache_size; - ESP_LOGD(TAG, "Allocated cache for %d glyphs"); + ESP_LOGD(TAG, "Allocated cache for %u glyphs", (unsigned int)cfg.cache_size); } else { ESP_LOGW(TAG, "Failed to allocate glyph cache"); } diff --git a/components/ts_net/src/ts_net.c b/components/ts_net/src/ts_net.c index 59a300d..1a218b8 100644 --- a/components/ts_net/src/ts_net.c +++ b/components/ts_net/src/ts_net.c @@ -18,7 +18,7 @@ #define TAG "ts_net" static bool s_initialized = false; -static char s_hostname[32] = "tianshanOS"; +static char s_hostname[32] = "TianshanOS"; static char s_ip_str[16]; #ifdef CONFIG_TS_NET_MDNS_ENABLE diff --git a/components/ts_ota/src/ts_ota_rollback.c b/components/ts_ota/src/ts_ota_rollback.c index cb21494..e483ecc 100644 --- a/components/ts_ota/src/ts_ota_rollback.c +++ b/components/ts_ota/src/ts_ota_rollback.c @@ -167,7 +167,7 @@ esp_err_t ts_ota_save_update_time(void) ret = nvs_commit(handle); nvs_close(handle); - ESP_LOGI(TAG, "Update #%lu recorded at %ld", count, now); + ESP_LOGI(TAG, "Update #%u recorded at %lld", (unsigned int)count, (long long)now); return ret; } diff --git a/components/ts_ota/src/ts_ota_www.c b/components/ts_ota/src/ts_ota_www.c index 72419f8..9a124e0 100644 --- a/components/ts_ota/src/ts_ota_www.c +++ b/components/ts_ota/src/ts_ota_www.c @@ -296,10 +296,10 @@ static void www_ota_task(void *arg) size_t max_size = www_partition->size; if (chunked_mode) { - ESP_LOGW(TAG, "Content-Length not provided, using chunked mode (max %lu bytes)", max_size); + ESP_LOGW(TAG, "Content-Length not provided, using chunked mode (max %u bytes)", (unsigned int)max_size); content_length = 0; // Will be updated as we download } else if (content_length > (int)max_size) { - ESP_LOGE(TAG, "File too large: %d > %lu", content_length, max_size); + ESP_LOGE(TAG, "File too large: %d > %u", content_length, (unsigned int)max_size); www_ota_update_progress(TS_OTA_STATE_ERROR, 0, 0, "文件太大,超出分区容量"); goto cleanup; } diff --git a/components/ts_security/src/ts_ssh_log_watch.c b/components/ts_security/src/ts_ssh_log_watch.c index 4718822..9b5f666 100644 --- a/components/ts_security/src/ts_ssh_log_watch.c +++ b/components/ts_security/src/ts_ssh_log_watch.c @@ -242,7 +242,7 @@ static void log_watch_task(void *arg) { // 检查超时 uint32_t elapsed = (xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000 - task->start_time; if (elapsed >= task->config.timeout_sec) { - ESP_LOGW(TAG, "Log watch timeout for %s after %u seconds", + ESP_LOGW(TAG, "Log watch timeout for %s after %" PRIu32 " seconds", task->config.var_name, elapsed); update_status_var(task->config.var_name, "timeout"); break; @@ -274,11 +274,11 @@ static void log_watch_task(void *arg) { goto task_done; case -2: // NOTFOUND - ESP_LOGD(TAG, "Log file not found yet, waiting... (elapsed=%us)", elapsed); + ESP_LOGD(TAG, "Log file not found yet, waiting... (elapsed=%" PRIu32 "s)", elapsed); break; default: // WAITING - ESP_LOGI(TAG, "Waiting for pattern '%s' (elapsed=%us/%us)", + ESP_LOGI(TAG, "Waiting for pattern '%s' (elapsed=%" PRIu32 "s/%us)", task->config.ready_pattern, elapsed, task->config.timeout_sec); break; } @@ -532,7 +532,7 @@ void ts_ssh_log_watch_list(void) { while (task) { if (task->is_running) { uint32_t elapsed = (xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000 - task->start_time; - ESP_LOGI(TAG, " [%d] var=%s, pattern='%s', elapsed=%us/%us, log=%s", + ESP_LOGI(TAG, " [%d] var=%s, pattern='%s', elapsed=%" PRIu32 "s/%us, log=%s", ++count, task->config.var_name, task->config.ready_pattern, elapsed, task->config.timeout_sec, task->config.log_file); } diff --git a/components/ts_webui/web/css/style.css b/components/ts_webui/web/css/style.css index 85a270c..3056401 100644 --- a/components/ts_webui/web/css/style.css +++ b/components/ts_webui/web/css/style.css @@ -56,7 +56,10 @@ body { } .logo-icon { - font-size: 1.8rem; + width: 32px; + height: 32px; + object-fit: contain; + display: block; } .nav { @@ -66,7 +69,7 @@ body { } .nav-link { - color: var(--text-light); + color: #666; text-decoration: none; padding: 8px 12px; border-radius: 4px; @@ -74,8 +77,8 @@ body { } .nav-link:hover, .nav-link.active { - color: var(--primary-color); - background: rgba(52, 152, 219, 0.1); + color: #007bff; + background: #f0f8ff; } .user-menu { @@ -89,24 +92,19 @@ body { .main { flex: 1; margin-top: var(--header-height); - margin-bottom: var(--footer-height); padding: 20px; } /* Footer */ .footer { height: var(--footer-height); - background: var(--card-bg); - border-top: 1px solid var(--border-color); + background: transparent; + border-top: none; display: flex; align-items: center; justify-content: center; color: var(--text-light); font-size: 0.9rem; - position: fixed; - bottom: 0; - left: 0; - right: 0; } /* Cards */ @@ -114,7 +112,7 @@ body { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; - margin-top: 20px; + margin-top: 0; } .card { @@ -150,6 +148,11 @@ body { transition: width 0.3s; } +/* DRAM/PSRAM 进度条使用 btn-service-style 蓝色 */ +#heap-progress, #psram-progress { + background: #007bff; +} + /* Buttons */ .btn { padding: 8px 16px; @@ -172,8 +175,9 @@ body { } .btn-danger { - background: var(--danger-color); - color: white; + background: #ffebee; + border-color: #ef9a9a; + color: #c62828; } /* Modal */ @@ -874,15 +878,27 @@ body { } .toast-success { - background: var(--secondary-color); + background: #e8f5e9; + color: #2e7d32; + border: 1px solid #a5d6a7; } .toast-error { - background: var(--danger-color); + background: #ffebee; + color: #c62828; + border: 1px solid #ef9a9a; +} + +.toast-warning { + background: #fff8e1; + color: #f57c00; + border: 1px solid #ffd54f; } .toast-info { - background: var(--primary-color); + background: #f0f8ff; + color: #007bff; + border: 1px solid #d0e8ff; } /* 数据表格 */ @@ -2300,8 +2316,15 @@ body { background: #6b7280; } +.fan-mode-tab.manual { + border-radius: 4px; + border: 1px solid var(--border-color); +} + .fan-mode-tab.active.manual { - background: #f59e0b; + background: #fff8e1; + color: #f57c00; + border: 1px solid #ffd54f; } .fan-mode-tab.active.auto { @@ -2461,7 +2484,7 @@ body { } .curve-arrow { - color: var(--primary-color); + color: #333; font-weight: bold; } @@ -2647,7 +2670,7 @@ body { display: flex; justify-content: space-between; align-items: center; - padding: 10px 0; + padding-bottom: 10px; border-bottom: 1px solid var(--border-color); margin-bottom: 10px; } @@ -2801,6 +2824,20 @@ body { gap: 8px; } +/* 文件页面白色卡片容器 */ +.file-card { + background: var(--card-bg); + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +/* 文件操作按钮与登出按钮大小一致 */ +.btn-file-action { + padding: 4px 12px !important; + font-size: 0.85rem !important; +} + .storage-tabs { display: flex; gap: 5px; @@ -2813,29 +2850,69 @@ body { } .btn-success { - background-color: #22c55e; - border-color: #22c55e; - color: white; + background-color: #e8f5e9; + border-color: #a5d6a7; + color: #2e7d32; } .btn-success:hover { - background-color: #16a34a; - border-color: #16a34a; + background-color: #c8e6c9; + border-color: #81c784; } .btn-warning { - background-color: #f59e0b; - border-color: #f59e0b; - color: white; + background-color: #fff8e1; + border-color: #ffd54f; + color: #f57c00; } .btn-warning:hover { - background-color: #d97706; - border-color: #d97706; + background-color: #ffecb3; + border-color: #ffb74d; +} + +.tab-btn.btn-service-style { + padding: 4px 12px; + font-size: 0.85rem; + background: #f0f8ff; + color: #007bff; + border: 1px solid #d0e8ff; +} + +.tab-btn.btn-service-style:hover { + background: #e0f0ff; + border-color: #b0d8ff; +} + +.tab-btn.btn-service-style.active { + background: #007bff; + color: white; + border-color: #007bff; +} + +/* 灰色 Tab 按钮样式(SD卡、SPIFFS) */ +.tab-btn.btn-gray { + padding: 4px 12px; + font-size: 0.85rem; + background: #f5f6fa; + color: #666; + border: 1px solid #dcdfe6; +} + +.tab-btn.btn-gray:hover { + background: #ebeef5; + border-color: #c0c4cc; +} + +.tab-btn.btn-gray.active { + background: #666; + color: white; + border-color: #666; } .tab-btn { - padding: 8px 16px; + padding: 4px 12px; + font-size: 0.85rem; border: 1px solid var(--border-color); background: var(--card-bg); cursor: pointer; @@ -2876,16 +2953,15 @@ body { .file-table td { padding: 10px 15px; border-bottom: 1px solid var(--border-color); + vertical-align: middle; } -.file-row:hover { - background: rgba(52, 152, 219, 0.05); +.file-table tbody tr:last-child td { + border-bottom: none; } -.file-name { - display: flex; - align-items: center; - gap: 10px; +.file-row:hover { + background: rgba(52, 152, 219, 0.05); } .file-name.clickable { @@ -2897,23 +2973,22 @@ body { text-decoration: underline; } -.file-icon { - font-size: 1.2rem; -} - .file-size { color: var(--text-light); font-size: 0.9rem; + text-align: center; } .file-actions-cell { - display: flex; - gap: 5px; + white-space: nowrap; + text-align: center; } .file-actions-cell .btn-sm { padding: 4px 8px; font-size: 0.8rem; + display: inline-block; + margin: 0 2px; } .empty-folder { @@ -2959,7 +3034,11 @@ body { } .storage-info .mounted { - color: var(--secondary-color); + color: #2e7d32; + background: #e8f5e9; + padding: 2px 8px; + border-radius: 4px; + font-size: 0.85em; } .storage-info .unmounted { @@ -3354,8 +3433,10 @@ body { align-items: center; justify-content: space-between; padding: 12px 16px; + height: 48px; + box-sizing: border-box; border-bottom: 1px solid var(--border-color); - background: var(--bg-color); + background: #fff; } .panel-header h3 { @@ -3388,6 +3469,27 @@ body { color: white; } +/* 网络页面 Tab 按钮使用登录按钮样式 */ +.panel-tab.btn-service-style { + padding: 4px 12px; + font-size: 0.85rem; + background: #f0f8ff !important; + color: #007bff !important; + border: 1px solid #d0e8ff; + border-radius: 4px; +} + +.panel-tab.btn-service-style:hover { + background: #e0f0ff !important; + border-color: #b0d8ff; +} + +.panel-tab.btn-service-style.active { + background: #007bff !important; + color: white !important; + border-color: #007bff; +} + .panel-content { padding: 16px; } @@ -3442,7 +3544,7 @@ body { margin-right: 6px; } -.status-dot.green { background: var(--secondary-color); } +.status-dot.green { background: #2e7d32; } .status-dot.red { background: var(--danger-color); } .status-dot.gray { background: #999; } .status-dot.yellow { background: var(--warning-color); } @@ -3951,6 +4053,7 @@ body { width: 100%; border-collapse: collapse; font-size: 0.85rem; + color: #666; } .memory-table th, @@ -3963,7 +4066,7 @@ body { .memory-table th { background: var(--bg-color); font-weight: 600; - color: var(--text-light); + color: #666; font-size: 0.8rem; text-transform: uppercase; } @@ -4030,9 +4133,9 @@ body { .memory-static-sections { margin-top: 16px; padding: 12px 16px; - background: linear-gradient(135deg, #e8f4f8 0%, #f0f7fa 100%); + background: #f0f8ff; border-radius: 8px; - border: 1px solid #d0e8f0; + border: 1px solid #d0e8ff; } .static-grid { @@ -4081,9 +4184,9 @@ body { .memory-iram { margin-top: 16px; padding: 12px 16px; - background: linear-gradient(135deg, #fff3e0 0%, #fff8e1 100%); + background: #fff8e1; border-radius: 8px; - border: 1px solid #ffe0b2; + border: 1px solid #ffd54f; } .iram-grid { @@ -4111,16 +4214,16 @@ body { .iram-value { font-size: 1rem; font-weight: 600; - color: #e65100; + color: #f57c00; } /* RTC 内存 */ .memory-rtc { margin-top: 16px; padding: 12px 16px; - background: linear-gradient(135deg, #f3e5f5 0%, #fce4ec 100%); + background: #fff8e1; border-radius: 8px; - border: 1px solid #e1bee7; + border: 1px solid #ffd54f; } .rtc-labels { @@ -4556,13 +4659,15 @@ body { } .status-badge.status-running { - background: #d1fae5; - color: #059669; + background: #e8f5e9; + color: #2e7d32; + border: 1px solid #a5d6a7; } .status-badge.status-stopped { - background: #fee2e2; - color: #dc2626; + background: #ffebee; + color: #c62828; + border: 1px solid #ef9a9a; } /* 类型标签 */ @@ -6109,7 +6214,7 @@ body { display: flex; align-items: center; gap: 16px; - padding: 12px 16px; + padding: 12px 0; background: var(--bg-elevated); border-radius: 10px; margin-bottom: 16px; @@ -7250,6 +7355,48 @@ body { width: 100%; } +/* ========================================================================= + TianshanOS UI/UX Updates - RemixIcon & Unified Styles + ========================================================================= */ + +/* RemixIcon 图标样式 */ +.ri-question-line, +.ri-service-line, +.ri-restart-line, +.ri-download-cloud-line, +.ri-refresh-line, +.ri-time-line, +.ri-usb-line { + font-style: normal; + line-height: 1; + display: inline-block; + vertical-align: middle; +} + +/* 服务徽章统一样式 */ +.service-badge, +.btn-service-style { + background: #f0f8ff !important; + color: #007bff !important; + border-radius: 4px; + padding: 4px 12px; + border: 1px solid #d0e8ff; +} + +.btn-service-style:hover { + background: #e0f0ff !important; + border-color: #b0d8ff; +} + +/* 深灰色文本(用于服务状态) */ +.text-gray { + color: #666 !important; +} + +/* 健康状态图标颜色 */ +.health-ok { color: #27ae60; } +.health-fail { color: #e74c3c; } + /* ========================================================================= * SSH 指令 - 服务模式样式 * ========================================================================= */ @@ -7426,4 +7573,3 @@ body { .quick-action-card.has-service.is-running { /* 统一高度,不再额外增加 padding */ } - diff --git a/components/ts_webui/web/favicon.ico b/components/ts_webui/web/favicon.ico new file mode 100644 index 0000000..62bda9e Binary files /dev/null and b/components/ts_webui/web/favicon.ico differ diff --git a/components/ts_webui/web/fonts/remixicon.css b/components/ts_webui/web/fonts/remixicon.css new file mode 100644 index 0000000..9f3d671 --- /dev/null +++ b/components/ts_webui/web/fonts/remixicon.css @@ -0,0 +1,72 @@ +/* +* Remix Icon v4.9.1 (Minimal Build - TianshanOS) +* https://remixicon.com +* Only includes icons used in TianshanOS Web UI +*/ +@font-face { + font-family: "remixicon"; + src: url('/fonts/remixicon.woff2') format('woff2'); + font-display: swap; +} + +[class^="ri-"], [class*=" ri-"] { + font-family: 'remixicon' !important; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Icon sizes */ +.ri-lg { font-size: 1.3333em; line-height: 0.75em; vertical-align: -.0667em; } +.ri-xl { font-size: 1.5em; line-height: 0.6666em; vertical-align: -.075em; } +.ri-xxs { font-size: .5em; } +.ri-xs { font-size: .75em; } +.ri-sm { font-size: .875em } +.ri-1x { font-size: 1em; } +.ri-2x { font-size: 2em; } +.ri-fw { text-align: center; width: 1.25em; } + +/* TianshanOS Icons - Only what we use */ +.ri-download-cloud-line:before { content: "\ec58"; } +.ri-question-line:before { content: "\f045"; } +.ri-refresh-line:before { content: "\f064"; } +.ri-restart-line:before { content: "\f080"; } +.ri-service-line:before { content: "\f0e2"; } +.ri-time-line:before { content: "\f20f"; } +.ri-usb-line:before { content: "\f252"; } +.ri-user-line:before { content: "\f264"; } +.ri-key-line:before { content: "\eea5"; } +.ri-lock-line:before { content: "\eef1"; } +.ri-login-box-line:before { content: "\eef7"; } +.ri-shield-user-line:before { content: "\f0da"; } +.ri-admin-line:before { content: "\ee03"; } +.ri-check-line:before { content: "\eb7b"; } +.ri-close-line:before { content: "\eb98"; } +.ri-door-open-line:before { content: "\ec4c"; } +.ri-checkbox-blank-circle-fill:before { content: "\eb7c"; } +.ri-apps-line:before { content: "\ea44"; } +.ri-line-chart-line:before { content: "\eeab"; } +.ri-temp-hot-line:before { content: "\f1f4"; } +.ri-dashboard-3-line:before { content: "\ec12"; } +.ri-scan-line:before { content: "\f0bd"; } +.ri-box-3-line:before { content: "\f2f5"; } +.ri-delete-bin-line:before { content: "\ec2a"; } +.ri-add-line:before { content: "\ea13"; } +.ri-arrow-right-line:before { content: "\ea6c"; } +.ri-link-unlink:before { content: "\eeb1"; } +.ri-network-line:before { content: "\f65e"; } +.ri-signal-wifi-3-line:before { content: "\f130"; } +.ri-broadcast-line:before { content: "\eaf9"; } +.ri-edit-line:before { content: "\ec86"; } +.ri-download-line:before { content: "\ec5a"; } +.ri-upload-line:before { content: "\f250"; } +.ri-folder-add-line:before { content: "\ed5a"; } +.ri-folder-line:before { content: "\ed6a"; } +.ri-home-line:before { content: "\ee2b"; } +.ri-toggle-line:before { content: "\f219"; } +.ri-toggle-fill:before { content: "\f218"; } +.ri-eject-line:before { content: "\ec88"; } +.ri-lightbulb-line:before { content: "\eea9"; } +.ri-lightbulb-flash-line:before { content: "\eea8"; } +.ri-information-line:before { content: "\ee59"; } +.ri-question-answer-line:before { content: "\f04e"; } \ No newline at end of file diff --git a/components/ts_webui/web/fonts/remixicon.woff2 b/components/ts_webui/web/fonts/remixicon.woff2 new file mode 100644 index 0000000..5e86a2f Binary files /dev/null and b/components/ts_webui/web/fonts/remixicon.woff2 differ diff --git a/components/ts_webui/web/images/README.md b/components/ts_webui/web/images/README.md new file mode 100644 index 0000000..6bb5679 --- /dev/null +++ b/components/ts_webui/web/images/README.md @@ -0,0 +1,55 @@ +# TianshanOS Logo 资源 + +本目录包含 TianshanOS 的官方 logo 图片资源,以不同分辨率提供,用于不同场景。 + +## 文件说明 + +| 文件名 | 尺寸 | 用途 | 使用位置 | +|--------|------|------|----------| +| `tslogo-original.png` | 1024x1024 | 原始高清版本 | 设计源文件 | +| `tslogo-512.png` | 512x512 | 高分辨率版本 | 启动画面、关于页面 | +| `tslogo-256.png` | 256x256 | 中高分辨率 | 应用图标、大型展示 | +| `tslogo-128.png` | 128x128 | 标准分辨率 | 应用列表图标 | +| `tslogo-64.png` | 64x64 | 中等图标 | 工具栏、通知 | +| `tslogo-48.png` | 48x48 | Header 导航栏 | WebUI 顶部 Logo | +| `tslogo-32.png` | 32x32 | 小图标 | 按钮、标签页图标 | +| `favicon.ico` | 多尺寸 | 浏览器标签图标 | 浏览器 favicon | +| `favicon.icns` | 多尺寸 | macOS 图标格式 | 中间产物 | + +## 设计说明 + +Logo 采用渐变背景设计: +- **颜色渐变**:从左下角的青色 (#00BCD4) 渐变到右上角的紫色 (#9C27B0) +- **主体图形**:黑色山形轮廓,象征"天山"主题 +- **圆角设计**:柔和的圆角矩形背景,现代化风格 +- **高对比度**:黑色图形在渐变背景上清晰可辨 + +## 使用示例 + +### HTML +```html + +TianshanOS Logo + + + +``` + +### CSS +```css +.logo-icon { + width: 32px; + height: 32px; + object-fit: contain; + display: block; +} +``` + +## 更新日期 + +- 初始创建:2026-01-30 +- 替换原有 ⛰️ emoji 图标 + +## 版权信息 + +TianshanOS Logo © 2026 TianshanOS Project diff --git a/components/ts_webui/web/images/favicon.icns b/components/ts_webui/web/images/favicon.icns new file mode 100644 index 0000000..8f85f89 Binary files /dev/null and b/components/ts_webui/web/images/favicon.icns differ diff --git a/components/ts_webui/web/images/tslogo-128.png b/components/ts_webui/web/images/tslogo-128.png new file mode 100644 index 0000000..b071caa Binary files /dev/null and b/components/ts_webui/web/images/tslogo-128.png differ diff --git a/components/ts_webui/web/images/tslogo-256.png b/components/ts_webui/web/images/tslogo-256.png new file mode 100644 index 0000000..dce1b2b Binary files /dev/null and b/components/ts_webui/web/images/tslogo-256.png differ diff --git a/components/ts_webui/web/images/tslogo-32.png b/components/ts_webui/web/images/tslogo-32.png new file mode 100644 index 0000000..454c2a2 Binary files /dev/null and b/components/ts_webui/web/images/tslogo-32.png differ diff --git a/components/ts_webui/web/images/tslogo-48.png b/components/ts_webui/web/images/tslogo-48.png new file mode 100644 index 0000000..ad03b18 Binary files /dev/null and b/components/ts_webui/web/images/tslogo-48.png differ diff --git a/components/ts_webui/web/images/tslogo-512.png b/components/ts_webui/web/images/tslogo-512.png new file mode 100644 index 0000000..c7be743 Binary files /dev/null and b/components/ts_webui/web/images/tslogo-512.png differ diff --git a/components/ts_webui/web/images/tslogo-64.png b/components/ts_webui/web/images/tslogo-64.png new file mode 100644 index 0000000..46255e0 Binary files /dev/null and b/components/ts_webui/web/images/tslogo-64.png differ diff --git a/components/ts_webui/web/images/tslogo-original.png b/components/ts_webui/web/images/tslogo-original.png new file mode 100644 index 0000000..8d83289 Binary files /dev/null and b/components/ts_webui/web/images/tslogo-original.png differ diff --git a/components/ts_webui/web/index.html b/components/ts_webui/web/index.html index d57e83e..7adacf8 100644 --- a/components/ts_webui/web/index.html +++ b/components/ts_webui/web/index.html @@ -3,8 +3,9 @@ - TianShanOS + TianshanOS + @@ -15,8 +16,8 @@