diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | Map/MapApp.swift | 2 | ||||
| -rw-r--r-- | Map/Presentation/Base Components/CollaborationButton.swift | 51 | ||||
| -rw-r--r-- | Map/Presentation/MapEditor.swift | 29 | ||||
| -rw-r--r-- | QuickLook/MapPreviewView.swift | 41 | ||||
| -rw-r--r-- | QuickLook/PreviewViewController.swift | 113 | ||||
| -rw-r--r-- | QuickLook/QuickLookTextEditor.swift | 78 |
7 files changed, 201 insertions, 114 deletions
@@ -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) + } + } + +} |