smlflg's picture
Initial public upload from Projekte/PromtGarageMac
4977db2 verified
Raw History Blame Contribute Delete
4.53 kB
import Foundation
public enum ImportProvider: String, CaseIterable, Codable, Identifiable, Sendable {
case claude
case codex
case hermes
case chatGPT
case gemini
public var id: String { rawValue }
public var displayName: String {
switch self {
case .claude: "Claude"
case .codex: "Codex"
case .hermes: "Hermes"
case .chatGPT: "ChatGPT"
case .gemini: "Gemini"
}
}
public var formats: [ImportFormat] {
ImportFormat.allCases.filter { $0.provider == self }
}
}
public enum ImportFormat: String, CaseIterable, Codable, Identifiable, Sendable {
case claudeJSONL
case codexHistoryJSONL
case hermesStateDatabase
case chatGPTAnalysisJSON
case chatGPTRawConversationsJSON
case geminiMarkdown
case geminiJSONL
public var id: String { rawValue }
public var provider: ImportProvider {
switch self {
case .claudeJSONL: .claude
case .codexHistoryJSONL: .codex
case .hermesStateDatabase: .hermes
case .chatGPTAnalysisJSON, .chatGPTRawConversationsJSON: .chatGPT
case .geminiMarkdown, .geminiJSONL: .gemini
}
}
public var displayName: String {
switch self {
case .claudeJSONL: "Claude Code JSONL"
case .codexHistoryJSONL: "Codex history.jsonl"
case .hermesStateDatabase: "Hermes state.db"
case .chatGPTAnalysisJSON: "ChatGPT Analysis JSON"
case .chatGPTRawConversationsJSON: "ChatGPT Conversations JSON"
case .geminiMarkdown: "Gemini Markdown"
case .geminiJSONL: "Gemini JSONL"
}
}
public var allowsDirectory: Bool {
switch self {
case .hermesStateDatabase: true
default: true
}
}
}
public struct ImportCandidate: Hashable, Sendable {
public let title: String
public let content: String
public let source: String
public let tags: String?
public let model: String?
public let promptedAt: String?
public init(
title: String,
content: String,
source: String,
tags: String?,
model: String?,
promptedAt: String? = nil
) {
self.title = title
self.content = content
self.source = source
self.tags = tags
self.model = model
self.promptedAt = promptedAt
}
}
public struct ImportStatistics: Codable, Equatable, Sendable {
public var scanned = 0
public var imported = 0
public var skippedShort = 0
public var skippedLong = 0
public var skippedDuplicate = 0
public var skippedAgent = 0
public var skippedSystem = 0
public var skippedMalformed = 0
public init() {}
}
public struct ImportPreview: Sendable {
public let format: ImportFormat
public let sourceURL: URL
public let candidates: [ImportCandidate]
public let statistics: ImportStatistics
public init(
format: ImportFormat,
sourceURL: URL,
candidates: [ImportCandidate],
statistics: ImportStatistics
) {
self.format = format
self.sourceURL = sourceURL
self.candidates = candidates
self.statistics = statistics
}
}
public struct ImportReport: Codable, Sendable {
public let provider: String
public let format: String
public let sourceName: String
public let completedAt: Date
public let statistics: ImportStatistics
public init(preview: ImportPreview, statistics: ImportStatistics, completedAt: Date = Date()) {
provider = preview.format.provider.rawValue
format = preview.format.rawValue
sourceName = preview.sourceURL.lastPathComponent
self.completedAt = completedAt
self.statistics = statistics
}
}
public struct DedupIndex: Sendable {
public var hashes: Set<String>
public let existingContents: [String]
public init(hashes: Set<String>, existingContents: [String]) {
// Legacy imports encode their 12-character content hash in `source`.
// Manual and other pre-native rows may not. Hash every exact stored
// content as well so preview and commit use the same dedup identity
// without rewriting any existing row.
self.hashes = hashes.union(existingContents.map {
ImportText.contentHash(ImportText.trimmed($0))
})
self.existingContents = existingContents
}
public func contains(_ hash: String) -> Bool {
hashes.contains(hash)
}
}