iOS: export through the share sheet !118
merged
merged by cmc on 2026-10-07 00:25 UTC
· krz/orgstar:ios-export into main
3 files changed, +63 −0
Layout: unified · split
Sources/OrgstarMobile/EditorScreen.swift
+1
| @@ -53,6 +53,7 @@ struct EditorScreen: View { |
| 53 | 53 | Menu("More", systemImage: "ellipsis.circle") { |
| 54 | 54 | Button("Commands", systemImage: "command") { sheet = .palette } |
| 55 | 55 | Button("Clock In", systemImage: "stopwatch") { editor?.run("app.clock.in") } |
| 56 | if let text = session.document?.text, session.path == path { ExportMenu(path: path, text: text) } |
| 56 | 57 | Button("Sync Conflict Copies", systemImage: "doc.on.doc") { sheet = .syncCopies } |
| 57 | 58 | Button("Recovered Versions", systemImage: "clock.arrow.circlepath") { sheet = .recovery } |
| 58 | 59 | } |
Sources/OrgstarMobile/ExportMenu.swift
added
+61
| @@ -0,0 +1,61 @@ |
| 1 | #if os(iOS) |
| 2 | import CoreTransferable |
| 3 | import OrgApp |
| 4 | import OrgCore |
| 5 | import SwiftUI |
| 6 | import UniformTypeIdentifiers |
| 7 | |
| 8 | /// A file exported as HTML or Markdown, written when the share sheet takes it. |
| 9 | struct OrgExport: Transferable { |
| 10 | enum Format { case html, markdown } |
| 11 | |
| 12 | let text: String |
| 13 | let path: String |
| 14 | let format: Format |
| 15 | let settings: OrgSettings |
| 16 | |
| 17 | var fileName: String { |
| 18 | let base = ((path as NSString).lastPathComponent as NSString).deletingPathExtension |
| 19 | return base + (format == .html ? ".html" : ".md") |
| 20 | } |
| 21 | |
| 22 | static var transferRepresentation: some TransferRepresentation { |
| 23 | FileRepresentation(exportedContentType: .html) { export in |
| 24 | try export.write() |
| 25 | } |
| 26 | .exportingCondition { $0.format == .html } |
| 27 | FileRepresentation(exportedContentType: UTType(filenameExtension: "md") ?? .plainText) { export in |
| 28 | try export.write() |
| 29 | } |
| 30 | .exportingCondition { $0.format == .markdown } |
| 31 | } |
| 32 | |
| 33 | private func write() throws -> SentTransferredFile { |
| 34 | let base = ((path as NSString).lastPathComponent as NSString).deletingPathExtension |
| 35 | let content = switch format { |
| 36 | case .html: HTMLExport.page(text, fallbackTitle: base, settings: settings, path: path) |
| 37 | case .markdown: MarkdownExport.document(text, settings: settings) |
| 38 | } |
| 39 | let folder = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) |
| 40 | try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true) |
| 41 | let url = folder.appendingPathComponent(fileName) |
| 42 | try content.write(to: url, atomically: true, encoding: .utf8) |
| 43 | return SentTransferredFile(url) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | /// Sharing the file as HTML or Markdown: to another app, or saved with Save to Files. |
| 48 | struct ExportMenu: View { |
| 49 | let path: String |
| 50 | let text: String |
| 51 | |
| 52 | var body: some View { |
| 53 | Menu("Export", systemImage: "square.and.arrow.up") { |
| 54 | ForEach([(OrgExport.Format.html, "HTML"), (.markdown, "Markdown")], id: \.1) { format, name in |
| 55 | let export = OrgExport(text: text, path: path, format: format, settings: OrgPreferences.orgSettings()) |
| 56 | ShareLink(item: export, preview: SharePreview(export.fileName)) { Text(name) } |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | #endif |
Sources/OrgstarMobile/ReaderScreen.swift
+1
| @@ -67,6 +67,7 @@ struct ReaderScreen: View { |
| 67 | 67 | ToolbarItemGroup(placement: .primaryAction) { |
| 68 | 68 | Button("Outline", systemImage: "list.bullet.indent") { showingOutline = true }.disabled(outline.isEmpty) |
| 69 | 69 | if let path = session.path { |
| 70 | if let text = session.document?.text { ExportMenu(path: path, text: text) } |
| 70 | 71 | NavigationLink(value: EditorRoute(path: path, offset: nil)) { Label("Edit", systemImage: "square.and.pencil") } |
| 71 | 72 | } |
| 72 | 73 | } |