package matrix import "strings" // SelectKnowledgeItems prefers explicit selections, otherwise falls back to all items. func SelectKnowledgeItems(selected, all []string) []string { source := selected if len(source) == 0 { source = all } return dedupeKnowledgeLines(source) } // MergeKnowledgeItems combines explicit user selections without falling back to unselected items. func MergeKnowledgeItems(parts ...[]string) []string { merged := make([]string, 0) for _, part := range parts { merged = append(merged, part...) } return dedupeKnowledgeLines(merged) } func dedupeKnowledgeLines(lines []string) []string { out := make([]string, 0, len(lines)) seen := map[string]struct{}{} for _, item := range lines { item = strings.TrimSpace(item) if item == "" { continue } key := strings.ToLower(item) if _, ok := seen[key]; ok { continue } seen[key] = struct{}{} out = append(out, item) } return out } // FormatCopyResearchMapBlock renders a compact research-map block for LLM prompts. func FormatCopyResearchMapBlock(audience, goal string, questions, pillars, exclusions []string, knowledgeItems ...[]string) string { var b strings.Builder if audience = strings.TrimSpace(audience); audience != "" { b.WriteString("受眾:") b.WriteString(audience) b.WriteString("\n") } if goal = strings.TrimSpace(goal); goal != "" { b.WriteString("內容目標:") b.WriteString(goal) b.WriteString("\n") } if len(pillars) > 0 { b.WriteString("支柱:") b.WriteString(strings.Join(pillars, "、")) b.WriteString("\n") } if len(questions) > 0 { b.WriteString("受眾問題:") b.WriteString(strings.Join(questions, ";")) b.WriteString("\n") } if len(knowledgeItems) > 0 && len(knowledgeItems[0]) > 0 { b.WriteString("勾選延伸知識:") b.WriteString(strings.Join(knowledgeItems[0], ";")) b.WriteString("\n") } if len(exclusions) > 0 { b.WriteString("排除:") b.WriteString(strings.Join(exclusions, ";")) } return strings.TrimSpace(b.String()) }