krz/libre-edit

A free and private text editor for iOS. ios privacy swift text-editor

Commit 42f3aa0f6f

42f3aa0f6f9b72b06583715c812b0668801b5689

parent: 896d36505a

Verified · cmc

cmc <hello@cleberg.net> · 2024-01-14 18:01 UTC

feat: add option to view rendered markdown

Layout: unified · split

LibreEdit.xcodeproj/project.pbxproj +31
@@ -7,6 +7,8 @@
7 objects = { 7 objects = {
8 8
9/* Begin PBXBuildFile section */ 9/* Begin PBXBuildFile section */
10 8B930F732B544EA200413A34 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8B930F722B544EA200413A34 /* MarkdownUI */; };
11 8B930F752B54504D00413A34 /* MarkdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B930F742B54504D00413A34 /* MarkdownView.swift */; };
10 8BCA57062B537F9600DBA94B /* LibreEditApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCA57052B537F9600DBA94B /* LibreEditApp.swift */; }; 12 8BCA57062B537F9600DBA94B /* LibreEditApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCA57052B537F9600DBA94B /* LibreEditApp.swift */; };
11 8BCA57082B537F9600DBA94B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCA57072B537F9600DBA94B /* ContentView.swift */; }; 13 8BCA57082B537F9600DBA94B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BCA57072B537F9600DBA94B /* ContentView.swift */; };
12 8BCA570A2B537F9700DBA94B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8BCA57092B537F9700DBA94B /* Assets.xcassets */; }; 14 8BCA570A2B537F9700DBA94B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8BCA57092B537F9700DBA94B /* Assets.xcassets */; };
@@ -14,6 +16,7 @@
14/* End PBXBuildFile section */ 16/* End PBXBuildFile section */
15 17
16/* Begin PBXFileReference section */ 18/* Begin PBXFileReference section */
19 8B930F742B54504D00413A34 /* MarkdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownView.swift; sourceTree = "<group>"; };
17 8BCA57022B537F9600DBA94B /* LibreEdit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LibreEdit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 8BCA57022B537F9600DBA94B /* LibreEdit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LibreEdit.app; sourceTree = BUILT_PRODUCTS_DIR; };
18 8BCA57052B537F9600DBA94B /* LibreEditApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreEditApp.swift; sourceTree = "<group>"; }; 21 8BCA57052B537F9600DBA94B /* LibreEditApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreEditApp.swift; sourceTree = "<group>"; };
19 8BCA57072B537F9600DBA94B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; 22 8BCA57072B537F9600DBA94B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -26,6 +29,7 @@
26 isa = PBXFrameworksBuildPhase; 29 isa = PBXFrameworksBuildPhase;
27 buildActionMask = 2147483647; 30 buildActionMask = 2147483647;
28 files = ( 31 files = (
32 8B930F732B544EA200413A34 /* MarkdownUI in Frameworks */,
29 ); 33 );
30 runOnlyForDeploymentPostprocessing = 0; 34 runOnlyForDeploymentPostprocessing = 0;
31 }; 35 };
@@ -55,6 +59,7 @@
55 8BCA57072B537F9600DBA94B /* ContentView.swift */, 59 8BCA57072B537F9600DBA94B /* ContentView.swift */,
56 8BCA57092B537F9700DBA94B /* Assets.xcassets */, 60 8BCA57092B537F9700DBA94B /* Assets.xcassets */,
57 8BCA570B2B537F9700DBA94B /* Preview Content */, 61 8BCA570B2B537F9700DBA94B /* Preview Content */,
62 8B930F742B54504D00413A34 /* MarkdownView.swift */,
58 ); 63 );
59 path = LibreEdit; 64 path = LibreEdit;
60 sourceTree = "<group>"; 65 sourceTree = "<group>";
@@ -83,6 +88,9 @@
83 dependencies = ( 88 dependencies = (
84 ); 89 );
85 name = LibreEdit; 90 name = LibreEdit;
91 packageProductDependencies = (
92 8B930F722B544EA200413A34 /* MarkdownUI */,
93 );
86 productName = LibreEdit; 94 productName = LibreEdit;
87 productReference = 8BCA57022B537F9600DBA94B /* LibreEdit.app */; 95 productReference = 8BCA57022B537F9600DBA94B /* LibreEdit.app */;
88 productType = "com.apple.product-type.application"; 96 productType = "com.apple.product-type.application";
@@ -111,6 +119,9 @@
111 Base, 119 Base,
112 ); 120 );
113 mainGroup = 8BCA56F92B537F9600DBA94B; 121 mainGroup = 8BCA56F92B537F9600DBA94B;
122 packageReferences = (
123 8B930F712B544EA200413A34 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
124 );
114 productRefGroup = 8BCA57032B537F9600DBA94B /* Products */; 125 productRefGroup = 8BCA57032B537F9600DBA94B /* Products */;
115 projectDirPath = ""; 126 projectDirPath = "";
116 projectRoot = ""; 127 projectRoot = "";
@@ -137,6 +148,7 @@
137 isa = PBXSourcesBuildPhase; 148 isa = PBXSourcesBuildPhase;
138 buildActionMask = 2147483647; 149 buildActionMask = 2147483647;
139 files = ( 150 files = (
151 8B930F752B54504D00413A34 /* MarkdownView.swift in Sources */,
140 8BCA57082B537F9600DBA94B /* ContentView.swift in Sources */, 152 8BCA57082B537F9600DBA94B /* ContentView.swift in Sources */,
141 8BCA57062B537F9600DBA94B /* LibreEditApp.swift in Sources */, 153 8BCA57062B537F9600DBA94B /* LibreEditApp.swift in Sources */,
142 ); 154 );
@@ -346,6 +358,25 @@
346 defaultConfigurationName = Release; 358 defaultConfigurationName = Release;
347 }; 359 };
348/* End XCConfigurationList section */ 360/* End XCConfigurationList section */
361
362/* Begin XCRemoteSwiftPackageReference section */
363 8B930F712B544EA200413A34 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */ = {
364 isa = XCRemoteSwiftPackageReference;
365 repositoryURL = "https://github.com/gonzalezreal/swift-markdown-ui";
366 requirement = {
367 kind = upToNextMajorVersion;
368 minimumVersion = 2.3.0;
369 };
370 };
371/* End XCRemoteSwiftPackageReference section */
372
373/* Begin XCSwiftPackageProductDependency section */
374 8B930F722B544EA200413A34 /* MarkdownUI */ = {
375 isa = XCSwiftPackageProductDependency;
376 package = 8B930F712B544EA200413A34 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
377 productName = MarkdownUI;
378 };
379/* End XCSwiftPackageProductDependency section */
349 }; 380 };
350 rootObject = 8BCA56FA2B537F9600DBA94B /* Project object */; 381 rootObject = 8BCA56FA2B537F9600DBA94B /* Project object */;
351} 382}
LibreEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved added +23
@@ -0,0 +1,23 @@
1{
2 "pins" : [
3 {
4 "identity" : "networkimage",
5 "kind" : "remoteSourceControl",
6 "location" : "https://github.com/gonzalezreal/NetworkImage",
7 "state" : {
8 "revision" : "7aff8d1b31148d32c5933d75557d42f6323ee3d1",
9 "version" : "6.0.0"
10 }
11 },
12 {
13 "identity" : "swift-markdown-ui",
14 "kind" : "remoteSourceControl",
15 "location" : "https://github.com/gonzalezreal/swift-markdown-ui",
16 "state" : {
17 "revision" : "ae799d015a5374708f7b4c85f3294c05f2a564e2",
18 "version" : "2.3.0"
19 }
20 }
21 ],
22 "version" : 2
23}
LibreEdit/ContentView.swift +49 −42
@@ -43,51 +43,58 @@ struct ContentView: View {
43 @Binding var document: TextFile 43 @Binding var document: TextFile
44 @State private var showingPopover = false 44 @State private var showingPopover = false
45 @State private var wordCount: Int = 0 45 @State private var wordCount: Int = 0
46 46
47 var body: some View { 47 var body: some View {
48 Text("\(wordCount) words") 48 NavigationView {
49 .foregroundColor(Color.white) 49 VStack {
50 .font(.headline) 50 Text("\(wordCount) words")
51 .padding(.trailing) 51 .foregroundColor(Color.white)
52 .frame(maxWidth: .infinity, maxHeight: 50) 52 .font(.headline)
53 .background(Color.gray.opacity(0.3)) 53 .padding(.trailing)
54 .padding(.top, 20) 54 .frame(maxWidth: .infinity, maxHeight: 50)
55 TextEditor(text: $document.text) 55 .background(Color.gray.opacity(0.3))
56 .onAppear { 56 .padding(.top, 20)
57 self.wordCount = countWords(text: document.text) 57 TextEditor(text: $document.text)
58 } 58 .onAppear {
59 .onChange(of: document.text) { 59 self.wordCount = countWords(text: document.text)
60 self.wordCount = countWords(text: document.text) 60 }
61 } 61 .onChange(of: document.text) {
62 .toolbar { 62 self.wordCount = countWords(text: document.text)
63 ToolbarItemGroup(placement: .secondaryAction) { 63 }
64 Button { 64 .toolbar {
65 showingPopover = true 65 ToolbarItemGroup(placement: .secondaryAction) {
66 } label: { 66 NavigationLink(destination: MarkdownView(document: $document)) {
67 Text("More Info") 67 Text("Show Rendered Markdown")
68 }.popover(isPresented: $showingPopover) { 68 }
69 VStack(alignment: .leading) { 69 Button {
70 Text("More Info") 70 showingPopover = true
71 .font(.largeTitle) 71 } label: {
72 Text("") 72 Text("More Info")
73 Text("Instructions") 73 }.popover(isPresented: $showingPopover) {
74 .font(.title) 74 VStack(alignment: .leading) {
75 Text("") 75 Text("More Info")
76 Text("LibreEdit provides a direct interface with the Apple Files app on your iPhone. Simply navigate to your preferred directory, edit existing files, or create new files!") 76 .font(.largeTitle)
77 Text("") 77 Text("")
78 Text("Developer") 78 Text("Instructions")
79 .font(.title) 79 .font(.title)
80 Text("") 80 Text("")
81 Text("LibreEdit is a free and open source text editor for iOS built by [Christian Cleberg](https://cleberg.net).") 81 Text("LibreEdit provides a direct interface with the Apple Files app on your iPhone. Simply navigate to your preferred directory, edit existing files, or create new files!")
82 Text("") 82 Text("")
83 Text("Visit the [GitHub Repository](https://github.com/ccleberg/ios-edit) to view the source code.") 83 Text("Developer")
84 Text("") 84 .font(.title)
85 Text("This project was developed under the [GNU GPL v3 license](https://github.com/ccleberg/ios-edit/LICENSE).") 85 Text("")
86 Spacer() 86 Text("LibreEdit is a free and open source text editor for iOS built by [Christian Cleberg](https://cleberg.net).")
87 Text("")
88 Text("Visit the [GitHub Repository](https://github.com/ccleberg/ios-edit) to view the source code.")
89 Text("")
90 Text("This project was developed under the [GNU GPL v3 license](https://github.com/ccleberg/ios-edit/LICENSE).")
91 Spacer()
92 }
93 .padding()
94 }
87 } 95 }
88 .padding()
89 } 96 }
90 }
91 } 97 }
98 }
92 } 99 }
93} 100}
LibreEdit/MarkdownView.swift added +24
@@ -0,0 +1,24 @@
1//
2// MarkdownView.swift
3// LibreEdit
4//
5// Created by Christian Cleberg on 2024-01-14.
6//
7
8import SwiftUI
9import MarkdownUI
10
11struct MarkdownView: View {
12 @Binding var document: TextFile
13
14 var body: some View {
15 ScrollView {
16 Markdown {
17 MarkdownContent(document.text)
18 }
19 .markdownTheme(.docC)
20 .padding()
21 }
22 .navigationTitle("Rendered Markdown")
23 }
24}
README.md +13 −5
@@ -3,12 +3,20 @@
3LibreEdit intended to be a simple open-source text editing app for iOS, written 3LibreEdit intended to be a simple open-source text editing app for iOS, written
4in Swift. 4in Swift.
5 5
6## TODO 6LibreEdit integrates with the native iOS file manager, allowing users full
7control over the files themselves. The editor is simple and should display all
8file types.
7 9
8- [ ] Add functionality to `New` button. 10There is an option to view rendered Markdown in the secondary actions menu in
9- [ ] Allow custom file names when saving. 11the naviation bar.
10- [ ] Allow editing of existing files.
11 12
12## Screenshots 13## Screenshots
13 14
14![Libre Edit](Screenshots/libre_edit.png) 15Native file manager:
16![Libre Edit](Screenshots/file_manager.png)
17
18Editor:
19![Libre Edit](Screenshots/editor.png)
20
21Rendered Markdown:
22![Libre Edit](Screenshots/rendered.png)
Screenshots/editor.png added

Binary file not shown.

Screenshots/file_manager.png added

Binary file not shown.

Screenshots/libre_edit.png deleted

Binary file not shown.

Screenshots/rendered.png added

Binary file not shown.