using System.IO; using System.Reflection; using System.Text.Json; namespace AxCopilot.Services; /// /// 대화 주제 프리셋을 EmbeddedResource에서 동적으로 로드합니다. /// Assets/Presets/*.json 파일을 빌드 시 내장하고 런타임에 읽어옵니다. /// public static class PresetService { private static readonly List _presets = new(); private static bool _loaded; public static IReadOnlyList Presets { get { if (!_loaded) Load(); return _presets; } } public static TopicPreset? GetByCategory(string category) => Presets.FirstOrDefault(p => p.Category == category); /// 특정 탭의 프리셋만 반환. public static IReadOnlyList GetByTab(string tab) => Presets.Where(p => p.Tab.Equals(tab, StringComparison.OrdinalIgnoreCase)).ToList(); /// 내장 프리셋 + 커스텀 프리셋을 병합하여 탭별로 반환. public static IReadOnlyList GetByTabWithCustom(string tab, IEnumerable customPresets) { var result = GetByTab(tab).ToList(); foreach (var cp in customPresets.Where(c => c.Tab.Equals(tab, StringComparison.OrdinalIgnoreCase))) { result.Add(new TopicPreset { Category = $"custom_{cp.Id}", Label = cp.Label, Symbol = cp.Symbol, Color = cp.Color, Description = cp.Description, SystemPrompt = cp.SystemPrompt, Placeholder = "", Tab = cp.Tab, IsCustom = true, CustomId = cp.Id, }); } return result; } private static void Load() { _loaded = true; var assembly = Assembly.GetExecutingAssembly(); var prefix = "AxCopilot.Assets.Presets."; foreach (var name in assembly.GetManifestResourceNames()) { if (!name.StartsWith(prefix) || !name.EndsWith(".json")) continue; try { using var stream = assembly.GetManifestResourceStream(name); if (stream == null) continue; using var reader = new StreamReader(stream); var json = reader.ReadToEnd(); var preset = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }); if (preset != null) _presets.Add(preset); } catch (Exception ex) { LogService.Warn($"프리셋 로드 실패 ({name}): {ex.Message}"); } } // Order 필드 → ChatCategory 순서로 정렬 var catOrder = Models.ChatCategory.All.Select((c, i) => (c.Key, i)).ToDictionary(x => x.Key, x => x.i); _presets.Sort((a, b) => { var oa = a.Order; var ob = b.Order; if (oa != ob) return oa.CompareTo(ob); var ia = catOrder.GetValueOrDefault(a.Category, 99); var ib = catOrder.GetValueOrDefault(b.Category, 99); return ia.CompareTo(ib); }); LogService.Info($"대화 주제 프리셋 {_presets.Count}개 로드 완료"); } } /// 대화 주제 프리셋 모델. public class TopicPreset { public string Category { get; set; } = ""; public string Label { get; set; } = ""; public string Symbol { get; set; } = ""; public string Color { get; set; } = ""; public string Description { get; set; } = ""; public string SystemPrompt { get; set; } = ""; public string Placeholder { get; set; } = ""; /// 표시 순서 (낮을수록 먼저). 기본 50. public int Order { get; set; } = 50; /// 프리셋이 속하는 탭. "Chat" | "Cowork" | "Code" public string Tab { get; set; } = "Chat"; /// 사용자 커스텀 프리셋 여부. public bool IsCustom { get; set; } /// 커스텀 프리셋의 고유 ID (IsCustom == true일 때만 사용). public string? CustomId { get; set; } }