feat(ui): Phase 8 UI/UX optimization and system documentation update

## Sidebar Enhancement
- Change tabs to icon + small label layout for better space utilization
- Add Teams tab with team collaboration entry point

## Settings Page Improvements
- Connect theme toggle to gatewayStore.saveQuickConfig for persistence
- Remove OpenFang backend download section, simplify UI
- Add time range filter to UsageStats (7d/30d/all)
- Add stat cards with icons (sessions, messages, input/output tokens)
- Add token usage overview bar chart
- Add 8 ZCLAW system skill definitions with categories

## Bug Fixes
- Fix ChannelList duplicate content with deduplication logic
- Integrate CreateTriggerModal in TriggersPanel
- Add independent SecurityStatusPanel with 12 default enabled layers
- Change workflow view to use SchedulerPanel as unified entry

## New Components
- CreateTriggerModal: Event trigger creation modal
- HandApprovalModal: Hand approval workflow dialog
- HandParamsForm: Enhanced Hand parameter form
- SecurityLayersPanel: 16-layer security status display

## Architecture
- Add TOML config parsing support (toml-utils.ts, config-parser.ts)
- Add request timeout and retry mechanism (request-helper.ts)
- Add secure token storage (secure-storage.ts, secure_storage.rs)

## Tests
- Add unit tests for config-parser, toml-utils, request-helper
- Add team-client and teamStore tests

## Documentation
- Update SYSTEM_ANALYSIS.md with Phase 8 completion
- UI completion: 100% (30/30 components)
- API coverage: 93% (63/68 endpoints)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-15 14:12:11 +08:00
parent bf79c06d4a
commit 3e81bd3e50
30 changed files with 8875 additions and 284 deletions

View File

@@ -0,0 +1,99 @@
// Secure storage module for ZCLAW desktop app
// Uses the OS keyring/keychain for secure credential storage
// - Windows: DPAPI
// - macOS: Keychain
// - Linux: Secret Service API (gnome-keyring, kwallet, etc.)
use keyring::Entry;
const SERVICE_NAME: &str = "zclaw";
/// Store a value securely in the OS keyring
#[tauri::command]
pub fn secure_store_set(key: String, value: String) -> Result<(), String> {
let entry = Entry::new(SERVICE_NAME, &key).map_err(|e| {
format!(
"Failed to create keyring entry for '{}': {}",
key,
e.to_string()
)
})?;
entry.set_password(&value).map_err(|e| {
format!(
"Failed to store value for key '{}': {}",
key,
e.to_string()
)
})?;
Ok(())
}
/// Retrieve a value from the OS keyring
#[tauri::command]
pub fn secure_store_get(key: String) -> Result<String, String> {
let entry = Entry::new(SERVICE_NAME, &key).map_err(|e| {
format!(
"Failed to create keyring entry for '{}': {}",
key,
e.to_string()
)
})?;
entry.get_password().map_err(|e| {
// Return empty string for "not found" errors to distinguish from actual errors
let error_str = e.to_string();
if error_str.contains("No matching entry") || error_str.contains("not found") {
String::new()
} else {
format!("Failed to retrieve value for key '{}': {}", key, error_str)
}
})
}
/// Delete a value from the OS keyring
#[tauri::command]
pub fn secure_store_delete(key: String) -> Result<(), String> {
let entry = Entry::new(SERVICE_NAME, &key).map_err(|e| {
format!(
"Failed to create keyring entry for '{}': {}",
key,
e.to_string()
)
})?;
match entry.delete_credential() {
Ok(()) => Ok(()),
Err(e) => {
let error_str = e.to_string();
// Don't fail if the entry doesn't exist
if error_str.contains("No matching entry") || error_str.contains("not found") {
Ok(())
} else {
Err(format!("Failed to delete value for key '{}': {}", key, error_str))
}
}
}
}
/// Check if secure storage is available on this platform
#[tauri::command]
pub fn secure_store_is_available() -> bool {
// Try to create a test entry to verify keyring is working
let test_key = "__zclaw_availability_test__";
match Entry::new(SERVICE_NAME, test_key) {
Ok(entry) => {
// Try to set and delete a test value
match entry.set_password("test") {
Ok(_) => {
// Clean up the test entry
let _ = entry.delete_credential();
true
}
Err(_) => false,
}
}
Err(_) => false,
}
}