package display import ( "context" "html" "net/url" "sort" "strings" "time" core "dappco.re/go/core" coreerr "dappco.re/go/core/log" "forge.lthn.ai/core/gui/pkg/chat" "github.com/wailsapp/wails/v3/pkg/application" ) type SchemeHandler func(context.Context, string, url.Values) core.Result type assetMiddlewareHandler struct { next application.Handler service *Service } func (h assetMiddlewareHandler) ServeHTTP(w application.ResponseWriter, r *application.Request) { rawURL := r.URL if strings.HasPrefix(strings.ToLower(strings.TrimSpace(rawURL)), "core://") { result := h.service.ResolveScheme(context.Background(), rawURL) if !result.OK { w.WriteHeader(404) _, _ = w.Write([]byte("core route not found")) return } payload, _ := result.Value.(map[string]any) body, _ := payload["body"].(string) headers := w.Header() contentType, _ := payload["content_type"].(string) if strings.TrimSpace(contentType) == "" { contentType = "text/html" } headers["Content-Type"] = []string{contentType + "; charset=utf-8"} w.WriteHeader(200) _, _ = w.Write([]byte(body)) return } if h.next != nil { h.next.ServeHTTP(w, r) } } func (s *Service) HandleScheme(scheme string, handler SchemeHandler) { if s.schemeHandlers == nil { s.schemeHandlers = make(map[string]SchemeHandler) } s.schemeHandlers[strings.ToLower(strings.TrimSpace(scheme))] = handler } func (s *Service) registerDefaultSchemes() { s.HandleScheme("core", func(ctx context.Context, route string, query url.Values) core.Result { return s.resolveCoreRoute(ctx, route, query) }) } func (s *Service) resolveCoreRoute(ctx context.Context, route string, query url.Values) core.Result { segment, subpath := splitCoreRoute(route) if segment == "" { return core.Result{ Value: coreerr.E("display.resolveCoreRoute", "core route is required", nil), OK: false, } } switch segment { case "settings": return s.resolveSettingsRoute(subpath, query) case "store": return s.resolveStoreRoute(subpath, query) case "network": return s.resolveNetworkRoute(subpath, query) case "models": return s.resolveModelsRoute(subpath, query) case "chat": return s.resolveChatRoute(ctx, subpath, query) case "agent": return s.resolveServiceBackedCoreRoute("agent", subpath, query, "agent", "core-agent") case "wallet": return s.resolveServiceBackedCoreRoute("wallet", subpath, query, "wallet", "blockchain", "go-blockchain") case "identity": return s.resolveServiceBackedCoreRoute("identity", subpath, query, "identity", "tim", "TIM") default: return core.Result{ Value: coreerr.E("display.resolveCoreRoute", "unknown core route: "+segment, nil), OK: false, } } } func splitCoreRoute(route string) (string, string) { route = strings.Trim(strings.TrimSpace(route), "/") if route == "" { return "", "" } segment, remainder, found := strings.Cut(route, "/") if !found { return segment, "" } return segment, remainder } func (s *Service) resolveSettingsRoute(subpath string, query url.Values) core.Result { key := coalesce(query.Get("key"), subpath) snapshot := s.currentSettingsSnapshot() if key != "" { value, ok := s.currentSettingValue(key) if !ok { return s.resolveUnavailableCoreRoute("settings", subpath, query) } return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderKeyValuePage(coreRouteURL("settings", key), key, value, snapshot), "route": "settings", "key": key, "value": value, "settings": snapshot, }, OK: true, } } return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderSettingsPage(snapshot), "route": "settings", "settings": snapshot, }, OK: true, } } func (s *Service) resolveStoreRoute(subpath string, query url.Values) core.Result { if subpath != "" { parts := strings.Split(subpath, "/") if len(parts) >= 2 { bucket := strings.TrimSpace(parts[0]) key := strings.TrimSpace(strings.Join(parts[1:], "/")) if entry, ok := s.storage.Get("", bucket, key); ok { return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderStoreEntryPage(entry), "route": "store", "entry": entry, }, OK: true, } } } } return s.handleStoreSearch(context.Background(), query) } func (s *Service) resolveModelsRoute(subpath string, query url.Values) core.Result { if modelName := coalesce(query.Get("id"), subpath); modelName != "" { if model, ok := s.findChatModel(modelName); ok { return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderKeyValuePage(coreRouteURL("models", modelName), modelName, model, s.modelState()), "route": "models", "model": model, }, OK: true, } } return s.resolveUnavailableCoreRoute("models", subpath, query) } state := s.modelState() return core.Result{ Value: map[string]any{ "content_type": "application/json", "body": core.JSONMarshalString(state), "state": state, "models": s.chatModels(), "route": "models", }, OK: true, } } func (s *Service) resolveNetworkRoute(subpath string, query url.Values) core.Result { state := s.networkState() if interfaceName := coalesce(query.Get("name"), subpath); interfaceName != "" { for _, iface := range state.Interfaces { if strings.EqualFold(iface.Name, interfaceName) { return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderNetworkInterfacePage(state, iface), "route": "network", "interface": iface, "state": state, }, OK: true, } } } return s.resolveUnavailableCoreRoute("network", subpath, query) } return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderNetworkPage(state), "route": "network", "state": state, }, OK: true, } } func (s *Service) resolveChatRoute(_ context.Context, subpath string, query url.Values) core.Result { if id := coalesce(query.Get("conversation_id"), query.Get("id"), subpath); id != "" { return s.Core().QUERY(chat.QueryHistory{ConversationID: id}) } return s.Core().QUERY(chat.QueryConversationList{}) } func (s *Service) resolveUnavailableCoreRoute(route, subpath string, query url.Values) core.Result { return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderUnavailableRoute(route, subpath, query), "route": route, "subpath": subpath, "query": query, "available": false, }, OK: true, } } func (s *Service) resolveServiceBackedCoreRoute(route, subpath string, query url.Values, serviceNames ...string) core.Result { for _, serviceName := range serviceNames { serviceName = strings.TrimSpace(serviceName) if serviceName == "" { continue } serviceResult := s.Core().Service(serviceName) if !serviceResult.OK { continue } payload := map[string]any{ "route": route, "service": serviceName, "subpath": subpath, "query": query, "value": serviceResult.Value, "actions": s.actionsForService(serviceName), "services": s.Core().Services(), } return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderSchemeBody(route, payload), "route": route, "service": serviceName, "value": serviceResult.Value, }, OK: true, } } return s.resolveUnavailableCoreRoute(route, subpath, query) } func (s *Service) actionsForService(serviceName string) []string { if strings.TrimSpace(serviceName) == "" { return nil } prefixes := []string{ serviceName + ".", "core." + serviceName + ".", "gui." + serviceName + ".", } actions := make([]string, 0) for _, actionName := range s.Core().Actions() { for _, prefix := range prefixes { if strings.HasPrefix(actionName, prefix) { actions = append(actions, actionName) break } } } sort.Strings(actions) return actions } func (s *Service) currentSettingsSnapshot() map[string]any { if s.configFile != nil { var snapshot map[string]any if err := s.configFile.Get("", &snapshot); err == nil && snapshot != nil { snapshot["app_mode"] = string(s.mode) return snapshot } } snapshot := make(map[string]any, len(s.configData)) for key, value := range s.configData { if value == nil { continue } snapshot[key] = value } snapshot["app_mode"] = string(s.mode) return snapshot } func (s *Service) currentSettingValue(key string) (any, bool) { if s.configFile != nil { var value any if err := s.configFile.Get(key, &value); err == nil { return value, true } } for section, values := range s.configData { if strings.Contains(key, ".") { if nested, ok := values[key]; ok { return nested, true } } if section == key { return values, true } } return nil, false } func (s *Service) findChatModel(name string) (chat.ModelEntry, bool) { for _, model := range s.chatModels() { if strings.EqualFold(model.Name, name) { return model, true } } return chat.ModelEntry{}, false } func (s *Service) ResolveScheme(ctx context.Context, rawURL string) core.Result { if strings.TrimSpace(rawURL) == "" { return core.Result{Value: coreerr.E("display.ResolveScheme", "scheme URL is required", nil), OK: false} } parsed, err := url.Parse(rawURL) if err != nil { return core.Result{Value: err, OK: false} } handler, ok := s.schemeHandlers[strings.ToLower(parsed.Scheme)] if !ok { return core.Result{Value: coreerr.E("display.ResolveScheme", "no handler registered for scheme "+parsed.Scheme, nil), OK: false} } route := strings.Trim(strings.TrimPrefix(parsed.Host+parsed.Path, "/"), "/") resolved := handler(ctx, route, parsed.Query()) if !resolved.OK { return resolved } if payload, ok := resolved.Value.(map[string]any); ok { if contentType, _ := payload["content_type"].(string); strings.TrimSpace(contentType) != "" { if body, ok := payload["body"].(string); ok && strings.TrimSpace(body) != "" { return core.Result{Value: payload, OK: true} } if !strings.EqualFold(contentType, "text/html") { payload["body"] = core.JSONMarshalString(payload["state"]) return core.Result{Value: payload, OK: true} } } } body := s.renderSchemeBody(route, resolved.Value) return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": body, "route": route, "url": rawURL, }, OK: true, } } func (s *Service) renderSchemeBody(route string, value any) string { title := buildCoreURL(route, nil) pretty := core.JSONMarshalString(value) return "" + html.EscapeString(title) + "
" + html.EscapeString(title) + "
" +
		html.EscapeString(pretty) +
		"
" } func (s *Service) renderSettingsPage(settings map[string]any) string { safeSettings := core.JSONMarshalString(settings) return "core://settings
core://settings
Application settings and live config state.
" +
		html.EscapeString(safeSettings) +
		"
" } func (s *Service) renderKeyValuePage(title, key string, value any, snapshot any) string { return "" + html.EscapeString(title) + "
" + html.EscapeString(title) + "
Key: " + html.EscapeString(key) + "
" +
		html.EscapeString(core.JSONMarshalString(value)) +
		"
" +
		html.EscapeString(core.JSONMarshalString(snapshot)) +
		"
" } func (s *Service) renderStoreEntryPage(entry StorageEntry) string { return "core://store
core://store
Origin: " + anchorHTML(safeOriginHref(entry.Origin), entry.Origin) + "
Bucket: " + html.EscapeString(entry.Bucket) + "
Key: " + html.EscapeString(entry.Key) + "
Updated: " + html.EscapeString(entry.UpdatedAt.Format(time.RFC3339)) + "
" +
		html.EscapeString(entry.Value) +
		"
" } func (s *Service) renderUnavailableRoute(route, subpath string, query url.Values) string { body := map[string]any{ "available": false, "route": route, "subpath": subpath, "query": query, "reason": "no backend is registered for this route", } return s.renderSchemeBody(route, body) } func (s *Service) renderStoreSearchPage(query string, results []StorageEntry) string { safeQuery := html.EscapeString(query) type groupedResults struct { Origin string Entries []StorageEntry UpdatedAt time.Time } groupMap := make(map[string]*groupedResults) for _, item := range results { group := groupMap[item.Origin] if group == nil { group = &groupedResults{Origin: item.Origin} groupMap[item.Origin] = group } group.Entries = append(group.Entries, item) if item.UpdatedAt.After(group.UpdatedAt) { group.UpdatedAt = item.UpdatedAt } } groups := make([]groupedResults, 0, len(groupMap)) for _, group := range groupMap { groups = append(groups, *group) } sort.Slice(groups, func(i, j int) bool { return groups[i].UpdatedAt.After(groups[j].UpdatedAt) }) for i := range groups { sort.Slice(groups[i].Entries, func(a, b int) bool { return groups[i].Entries[a].UpdatedAt.After(groups[i].Entries[b].UpdatedAt) }) } var items strings.Builder if len(results) == 0 && strings.TrimSpace(query) != "" { items.WriteString("

No matches found in Core storage.

") } else if strings.TrimSpace(query) == "" { items.WriteString("

Enter a search term to scan Core storage namespaces.

") } else { for _, group := range groups { items.WriteString("
") items.WriteString(anchorHTML(safeOriginHref(group.Origin), group.Origin)) items.WriteString("
") } } return "core://store
core://store
Search the in-memory storage scopes exposed by the preload shim. Query: " + safeQuery + "
" + items.String() + "
" } func (s *Service) searchAllStorage(query string) []StorageEntry { results := s.storage.Search(query) if conversations := s.Core().QUERY(chat.QueryConversationSearch{Query: query}); conversations.OK { switch list := conversations.Value.(type) { case []any: for _, item := range list { results = append(results, StorageEntry{ Origin: "core://chat", Bucket: "conversation", Key: "summary", Value: core.JSONMarshalString(item), UpdatedAt: time.Now(), }) } default: if payload := core.JSONMarshalString(list); payload != "null" && payload != "" && payload != "[]" { results = append(results, StorageEntry{ Origin: "core://chat", Bucket: "conversation", Key: "summary", Value: payload, UpdatedAt: time.Now(), }) } } } return results } func (s *Service) handleStoreSearch(_ context.Context, params url.Values) core.Result { query := coalesce(params.Get("q"), params.Get("query")) results := s.searchAllStorage(query) return core.Result{ Value: map[string]any{ "content_type": "text/html", "body": s.renderStoreSearchPage(query, results), "route": "store", "url": buildCoreURL("store", nil), "query": params, "results": results, }, OK: true, } } func coalesce(values ...string) string { for _, value := range values { if strings.TrimSpace(value) != "" { return value } } return "" } func coreRouteURL(segment string, parts ...string) string { return buildCoreURL(pathForCoreRoute(segment, parts...), nil) } func buildCoreURL(route string, query url.Values) string { route = strings.Trim(strings.TrimSpace(route), "/") if route == "" { return "core://" } built := "core://" + route if encoded := sanitizeCoreQuery(query).Encode(); encoded != "" { built += "?" + encoded } return built } func pathForCoreRoute(segment string, parts ...string) string { route := strings.Trim(strings.TrimSpace(segment), "/") for _, part := range parts { if strings.TrimSpace(part) == "" { continue } route += "/" + url.PathEscape(strings.TrimSpace(part)) } return route } func sanitizeCoreQuery(query url.Values) url.Values { if len(query) == 0 { return nil } sanitized := make(url.Values, len(query)) for key, values := range query { key = strings.TrimSpace(key) if key == "" { continue } for _, value := range values { sanitized.Add(key, value) } } return sanitized } func safeOriginHref(origin string) string { trimmed := strings.TrimSpace(origin) if trimmed == "" { return "#" } parsed, err := url.Parse(trimmed) if err != nil { return "#" } switch strings.ToLower(parsed.Scheme) { case "http", "https", "file", "core": return parsed.String() default: return "#" } } func anchorHTML(href, text string) string { escapedHref := html.EscapeString(strings.TrimSpace(href)) if escapedHref == "" { escapedHref = "#" } return "" + html.EscapeString(text) + "" } func (s *Service) AssetMiddleware() application.Middleware { return func(next application.Handler) application.Handler { return assetMiddlewareHandler{service: s, next: next} } }