package platform import ( "encoding/json" "strings" ) func validCanvasAI(value any, kind string) bool { if !canvasChoice(kind, "text", "script", "plain-text", "image", "plain-image") { return false } ai, ok := value.(map[string]any) if !ok || !canvasFields(ai, "enabled", "modelId", "prompt", "propagate", "parameters") { return false } for key, value := range ai { switch key { case "enabled", "propagate": if _, ok := value.(bool); !ok { return false } case "modelId": id, ok := value.(string) if !ok || len(id) == 0 || len(id) > 256 || strings.TrimSpace(id) != id { return false } case "prompt": if !canvasString(value, 16000) { return false } case "parameters": params, ok := value.(map[string]any) if !ok { return false } raw, err := json.Marshal(params) if err != nil || len(raw) > 16<<10 { return false } } } return true } // Fingerprints exclude geometry and visual styles so a result can be applied // after a concurrent drag. Content/configuration edits invalidate the result. func canvasTargetFingerprint(board, node string, data map[string]any) string { target := map[string]any{"board_id": board, "node_id": node, "kind": data["kind"], "ai": data["ai"]} if canvasChoice(data["kind"], "image", "plain-image") { target["src"] = data["src"] target["assetId"] = data["assetId"] } else { target["text"] = data["text"] } return payloadDigest(target) }