aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2025-12-18 15:13:52 +0100
committerRuben Beltran del Rio <jj@r.bdr.sh>2025-12-18 17:07:20 +0100
commit1b55fdad5984841334e864db942693cfef10e645 (patch)
tree656a0aaf4058e54cd20d423b150cc82907c1e12b
parent86e296f3b194979de41715ddffe8af262c23c4ba (diff)
Add sharing
-rw-r--r--Makefile1
-rw-r--r--Map/MapApp.swift2
-rw-r--r--Map/Presentation/Base Components/CollaborationButton.swift51
-rw-r--r--Map/Presentation/MapEditor.swift29
-rw-r--r--QuickLook/MapPreviewView.swift41
-rw-r--r--QuickLook/PreviewViewController.swift113
-rw-r--r--QuickLook/QuickLookTextEditor.swift78
7 files changed, 201 insertions, 114 deletions
diff --git a/Makefile b/Makefile
index 82e1c93..9e667ec 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,7 @@ deploy_host := deploy@conchos.bdr.sh
deploy_path := /srv/http/build.r.bdr.sh/map
distribute: archive package notarize repackage generate_appcast sign_distributable upload
+local_package: archive package notarize repackage sign_distributable
sign_distributable:
@scripts/sign.sh "$(project_name)" "$(build_directory)"
diff --git a/Map/MapApp.swift b/Map/MapApp.swift
index 989349c..38ff408 100644
--- a/Map/MapApp.swift
+++ b/Map/MapApp.swift
@@ -36,7 +36,7 @@ struct MapApp: App {
.focusedSceneValue(\.document, file.$document)
.focusedSceneValue(\.fileURL, file.fileURL)
}
- .windowToolbarStyle(.unifiedCompact)
+ .windowToolbarStyle(.unified)
.commands {
MapCommands()
UpdateCommands(updaterController: updaterController)
diff --git a/Map/Presentation/Base Components/CollaborationButton.swift b/Map/Presentation/Base Components/CollaborationButton.swift
new file mode 100644
index 0000000..49dd2c5
--- /dev/null
+++ b/Map/Presentation/Base Components/CollaborationButton.swift
@@ -0,0 +1,51 @@
+import SharedWithYou
+import SwiftUI
+
+struct CollaborationButton: NSViewRepresentable {
+ let fileURL: URL
+
+ func makeNSView(context: Context) -> SWCollaborationView {
+ let itemProvider = NSItemProvider(contentsOf: fileURL)!
+ let view = SWCollaborationView(itemProvider: itemProvider)
+
+ view.activeParticipantCount = 0
+ view.cloudSharingDelegate = context.coordinator
+
+ return view
+ }
+
+ func updateNSView(_ nsView: SWCollaborationView, context: Context) {
+ // Update participant count as needed
+ }
+
+ func makeCoordinator() -> Coordinator {
+ Coordinator(fileURL: fileURL)
+ }
+
+ class Coordinator: NSObject, NSCloudSharingServiceDelegate {
+ let fileURL: URL
+
+ init(fileURL: URL) {
+ self.fileURL = fileURL
+ }
+
+ func sharingService(
+ _ sharingService: NSSharingService,
+ didFailToShareItems items: [Any],
+ error: Error
+ ) {
+ print("Failed to share: \(error)")
+ }
+
+ func sharingService(
+ _ sharingService: NSSharingService,
+ didCompleteForItems items: [Any]
+ ) {
+ print("Sharing completed")
+ }
+
+ func options(for: NSSharingService, share: NSItemProvider) -> NSSharingService.CloudKitOptions {
+ return [.allowPublic, .allowPrivate, .allowReadWrite]
+ }
+ }
+}
diff --git a/Map/Presentation/MapEditor.swift b/Map/Presentation/MapEditor.swift
index c323882..fcf465e 100644
--- a/Map/Presentation/MapEditor.swift
+++ b/Map/Presentation/MapEditor.swift
@@ -12,13 +12,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see https://map.tranquil.systems.
+import SharedWithYou
import SwiftUI
import WmapParser
struct MapEditor: View {
+
@Binding var document: MapDocument
+ @FocusedValue(\.fileURL) var fileURL: URL?
+
@State var selectedEvolution: StageType = .behavior
@State var isSearching: Bool = false
+ @State var isPresentingSharingSheet: Bool = false
+
var parsedMap: WmapParser.Map {
document.parsed
}
@@ -209,6 +215,17 @@ struct MapEditor: View {
}
}
.toolbar {
+
+ if let fileURL {
+ if fileIsShared {
+ ToolbarItem(placement: .primaryAction) {
+ CollaborationButton(fileURL: fileURL)
+ }
+ }
+ ToolbarItem(placement: .primaryAction) {
+ ShareLink(item: fileURL)
+ }
+ }
if #unavailable(macOS 26) {
ToolbarItem(placement: .primaryAction) {
EvolutionPicker(selectedEvolution: $selectedEvolution)
@@ -219,6 +236,18 @@ struct MapEditor: View {
.focusedSceneValue(\.selectedEvolution, $selectedEvolution)
}
+ private var fileIsShared: Bool {
+ do {
+ if let fileURL {
+ let resourceValues = try fileURL.resourceValues(forKeys: [.ubiquitousItemIsSharedKey])
+ return resourceValues.ubiquitousItemIsShared ?? false
+ }
+ return false
+ } catch {
+ return false
+ }
+ }
+
@ViewBuilder
func adaptiveStack<Content: View>(@ViewBuilder content: () -> Content) -> some View {
if viewStyle == .horizontal {
diff --git a/QuickLook/MapPreviewView.swift b/QuickLook/MapPreviewView.swift
new file mode 100644
index 0000000..cd63130
--- /dev/null
+++ b/QuickLook/MapPreviewView.swift
@@ -0,0 +1,41 @@
+import SwiftUI
+import WmapParser
+
+struct MapPreviewView: View {
+ let parsedMap: WmapParser.Map
+ @State private var selectedEvolution: StageType = .general
+
+ var body: some View {
+ GeometryReader { geometry in
+ let mapSize = Dimensions.Map.size
+ let padding = Dimensions.Map.padding
+ let totalWidth = mapSize.width + 2 * padding
+ let totalHeight = mapSize.height + 2 * padding
+
+ // Calculate scale to fit the available space
+ let scaleX = geometry.size.width / totalWidth
+ let scaleY = geometry.size.height / totalHeight
+ let scale = min(scaleX, scaleY, 1.0) // Don't scale up, only down
+
+ ScrollView([.horizontal, .vertical]) {
+ MapRenderView(
+ parsedMap: parsedMap,
+ evolution: $selectedEvolution
+ )
+ .drawingGroup(opaque: false)
+ .scaleEffect(scale)
+ .frame(
+ width: totalWidth * scale,
+ height: totalHeight * scale
+ )
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .background(Color.white)
+ }
+ }
+}
+
+#Preview {
+ let parsedMap = WmapParser.parse("Hello (20, 20)")
+ MapPreviewView(parsedMap: parsedMap)
+}
diff --git a/QuickLook/PreviewViewController.swift b/QuickLook/PreviewViewController.swift
index 9dbc5f2..8a3467f 100644
--- a/QuickLook/PreviewViewController.swift
+++ b/QuickLook/PreviewViewController.swift
@@ -1,9 +1,6 @@
import Cocoa
import Quartz
-import SwiftTreeSitter
-import SwiftTreeSitterLayer
import SwiftUI
-import TreeSitterWmap
import WmapParser
class PreviewViewController: NSViewController, QLPreviewingController {
@@ -99,113 +96,3 @@ class PreviewViewController: NSViewController, QLPreviewingController {
}
}
}
-
-// A SwiftUI view specifically for QuickLook preview
-struct MapPreviewView: View {
- let parsedMap: WmapParser.Map
- @State private var selectedEvolution: StageType = .general
-
- var body: some View {
- GeometryReader { geometry in
- let mapSize = Dimensions.Map.size
- let padding = Dimensions.Map.padding
- let totalWidth = mapSize.width + 2 * padding
- let totalHeight = mapSize.height + 2 * padding
-
- // Calculate scale to fit the available space
- let scaleX = geometry.size.width / totalWidth
- let scaleY = geometry.size.height / totalHeight
- let scale = min(scaleX, scaleY, 1.0) // Don't scale up, only down
-
- ScrollView([.horizontal, .vertical]) {
- MapRenderView(
- parsedMap: parsedMap,
- evolution: $selectedEvolution
- )
- .drawingGroup(opaque: false)
- .scaleEffect(scale)
- .frame(
- width: totalWidth * scale,
- height: totalHeight * scale
- )
- }
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- .background(Color.white)
- }
- }
-}
-
-// A read-only text editor for QuickLook that shows syntax highlighting
-struct QuickLookTextEditor: NSViewRepresentable {
- let content: String
-
- func makeNSView(context: Context) -> NSScrollView {
- let scrollView = NSTextView.scrollableTextView()
- let textView = scrollView.documentView as! NSTextView
-
- // Configure the scroll view
- scrollView.hasVerticalScroller = true
- scrollView.hasHorizontalScroller = true
- scrollView.autohidesScrollers = false
-
- // Configure the text view for read-only display
- textView.isEditable = false
- textView.isSelectable = true
- textView.backgroundColor = NSColor.textBackgroundColor
- textView.font = NSFont.monospacedSystemFont(ofSize: 13, weight: .regular)
- textView.string = content
- textView.isVerticallyResizable = true
- textView.isHorizontallyResizable = true
- textView.textContainer?.widthTracksTextView = false
- textView.textContainer?.containerSize = NSSize(
- width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
-
- // Apply syntax highlighting using tree-sitter
- if let textStorage = textView.textStorage {
- applyTreeSitterHighlighting(textStorage: textStorage, content: content)
- }
-
- return scrollView
- }
-
- func updateNSView(_ nsView: NSScrollView, context: Context) {
- // No updates needed for read-only view
- }
-
- private func applyTreeSitterHighlighting(textStorage: NSTextStorage, content: String) {
- // Set up tree-sitter language layer
- guard let wmapConfiguration = try? LanguageConfiguration(tree_sitter_wmap(), name: "wmap")
- else { return }
-
- let languageConfiguration = LanguageLayer.Configuration(languageProvider: { name in
- switch name {
- case "wmap": return wmapConfiguration
- default: return nil
- }
- })
-
- guard
- let rootLayer = try? LanguageLayer(
- languageConfig: wmapConfiguration, configuration: languageConfiguration)
- else { return }
-
- // Update layer with content
- rootLayer.replaceContent(with: content)
-
- // Set default text color
- let fullRange = NSRange(location: 0, length: textStorage.length)
- textStorage.addAttribute(.foregroundColor, value: NSColor.textColor, range: fullRange)
-
- // Apply tree-sitter highlighting
- guard
- let highlights = try? rootLayer.highlights(
- in: fullRange, provider: content.predicateTextProvider)
- else { return }
-
- for highlight in highlights {
- let color = NSColor.Theme.Syntax.colorForSyntax(highlight.name)
- textStorage.addAttribute(.foregroundColor, value: color, range: highlight.range)
- }
- }
-
-}
diff --git a/QuickLook/QuickLookTextEditor.swift b/QuickLook/QuickLookTextEditor.swift
new file mode 100644
index 0000000..0501f6b
--- /dev/null
+++ b/QuickLook/QuickLookTextEditor.swift
@@ -0,0 +1,78 @@
+import SwiftUI
+import TreeSitterWmap
+import SwiftTreeSitter
+import SwiftTreeSitterLayer
+
+struct QuickLookTextEditor: NSViewRepresentable {
+ let content: String
+
+ func makeNSView(context: Context) -> NSScrollView {
+ let scrollView = NSTextView.scrollableTextView()
+ let textView = scrollView.documentView as! NSTextView
+
+ // Configure the scroll view
+ scrollView.hasVerticalScroller = true
+ scrollView.hasHorizontalScroller = true
+ scrollView.autohidesScrollers = false
+
+ // Configure the text view for read-only display
+ textView.isEditable = false
+ textView.isSelectable = true
+ textView.backgroundColor = NSColor.textBackgroundColor
+ textView.font = NSFont.monospacedSystemFont(ofSize: 13, weight: .regular)
+ textView.string = content
+ textView.isVerticallyResizable = true
+ textView.isHorizontallyResizable = true
+ textView.textContainer?.widthTracksTextView = false
+ textView.textContainer?.containerSize = NSSize(
+ width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
+
+ // Apply syntax highlighting using tree-sitter
+ if let textStorage = textView.textStorage {
+ applyTreeSitterHighlighting(textStorage: textStorage, content: content)
+ }
+
+ return scrollView
+ }
+
+ func updateNSView(_ nsView: NSScrollView, context: Context) {
+ // No updates needed for read-only view
+ }
+
+ private func applyTreeSitterHighlighting(textStorage: NSTextStorage, content: String) {
+ // Set up tree-sitter language layer
+ guard let wmapConfiguration = try? LanguageConfiguration(tree_sitter_wmap(), name: "wmap")
+ else { return }
+
+ let languageConfiguration = LanguageLayer.Configuration(languageProvider: { name in
+ switch name {
+ case "wmap": return wmapConfiguration
+ default: return nil
+ }
+ })
+
+ guard
+ let rootLayer = try? LanguageLayer(
+ languageConfig: wmapConfiguration, configuration: languageConfiguration)
+ else { return }
+
+ // Update layer with content
+ rootLayer.replaceContent(with: content)
+
+ // Set default text color
+ let fullRange = NSRange(location: 0, length: textStorage.length)
+ textStorage.addAttribute(.foregroundColor, value: NSColor.textColor, range: fullRange)
+
+ // Apply tree-sitter highlighting
+ guard
+ let highlights = try? rootLayer.highlights(
+ in: fullRange, provider: content.predicateTextProvider)
+ else { return }
+
+ for highlight in highlights {
+ let color = NSColor.Theme.Syntax.colorForSyntax(highlight.name)
+ textStorage.addAttribute(.foregroundColor, value: color, range: highlight.range)
+ }
+ }
+
+}