Files
foundry-core/internal/platform/canvas_ai_config.go
T
codex-bot c9bb38454d
Foundry Go checks / check (push) Successful in 3m13s
Foundry Go checks / check (pull_request) Successful in 2m57s
Preserve relocation context for conflicted AI outputs
2026-11-08 01:20:06 +08:00

59 lines
1.5 KiB
Go

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. The board is the immutable plan origin, not the
// current canonical owner; canonical target identity is checked separately.
// 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)
}