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:
186
desktop/src/lib/toml-utils.ts
Normal file
186
desktop/src/lib/toml-utils.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* TOML Utility Functions
|
||||
*
|
||||
* Provides TOML parsing and serialization capabilities for OpenFang configuration files.
|
||||
* Supports environment variable interpolation in the format ${VAR_NAME}.
|
||||
*
|
||||
* @module toml-utils
|
||||
*/
|
||||
|
||||
import TOML from 'smol-toml';
|
||||
|
||||
/**
|
||||
* Error class for TOML parsing errors
|
||||
*/
|
||||
export class TomlParseError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly cause?: unknown
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'TomlParseError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class for TOML serialization errors
|
||||
*/
|
||||
export class TomlStringifyError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly cause?: unknown
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'TomlStringifyError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TOML utility functions for parsing and serializing configuration files
|
||||
*/
|
||||
export const tomlUtils = {
|
||||
/**
|
||||
* Parse a TOML string into a JavaScript object
|
||||
*
|
||||
* @param content - The TOML string to parse
|
||||
* @returns The parsed JavaScript object
|
||||
* @throws TomlParseError if the TOML content is invalid
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const config = tomlUtils.parse(`
|
||||
* [server]
|
||||
* host = "127.0.0.1"
|
||||
* port = 4200
|
||||
* `);
|
||||
* // config = { server: { host: "127.0.0.1", port: 4200 } }
|
||||
* ```
|
||||
*/
|
||||
parse: <T = Record<string, unknown>>(content: string): T => {
|
||||
try {
|
||||
return TOML.parse(content) as T;
|
||||
} catch (error) {
|
||||
console.error('[TOML] Parse error:', error);
|
||||
throw new TomlParseError(
|
||||
`TOML parse error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
error
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize a JavaScript object to a TOML string
|
||||
*
|
||||
* @param data - The JavaScript object to serialize
|
||||
* @returns The TOML string representation
|
||||
* @throws TomlStringifyError if the object cannot be serialized to TOML
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const toml = tomlUtils.stringify({
|
||||
* server: { host: "127.0.0.1", port: 4200 }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
stringify: (data: Record<string, unknown>): string => {
|
||||
try {
|
||||
return TOML.stringify(data);
|
||||
} catch (error) {
|
||||
console.error('[TOML] Stringify error:', error);
|
||||
throw new TomlStringifyError(
|
||||
`TOML stringify error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
error
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolve environment variables in TOML content
|
||||
*
|
||||
* Replaces ${VAR_NAME} patterns with the corresponding environment variable values.
|
||||
* If the environment variable is not set, it's replaced with an empty string.
|
||||
*
|
||||
* Note: In browser/Tauri context, this function has limited access to environment
|
||||
* variables. For full resolution, use the Tauri backend to read env vars.
|
||||
*
|
||||
* @param content - The TOML content with potential ${VAR_NAME} patterns
|
||||
* @param envVars - Optional object containing environment variables (for testing or Tauri-provided values)
|
||||
* @returns The content with environment variables resolved
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const content = 'api_key = "${OPENAI_API_KEY}"';
|
||||
* const resolved = tomlUtils.resolveEnvVars(content, { OPENAI_API_KEY: 'sk-...' });
|
||||
* // resolved = 'api_key = "sk-..."'
|
||||
* ```
|
||||
*/
|
||||
resolveEnvVars: (
|
||||
content: string,
|
||||
envVars?: Record<string, string | undefined>
|
||||
): string => {
|
||||
return content.replace(/\$\{([^}]+)\}/g, (_, varName: string) => {
|
||||
// If envVars provided, use them; otherwise try to access from window or return empty
|
||||
if (envVars) {
|
||||
return envVars[varName] ?? '';
|
||||
}
|
||||
|
||||
// In browser context, we can't access process.env directly
|
||||
// This will be handled by passing envVars from Tauri backend
|
||||
console.warn(
|
||||
`[TOML] Environment variable ${varName} not resolved - no envVars provided`
|
||||
);
|
||||
return '';
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse TOML content with environment variable resolution
|
||||
*
|
||||
* Convenience method that combines resolveEnvVars and parse.
|
||||
*
|
||||
* @param content - The TOML content with potential ${VAR_NAME} patterns
|
||||
* @param envVars - Optional object containing environment variables
|
||||
* @returns The parsed and resolved JavaScript object
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const config = tomlUtils.parseWithEnvVars(tomlContent, {
|
||||
* ZHIPU_API_KEY: 'your-api-key'
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
parseWithEnvVars: <T = Record<string, unknown>>(
|
||||
content: string,
|
||||
envVars?: Record<string, string | undefined>
|
||||
): T => {
|
||||
const resolved = tomlUtils.resolveEnvVars(content, envVars);
|
||||
return tomlUtils.parse<T>(resolved);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a string contains unresolved environment variable placeholders
|
||||
*
|
||||
* @param content - The content to check
|
||||
* @returns true if there are unresolved ${VAR_NAME} patterns
|
||||
*/
|
||||
hasUnresolvedEnvVars: (content: string): boolean => {
|
||||
return /\$\{[^}]+\}/.test(content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract environment variable names from TOML content
|
||||
*
|
||||
* @param content - The TOML content to scan
|
||||
* @returns Array of environment variable names found
|
||||
*/
|
||||
extractEnvVarNames: (content: string): string[] => {
|
||||
const matches = content.matchAll(/\$\{([^}]+)\}/g);
|
||||
const names = new Set<string>();
|
||||
for (const match of matches) {
|
||||
names.add(match[1]);
|
||||
}
|
||||
return Array.from(names);
|
||||
},
|
||||
};
|
||||
|
||||
export default tomlUtils;
|
||||
Reference in New Issue
Block a user