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 | Menu("More", systemImage: "ellipsis.circle") { |
53 | Menu("More", systemImage: "ellipsis.circle") { |
| 54 | Button("Commands", systemImage: "command") { sheet = .palette } |
54 | Button("Commands", systemImage: "command") { sheet = .palette } |
| 55 | Button("Clock In", systemImage: "stopwatch") { editor?.run("app.clock.in") } |
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 | Button("Sync Conflict Copies", systemImage: "doc.on.doc") { sheet = .syncCopies } |
57 | Button("Sync Conflict Copies", systemImage: "doc.on.doc") { sheet = .syncCopies } |
| 57 | Button("Recovered Versions", systemImage: "clock.arrow.circlepath") { sheet = .recovery } |
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 | ToolbarItemGroup(placement: .primaryAction) { |
67 | ToolbarItemGroup(placement: .primaryAction) { |
| 68 | Button("Outline", systemImage: "list.bullet.indent") { showingOutline = true }.disabled(outline.isEmpty) |
68 | Button("Outline", systemImage: "list.bullet.indent") { showingOutline = true }.disabled(outline.isEmpty) |
| 69 | if let path = session.path { |
69 | if let path = session.path { |
| |
70 | if let text = session.document?.text { ExportMenu(path: path, text: text) } |
| 70 | NavigationLink(value: EditorRoute(path: path, offset: nil)) { Label("Edit", systemImage: "square.and.pencil") } |
71 | NavigationLink(value: EditorRoute(path: path, offset: nil)) { Label("Edit", systemImage: "square.and.pencil") } |
| 71 | } |
72 | } |
| 72 | } |
73 | } |