OCR desde imagen del portapapeles + botón copiar diagnóstico iCloud (build 61)

- Quick Update: checkClipboard ahora detecta también una IMAGEN copiada
  (pb.hasImages) y hace OCR automático — te ofrece pegar el importe detectado
  sin pulsar 'Escanear'. Gated por changeCount. El 'Pegar €X' del teclado
  también usa el importe OCR'd de la imagen.
- Settings iCloud: botón 'Copiar diagnóstico de iCloud' SIEMPRE visible (el
  desplegable de detalle solo salía con un error activo, difícil de encontrar).
  Copia versión/build, contadores locales, últimas fechas import/export,
  criticidad, hint y el error completo con códigos internos. Strings ×7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
alexandrev-tibco
2026-07-10 21:52:53 +02:00
parent 6bdf2740c5
commit 7c6a29c821
10 changed files with 79 additions and 23 deletions
@@ -464,6 +464,18 @@ struct SettingsView: View {
}
}
// Always-available diagnostics: copies status + last error (with
// the inner CloudKit codes) so it can be shared for support.
Button {
UIPasteboard.general.string = iCloudDiagnostics()
forceUploadResult = String(localized: "icloud_diagnostics_copied")
DispatchQueue.main.asyncAfter(deadline: .now() + 4) { forceUploadResult = nil }
} label: {
Label(String(localized: "icloud_copy_diagnostics"), systemImage: "doc.on.doc")
.font(.caption)
.foregroundColor(.appPrimary)
}
// Force upload use when data exists locally but hasn't reached iCloud
if cloudStack.localSourceCount > 0 {
if let result = forceUploadResult {
@@ -874,6 +886,20 @@ struct SettingsView: View {
formatter.countStyle = .file
return formatter.string(fromByteCount: bytes)
}
/// Copyable iCloud diagnostics snapshot for support/debugging.
private func iCloudDiagnostics() -> String {
let df = ISO8601DateFormatter()
var lines: [String] = ["Portfolio Journal — iCloud diagnostics"]
lines.append("app: \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "?"))")
lines.append("local: \(cloudStack.localSourceCount) sources · \(cloudStack.localSnapshotCount) snapshots")
lines.append("lastImport: \(cloudStack.lastImportDate.map { df.string(from: $0) } ?? "never")")
lines.append("lastExport: \(cloudStack.lastExportDate.map { df.string(from: $0) } ?? "never")")
lines.append("critical: \(cloudStack.syncErrorIsCritical)")
lines.append("hint: \(cloudStack.lastSyncErrorHint ?? "")")
lines.append("error: \(cloudStack.lastSyncError ?? "none this session")")
return lines.joined(separator: "\n")
}
}
// MARK: - Activity View