package main import "fmt" type InboxNodeDTO struct { NodeDTO CaptureKind string `json:"captureKind"` CaptureSource string `json:"captureSource"` CaptureStatus string `json:"captureStatus"` CaptureContextType string `json:"captureContextType"` CaptureContextNodeID string `json:"captureContextNodeId"` CaptureContextSection string `json:"captureContextSection"` SuggestedTargetNodeID string `json:"suggestedTargetNodeId"` CaptureContextLabel string `json:"captureContextLabel"` SuggestedTargetLabel string `json:"suggestedTargetLabel"` CapturedAt string `json:"capturedAt"` SourceKind string `json:"sourceKind"` URL string `json:"url,omitempty"` Hostname string `json:"hostname,omitempty"` } func (a *App) ListInboxNodes() ([]InboxNodeDTO, error) { if err := a.requireVault(); err != nil { return nil, err } list, err := a.nodes.ListInboxRoots(false) if err != nil { return nil, err } dtos := make([]InboxNodeDTO, 0, len(list)) for _, n := range list { dto, err := a.inboxNodeDTO(&n) if err != nil { return nil, err } dtos = append(dtos, *dto) } for i := range dtos { n, err := a.nodes.CountChildren(dtos[i].ID, "case", "client", "project", "folder", "document", "recipe") if err != nil { return nil, err } dtos[i].HasChildren = n > 0 } return dtos, nil } func (a *App) ListInboxNodesForTarget(nodeID string) ([]InboxNodeDTO, error) { if err := a.requireVault(); err != nil { return nil, err } if nodeID == "" { return []InboxNodeDTO{}, nil } list, err := a.ListInboxNodes() if err != nil { return nil, err } out := make([]InboxNodeDTO, 0, len(list)) for _, item := range list { if item.CaptureContextNodeID == nodeID || item.SuggestedTargetNodeID == nodeID { out = append(out, item) } } return out, nil } func (a *App) AssignInboxNode(nodeID, targetParentID string) (*NodeDTO, error) { return a.ResolveInboxNode(nodeID, targetParentID) } func (a *App) ResolveInboxNodeHere(nodeID string) (*NodeDTO, error) { if err := a.requireVault(); err != nil { return nil, err } dto, err := a.inboxNodeByID(nodeID) if err != nil { return nil, err } if dto.SuggestedTargetNodeID == "" { return nil, fmt.Errorf("suggested target is required") } return a.ResolveInboxNode(nodeID, dto.SuggestedTargetNodeID) } func (a *App) ResolveInboxNode(nodeID, targetParentID string) (*NodeDTO, error) { if err := a.requireVault(); err != nil { return nil, err } if !a.isInboxCaptureNode(nodeID) { return nil, fmt.Errorf("node is not an inbox artifact") } if targetParentID == "" { return nil, fmt.Errorf("target parent is required") } sourceKind := a.captureMeta(nodeID, "capture.source_kind") if sourceKind == "" { sourceKind = a.captureMeta(nodeID, "capture.kind") } if sourceKind == "url" { if err := a.resolveURLInboxNode(nodeID, targetParentID); err != nil { return nil, err } return a.GetNodeDetail(targetParentID) } if err := a.MoveNode(nodeID, targetParentID); err != nil { return nil, err } if err := a.clearCaptureMeta(nodeID); err != nil { return nil, err } dto, err := a.GetNodeDetail(nodeID) if err != nil { return nil, err } return dto, nil } func (a *App) inboxNodeByID(nodeID string) (*InboxNodeDTO, error) { n, err := a.nodes.GetActive(nodeID) if err != nil { return nil, err } return a.inboxNodeDTO(n) } func (a *App) resolveURLInboxNode(nodeID, targetParentID string) error { if _, err := a.nodes.GetActive(targetParentID); err != nil { return fmt.Errorf("target parent not found: %w", err) } rawURL := a.captureMeta(nodeID, "capture.url") if rawURL == "" { return fmt.Errorf("captured url is missing") } title := a.captureMeta(nodeID, "capture.title") if title == "" { if n, err := a.nodes.GetActive(nodeID); err == nil { title = n.Title } } source := a.captureMeta(nodeID, "capture.source") capturedAt := a.captureMeta(nodeID, "capture.created_at") if _, err := a.createResolvedLink(targetParentID, rawURL, title, "", source, capturedAt); err != nil { return err } if err := a.DeleteNode(nodeID); err != nil { return err } return a.clearCaptureMeta(nodeID) } func (a *App) DeleteInboxNode(nodeID string) error { if err := a.requireVault(); err != nil { return err } if !a.isInboxCaptureNode(nodeID) { return fmt.Errorf("node is not an inbox artifact") } if err := a.DeleteNode(nodeID); err != nil { return err } return a.clearCaptureMeta(nodeID) } func (a *App) filterInboxCaptureNodes(list []NodeDTO) []NodeDTO { out := make([]NodeDTO, 0, len(list)) for _, item := range list { if !a.isInboxCaptureNode(item.ID) { out = append(out, item) } } return out } func (a *App) isInboxCaptureNode(nodeID string) bool { v, ok, err := a.nodes.MetaGet(nodeID, "capture.inbox") if err != nil || !ok || v != "true" { return false } status, ok, err := a.nodes.MetaGet(nodeID, "capture.status") if err != nil { return false } return !ok || status == "" || status == "unresolved" } func (a *App) clearCaptureMeta(nodeID string) error { _, err := a.db.Exec(`DELETE FROM node_meta WHERE node_id = ? AND key LIKE 'capture.%'`, nodeID) return err } func (a *App) captureMeta(nodeID, key string) string { v, ok, err := a.nodes.MetaGet(nodeID, key) if err != nil || !ok { return "" } return v }