feat(#448): schema-driven config UI with category tabs, search, typed inputs

Closes #448

Replaced the raw YAML editor with a full schema-driven config management UI
matching the dashboard's ConfigPage experience.

### New API endpoints (HermesApiService)
- GET /api/config — load structured config
- GET /api/config/schema — load schema with field types + categories  
- GET /api/config/defaults — load defaults for reset
- PUT /api/config — save config (deep-merged on backend)

### Data models
- ConfigSchemaResponse — wraps fields + category_order
- SchemaField — type, description, category, options
- ConfigUpdateRequest — config map + optional profile

### ConfigViewModel
- Loads config + schema + defaults + path on init
- Tracks pending changes as dot-path → JsonElement map
- Builds nested JSON changeset for the save API
- YAML mode toggle with raw YAML load/save
- Per-category reset to defaults

### ConfigScreen
- Category tabs with field-count badges
- Search filtering across key, label, description, category
- Typed inputs: boolean toggles, select dropdowns, number inputs
- Modified highlighting with primaryContainer tint
- YAML ↔ Form mode toggle
- Sticky save indicator with pending-change count
This commit is contained in:
M57 authored and GitHub committed 2026-07-03 08:39:35 +03:00
1 parent d875c8a040
commit efb8e528ef
5 files changed
+962 -89

No files matched your search

@@ -1,8 +1,11 @@
package com.m57.hermescontrol.data.remote
import com.google.gson.JsonElement
import com.m57.hermescontrol.data.model.AchievementsResponse
import com.m57.hermescontrol.data.model.ActiveProfileResponse
import com.m57.hermescontrol.data.model.AgentPluginInstallBody
import com.m57.hermescontrol.data.model.ConfigSchemaResponse
import com.m57.hermescontrol.data.model.ConfigUpdateRequest
import com.m57.hermescontrol.data.model.CreateCronJobRequest
import com.m57.hermescontrol.data.model.CreateTaskBody
import com.m57.hermescontrol.data.model.CreateWebhookRequest
@@ -222,6 +225,20 @@ interface HermesApiService {
@Body body: UpdateRawConfigRequest,
): Response<Unit>
@GET("api/config")
suspend fun getConfig(): Response<Map<String, JsonElement>>
@GET("api/config/schema")
suspend fun getConfigSchema(): Response<ConfigSchemaResponse>
@GET("api/config/defaults")
suspend fun getConfigDefaults(): Response<Map<String, JsonElement>>
@PUT("api/config")
suspend fun updateConfig(
@Body body: ConfigUpdateRequest,
): Response<Unit>
@GET("api/mcp/servers")
suspend fun getMcpServers(): Response<McpServersResponse>