From 5009ead53fe1594133a3898046586559d276c2e6 Mon Sep 17 00:00:00 2001 From: rk-helper <62377740+rk-helper@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:33:20 +0400 Subject: [PATCH 1/5] Centered texting --- freewrite.xcodeproj/project.pbxproj | 8 +++--- freewrite/ContentView.swift | 38 +++++++++++++++++++---------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/freewrite.xcodeproj/project.pbxproj b/freewrite.xcodeproj/project.pbxproj index f89a773..e2600be 100644 --- a/freewrite.xcodeproj/project.pbxproj +++ b/freewrite.xcodeproj/project.pbxproj @@ -402,7 +402,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = 2UDAY4J48G; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -419,7 +419,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 13.5; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = app.humansongs.freewrite; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -444,7 +444,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = 2UDAY4J48G; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -461,7 +461,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 13.5; + MACOSX_DEPLOYMENT_TARGET = 14.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = app.humansongs.freewrite; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/freewrite/ContentView.swift b/freewrite/ContentView.swift index cc4113a..b9b927f 100644 --- a/freewrite/ContentView.swift +++ b/freewrite/ContentView.swift @@ -381,6 +381,8 @@ struct ContentView: View { return colorScheme == .light ? Color.primary : Color.white } + @State private var viewHeight: CGFloat = 0 + var body: some View { let buttonBackground = colorScheme == .light ? Color.white : Color.black let navHeight: CGFloat = 68 @@ -393,17 +395,18 @@ struct ContentView: View { Color(colorScheme == .light ? .white : .black) .ignoresSafeArea() - TextEditor(text: Binding( - get: { text }, - set: { newValue in - // Ensure the text always starts with two newlines - if !newValue.hasPrefix("\n\n") { - text = "\n\n" + newValue.trimmingCharacters(in: .newlines) - } else { - text = newValue + + TextEditor(text: Binding( + get: { text }, + set: { newValue in + // Ensure the text always starts with two newlines + if !newValue.hasPrefix("\n\n") { + text = "\n\n" + newValue.trimmingCharacters(in: .newlines) + } else { + text = newValue + } } - } - )) + )) .background(Color(colorScheme == .light ? .white : .black)) .font(.custom(selectedFont, size: fontSize)) .foregroundColor(colorScheme == .light ? Color(red: 0.20, green: 0.20, blue: 0.20) : Color(red: 0.9, green: 0.9, blue: 0.9)) @@ -411,6 +414,8 @@ struct ContentView: View { .scrollIndicators(.never) .lineSpacing(lineHeight) .frame(maxWidth: 650) + + .id("\(selectedFont)-\(fontSize)-\(colorScheme)") .padding(.bottom, bottomNavOpacity > 0 ? navHeight : 0) .ignoresSafeArea() @@ -425,13 +430,20 @@ struct ContentView: View { Text(placeholderText) .font(.custom(selectedFont, size: fontSize)) .foregroundColor(colorScheme == .light ? .gray.opacity(0.5) : .gray.opacity(0.6)) - // .padding(.top, 8) - // .padding(.leading, 8) + // .padding(.top, 8) + // .padding(.leading, 8) .allowsHitTesting(false) .offset(x: 5, y: placeholderOffset) } }, alignment: .topLeading ) + .onGeometryChange(for: CGFloat.self) { proxy in + proxy.size.height + } action: { height in + viewHeight = height + } + .contentMargins(.bottom, viewHeight / 2) + VStack { Spacer() @@ -1305,4 +1317,4 @@ extension NSView { #Preview { ContentView() -} \ No newline at end of file +} From 57ba4af5be325347048eb884364cb63ba3606025 Mon Sep 17 00:00:00 2001 From: thorfinn Date: Fri, 25 Apr 2025 05:47:09 -0700 Subject: [PATCH 2/5] changes --- freewrite.xcodeproj/project.pbxproj | 8 ++++---- freewrite/ContentView.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freewrite.xcodeproj/project.pbxproj b/freewrite.xcodeproj/project.pbxproj index e2600be..f89a773 100644 --- a/freewrite.xcodeproj/project.pbxproj +++ b/freewrite.xcodeproj/project.pbxproj @@ -402,7 +402,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2UDAY4J48G; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -419,7 +419,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 13.5; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = app.humansongs.freewrite; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -444,7 +444,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2UDAY4J48G; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -461,7 +461,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 18.1; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 14.0; + MACOSX_DEPLOYMENT_TARGET = 13.5; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = app.humansongs.freewrite; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/freewrite/ContentView.swift b/freewrite/ContentView.swift index b9b927f..2616d91 100644 --- a/freewrite/ContentView.swift +++ b/freewrite/ContentView.swift @@ -442,7 +442,7 @@ struct ContentView: View { } action: { height in viewHeight = height } - .contentMargins(.bottom, viewHeight / 2) + .contentMargins(.bottom, viewHeight / 4) VStack { From 601365c8529bd448ce72af3f4fb30077a52cce3a Mon Sep 17 00:00:00 2001 From: rk-helper <62377740+rk-helper@users.noreply.github.com> Date: Fri, 25 Apr 2025 17:57:16 +0400 Subject: [PATCH 3/5] initial project file --- freewrite.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freewrite.xcodeproj/project.pbxproj b/freewrite.xcodeproj/project.pbxproj index e2600be..b4bede5 100644 --- a/freewrite.xcodeproj/project.pbxproj +++ b/freewrite.xcodeproj/project.pbxproj @@ -402,7 +402,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2UDAY4J48G; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -444,7 +444,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"freewrite/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 2UDAY4J48G; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; From c89b0c2fd98248843149f1c5f2b739474911c799 Mon Sep 17 00:00:00 2001 From: francostan Date: Tue, 29 Apr 2025 12:23:50 +0200 Subject: [PATCH 4/5] add searcher bar --- freewrite/ContentView.swift | 85 ++++++++++++------------------------- 1 file changed, 27 insertions(+), 58 deletions(-) diff --git a/freewrite/ContentView.swift b/freewrite/ContentView.swift index 1ed74db..6dfa8da 100644 --- a/freewrite/ContentView.swift +++ b/freewrite/ContentView.swift @@ -85,6 +85,7 @@ struct ContentView: View { @State private var colorScheme: ColorScheme = .light // Add state for color scheme @State private var isHoveringThemeToggle = false // Add state for theme toggle hover @State private var didCopyPrompt: Bool = false // Add state for copy prompt feedback + @State private var searchText: String = "" let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() let entryHeight: CGFloat = 40 @@ -888,7 +889,7 @@ struct ContentView: View { // Right sidebar if showingSidebar { Divider() - + VStack(spacing: 0) { // Header Button(action: { @@ -918,21 +919,31 @@ struct ContentView: View { .onHover { hovering in isHoveringHistory = hovering } - + Divider() - - // Entries List + + // searchBar + HStack { + TextField("Search entries...", text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + .padding(8) + .background(Color.gray.opacity(0.1)) + .cornerRadius(8) + } + .padding([.horizontal, .vertical], 12) + + Divider() + + // Entries List with Search Filtering ScrollView { LazyVStack(spacing: 0) { - ForEach(entries) { entry in + ForEach(entries.filter { searchText.isEmpty ? true : $0.previewText.localizedCaseInsensitiveContains(searchText) }) { entry in Button(action: { if selectedEntryId != entry.id { - // Save current entry before switching if let currentId = selectedEntryId, let currentEntry = entries.first(where: { $0.id == currentId }) { saveEntry(entry: currentEntry) } - selectedEntryId = entry.id loadEntry(entry: entry) } @@ -944,83 +955,41 @@ struct ContentView: View { .font(.system(size: 13)) .lineLimit(1) .foregroundColor(.primary) - + Spacer() - - // Export/Trash icons that appear on hover + if hoveredEntryId == entry.id { HStack(spacing: 8) { - // Export PDF button - Button(action: { - exportEntryAsPDF(entry: entry) - }) { + Button(action: { exportEntryAsPDF(entry: entry) }) { Image(systemName: "arrow.down.circle") .font(.system(size: 11)) - .foregroundColor(hoveredExportId == entry.id ? - (colorScheme == .light ? .black : .white) : - (colorScheme == .light ? .gray : .gray.opacity(0.8))) + .foregroundColor(hoveredExportId == entry.id ? (colorScheme == .light ? .black : .white) : .gray) } .buttonStyle(.plain) - .help("Export entry as PDF") - .onHover { hovering in - withAnimation(.easeInOut(duration: 0.2)) { - hoveredExportId = hovering ? entry.id : nil - } - if hovering { - NSCursor.pointingHand.push() - } else { - NSCursor.pop() - } - } - - // Trash icon - Button(action: { - deleteEntry(entry: entry) - }) { + + Button(action: { deleteEntry(entry: entry) }) { Image(systemName: "trash") .font(.system(size: 11)) .foregroundColor(hoveredTrashId == entry.id ? .red : .gray) } .buttonStyle(.plain) - .onHover { hovering in - withAnimation(.easeInOut(duration: 0.2)) { - hoveredTrashId = hovering ? entry.id : nil - } - if hovering { - NSCursor.pointingHand.push() - } else { - NSCursor.pop() - } - } } } } - Text(entry.date) .font(.system(size: 12)) .foregroundColor(.secondary) } } - .frame(maxWidth: .infinity) .padding(.horizontal, 16) .padding(.vertical, 8) - .background( - RoundedRectangle(cornerRadius: 4) - .fill(backgroundColor(for: entry)) - ) + .background(RoundedRectangle(cornerRadius: 4).fill(backgroundColor(for: entry))) } .buttonStyle(PlainButtonStyle()) .contentShape(Rectangle()) .onHover { hovering in - withAnimation(.easeInOut(duration: 0.2)) { - hoveredEntryId = hovering ? entry.id : nil - } - } - .onAppear { - NSCursor.pop() // Reset cursor when button appears + hoveredEntryId = hovering ? entry.id : nil } - .help("Click to select this entry") // Add tooltip - if entry.id != entries.last?.id { Divider() } @@ -1030,7 +999,7 @@ struct ContentView: View { .scrollIndicators(.never) } .frame(width: 200) - .background(Color(colorScheme == .light ? .white : NSColor.black)) + .background(Color(colorScheme == .light ? .white : .black)) } } .frame(minWidth: 1100, minHeight: 600) From 22da78e822a134313b6badf5ce197c25a67d0a85 Mon Sep 17 00:00:00 2001 From: francostan Date: Tue, 29 Apr 2025 13:29:04 +0200 Subject: [PATCH 5/5] added searcher bar --- freewrite/ContentView.swift | 115 ++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/freewrite/ContentView.swift b/freewrite/ContentView.swift index 6dfa8da..b3813eb 100644 --- a/freewrite/ContentView.swift +++ b/freewrite/ContentView.swift @@ -86,6 +86,21 @@ struct ContentView: View { @State private var isHoveringThemeToggle = false // Add state for theme toggle hover @State private var didCopyPrompt: Bool = false // Add state for copy prompt feedback @State private var searchText: String = "" + + var filteredEntries: [HumanEntry] { + let query = searchText.trimmed().lowercased() + guard !query.isEmpty else { return entries } + + return entries.filter { entry in + let fileURL = getDocumentsDirectory().appendingPathComponent(entry.filename) + if let content = try? String(contentsOf: fileURL, encoding: .utf8) { + return content.lowercased().contains(query) + } + return false + } + } + + let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() let entryHeight: CGFloat = 40 @@ -889,7 +904,7 @@ struct ContentView: View { // Right sidebar if showingSidebar { Divider() - + VStack(spacing: 0) { // Header Button(action: { @@ -919,31 +934,32 @@ struct ContentView: View { .onHover { hovering in isHoveringHistory = hovering } - + Divider() - - // searchBar + HStack { - TextField("Search entries...", text: $searchText) - .textFieldStyle(PlainTextFieldStyle()) - .padding(8) - .background(Color.gray.opacity(0.1)) - .cornerRadius(8) - } - .padding([.horizontal, .vertical], 12) - - Divider() - - // Entries List with Search Filtering + TextField("Search entries...", text: $searchText) + .textFieldStyle(PlainTextFieldStyle()) + .padding(8) + .background(Color.gray.opacity(0.1)) + .cornerRadius(8) + } + .padding([.horizontal, .vertical], 12) + + Divider() + + // Entries List ScrollView { LazyVStack(spacing: 0) { - ForEach(entries.filter { searchText.isEmpty ? true : $0.previewText.localizedCaseInsensitiveContains(searchText) }) { entry in + ForEach(filteredEntries) { entry in Button(action: { if selectedEntryId != entry.id { + // Save current entry before switching if let currentId = selectedEntryId, let currentEntry = entries.first(where: { $0.id == currentId }) { saveEntry(entry: currentEntry) } + selectedEntryId = entry.id loadEntry(entry: entry) } @@ -955,41 +971,83 @@ struct ContentView: View { .font(.system(size: 13)) .lineLimit(1) .foregroundColor(.primary) - + Spacer() - + + // Export/Trash icons that appear on hover if hoveredEntryId == entry.id { HStack(spacing: 8) { - Button(action: { exportEntryAsPDF(entry: entry) }) { + // Export PDF button + Button(action: { + exportEntryAsPDF(entry: entry) + }) { Image(systemName: "arrow.down.circle") .font(.system(size: 11)) - .foregroundColor(hoveredExportId == entry.id ? (colorScheme == .light ? .black : .white) : .gray) + .foregroundColor(hoveredExportId == entry.id ? + (colorScheme == .light ? .black : .white) : + (colorScheme == .light ? .gray : .gray.opacity(0.8))) } .buttonStyle(.plain) - - Button(action: { deleteEntry(entry: entry) }) { + .help("Export entry as PDF") + .onHover { hovering in + withAnimation(.easeInOut(duration: 0.2)) { + hoveredExportId = hovering ? entry.id : nil + } + if hovering { + NSCursor.pointingHand.push() + } else { + NSCursor.pop() + } + } + + // Trash icon + Button(action: { + deleteEntry(entry: entry) + }) { Image(systemName: "trash") .font(.system(size: 11)) .foregroundColor(hoveredTrashId == entry.id ? .red : .gray) } .buttonStyle(.plain) + .onHover { hovering in + withAnimation(.easeInOut(duration: 0.2)) { + hoveredTrashId = hovering ? entry.id : nil + } + if hovering { + NSCursor.pointingHand.push() + } else { + NSCursor.pop() + } + } } } } + Text(entry.date) .font(.system(size: 12)) .foregroundColor(.secondary) } } + .frame(maxWidth: .infinity) .padding(.horizontal, 16) .padding(.vertical, 8) - .background(RoundedRectangle(cornerRadius: 4).fill(backgroundColor(for: entry))) + .background( + RoundedRectangle(cornerRadius: 4) + .fill(backgroundColor(for: entry)) + ) } .buttonStyle(PlainButtonStyle()) .contentShape(Rectangle()) .onHover { hovering in - hoveredEntryId = hovering ? entry.id : nil + withAnimation(.easeInOut(duration: 0.2)) { + hoveredEntryId = hovering ? entry.id : nil + } } + .onAppear { + NSCursor.pop() // Reset cursor when button appears + } + .help("Click to select this entry") // Add tooltip + if entry.id != entries.last?.id { Divider() } @@ -999,7 +1057,7 @@ struct ContentView: View { .scrollIndicators(.never) } .frame(width: 200) - .background(Color(colorScheme == .light ? .white : .black)) + .background(Color(colorScheme == .light ? .white : NSColor.black)) } } .frame(minWidth: 1100, minHeight: 600) @@ -1378,6 +1436,13 @@ extension NSView { } } +extension String { + func trimmed() -> String { + self.trimmingCharacters(in: .whitespacesAndNewlines) + } +} + + #Preview { ContentView() }