import Foundation /// How a takeaways section is shaped (drives the LLM output shape + rendering). enum SectionKind: String, Codable, CaseIterable, Identifiable { case items // point + optional who / timestamp / note (decisions, actions, quotes, questions…) case bullets // plain short points case paragraph // a prose block var id: String { rawValue } var label: String { switch self { case .items: return "Attributed items" case .bullets: return "Bulleted list" case .paragraph: return "Paragraph" } } } /// One configurable takeaways category in a template. struct TemplateSection: Codable, Identifiable, Equatable { var id: String var title: String var kind: SectionKind var instruction: String // editable prompt text: what the LLM should extract init(id: String = UUID().uuidString, title: String, kind: SectionKind, instruction: String) { self.id = id; self.title = title; self.kind = kind; self.instruction = instruction } } /// How finely to break the transcript into topic sections. enum TopicGranularity: String, Codable, CaseIterable, Identifiable { case coarse, auto, fine var id: String { rawValue } var label: String { switch self { case .coarse: return "Fewer, broader topics"; case .auto: return "Automatic"; case .fine: return "More, finer topics" } } /// Multiplier applied to the auto target-section count. var multiplier: Double { switch self { case .coarse: return 0.6; case .auto: return 1.0; case .fine: return 1.7 } } } /// A recap template: the always-on TLDR + an ordered list of takeaways sections. /// Fully user-editable in Settings (titles, types, instructions); the analyzer builds /// the LLM prompt from it and the renderer renders from it, so categories are data, /// not code. struct RecapTemplate: Codable, Identifiable, Equatable { var id: String var name: String var includeTLDR: Bool var topicGranularity: TopicGranularity var sections: [TemplateSection] init(id: String = UUID().uuidString, name: String, includeTLDR: Bool = true, topicGranularity: TopicGranularity = .auto, sections: [TemplateSection]) { self.id = id; self.name = name; self.includeTLDR = includeTLDR self.topicGranularity = topicGranularity; self.sections = sections } // MARK: - Built-in defaults (seeded once; all editable thereafter) static var builtIns: [RecapTemplate] { [internalMeeting, oneOnOne, companyCall] } static var internalMeeting: RecapTemplate { RecapTemplate(id: "builtin.internal", name: "Internal Meeting", sections: [ .init(id: "internal.decisions", title: "Decisions", kind: .items, instruction: "Things explicitly decided or agreed. Only clear commitments (\"we'll do X\", \"let's go with Y\"), not casual mentions. text = the decision; who = who agreed; when = the timestamp."), .init(id: "internal.actions", title: "Action Items", kind: .items, instruction: "Explicit ownership (\"I'll send the doc\", \"Matt will follow up\"), not vague \"someone should\". text = the action in imperative form; who = the owner; note = a due date if mentioned; when = the timestamp."), .init(id: "internal.questions", title: "Open Questions", kind: .items, instruction: "Questions raised that were NOT clearly answered. Skip rhetorical or answered ones. text = the question, self-contained; who = who raised it."), .init(id: "internal.quotes", title: "Key Quotes", kind: .items, instruction: "3-6 pivotal or insightful statements worth surfacing verbatim. text = the quote (4-30 words); who = the speaker; when = the timestamp; note = why it's notable."), ]) } static var oneOnOne: RecapTemplate { RecapTemplate(id: "builtin.oneonone", name: "1:1", sections: [ .init(id: "1on1.takeaways", title: "Key Takeaways", kind: .bullets, instruction: "The main points and conclusions from the conversation, as concise bullets."), .init(id: "1on1.actions", title: "Action Items", kind: .items, instruction: "Anything either person committed to do. text = the action; who = the owner; note = a due date if mentioned."), .init(id: "1on1.followups", title: "Follow-ups", kind: .bullets, instruction: "Things to revisit or circle back on next time."), ]) } static var companyCall: RecapTemplate { RecapTemplate(id: "builtin.company", name: "Company / Sales Call", sections: [ .init(id: "company.takeaways", title: "Key Takeaways", kind: .bullets, instruction: "The most important points from the call, as concise bullets."), .init(id: "company.needs", title: "Their Asks & Needs", kind: .bullets, instruction: "What the other party wants, needs, or is trying to solve."), .init(id: "company.objections", title: "Objections & Concerns", kind: .items, instruction: "Concerns, hesitations, or objections raised. text = the concern; who = who raised it."), .init(id: "company.next", title: "Next Steps", kind: .items, instruction: "Agreed next steps. text = the step; who = the owner; note = timing if mentioned; when = the timestamp."), ]) } }