]> git.r.bdr.sh - rbdr/map/commitdiff
Update to support notes + new style 2.0.0
authorRuben Beltran del Rio <redacted>
Sun, 7 May 2023 13:22:54 +0000 (15:22 +0200)
committerRuben Beltran del Rio <redacted>
Sun, 7 May 2023 13:22:54 +0000 (15:22 +0200)
63 files changed:
Map.xcodeproj/project.pbxproj
Map.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved [new file with mode: 0644]
Map/Assets.xcassets/AccentColor.colorset/Contents.json
Map/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
Map/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
Map/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
Map/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
Map/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
Map/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
Map/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
Map/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
Map/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
Map/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
Map/Assets.xcassets/Colors/Background.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Foreground.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Syntax/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Syntax/Number.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Syntax/Option.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Syntax/Symbol.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Syntax/Vertex.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Dark Neutral Gray.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Dark Slate.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Jasper Red.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Light Neutral Gray.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Neutral Gray.colorset/Contents.json [new file with mode: 0644]
Map/Assets.xcassets/Colors/Theme/Olympic Blue.colorset/Contents.json [new file with mode: 0644]
Map/Core Extensions/Color+Theme.swift [new file with mode: 0644]
Map/Core Extensions/Date+format.swift [moved from Map/Extensions/Date+format.swift with 100% similarity]
Map/Core Extensions/Font+Theme.swift [new file with mode: 0644]
Map/Core Extensions/NSColor+Theme.swift [new file with mode: 0644]
Map/Core Extensions/NSImage+writePNG.swift [moved from Map/Extensions/NSImage+writePNG.swift with 100% similarity]
Map/Data/AppState.swift [moved from Map/State/AppState.swift with 94% similarity]
Map/Data/Models/Map+parse.swift [moved from Map/Extensions/Map+parse.swift with 94% similarity]
Map/Data/Persistence.swift [moved from Map/State/Persistence.swift with 100% similarity]
Map/Data/Stage.swift [moved from Map/State/Stage.swift with 100% similarity]
Map/Data/Store.swift [moved from Map/State/Store.swift with 100% similarity]
Map/Logic/Debouncer.swift [moved from Map/Debouncer.swift with 100% similarity]
Map/Logic/MapParser/MapParser.swift [moved from Map/MapParser/MapParser.swift with 80% similarity]
Map/Logic/MapParser/Strategies/BlockerParserStrategy.swift [moved from Map/MapParser/Strategies/BlockerParserStrategy.swift with 100% similarity]
Map/Logic/MapParser/Strategies/EdgeParserStrategy.swift [moved from Map/MapParser/Strategies/EdgeParserStrategy.swift with 100% similarity]
Map/Logic/MapParser/Strategies/NoteParserStrategy.swift [new file with mode: 0644]
Map/Logic/MapParser/Strategies/OpportunityParserStrategy.swift [moved from Map/MapParser/Strategies/OpportunityParserStrategy.swift with 100% similarity]
Map/Logic/MapParser/Strategies/StageParserStrategy.swift [moved from Map/MapParser/Strategies/StageParserStrategy.swift with 100% similarity]
Map/Logic/MapParser/Strategies/VertexParserStrategy.swift [moved from Map/MapParser/Strategies/VertexParserStrategy.swift with 100% similarity]
Map/MapApp.swift [moved from Map/Views/MapApp.swift with 95% similarity]
Map/MapColor.swift [deleted file]
Map/MapRenderComponents/MapStages.swift [deleted file]
Map/Presentation/Base Components/EvolutionPicker.swift [moved from Map/Views/EvolutionPicker.swift with 92% similarity]
Map/Presentation/Base Components/MapRender/MapAxes.swift [moved from Map/MapRenderComponents/MapAxes.swift with 72% similarity]
Map/Presentation/Base Components/MapRender/MapBlockers.swift [moved from Map/MapRenderComponents/MapBlockers.swift with 89% similarity]
Map/Presentation/Base Components/MapRender/MapEdges.swift [moved from Map/MapRenderComponents/MapEdges.swift with 91% similarity]
Map/Presentation/Base Components/MapRender/MapNotes.swift [new file with mode: 0644]
Map/Presentation/Base Components/MapRender/MapOpportunities.swift [moved from Map/MapRenderComponents/MapOpportunities.swift with 88% similarity]
Map/Presentation/Base Components/MapRender/MapStages.swift [new file with mode: 0644]
Map/Presentation/Base Components/MapRender/MapVertices.swift [moved from Map/MapRenderComponents/MapVertices.swift with 64% similarity]
Map/Presentation/Base Components/MapTextEditor.swift [moved from Map/Views/MapTextEditor.swift with 55% similarity]
Map/Presentation/Complex Components/MapRender/MapRenderView.swift [moved from Map/Views/MapRender.swift with 81% similarity]
Map/Presentation/Screens/EmptyMapDetailScreen.swift [moved from Map/Views/DefaultMapView.swift with 67% similarity]
Map/Presentation/Screens/MapDetailScreen.swift [moved from Map/Views/MapDetail.swift with 57% similarity]
Map/Presentation/Windows/MapEditorWindow.swift [moved from Map/Views/ContentView.swift with 90% similarity]
Map/Views/SlowMapRender.swift [deleted file]

index b212de6ee8128558dc1ed803e815508ac46b9ade..9cd5d87f48829e9bc33f0d6064131bcb2e1c9c58 100644 (file)
@@ -3,37 +3,35 @@
        archiveVersion = 1;
        classes = {
        };
        archiveVersion = 1;
        classes = {
        };
-       objectVersion = 50;
+       objectVersion = 53;
        objects = {
 
 /* Begin PBXBuildFile section */
                B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */; };
                B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B523C74525C9BD3500C44061 /* CloudKit.framework */; };
        objects = {
 
 /* Begin PBXBuildFile section */
                B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */; };
                B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B523C74525C9BD3500C44061 /* CloudKit.framework */; };
-               B523C74B25C9C1BA00C44061 /* DefaultMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */; };
+               B523C74B25C9C1BA00C44061 /* EmptyMapDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */; };
                B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C75925C9FD4900C44061 /* MapAxes.swift */; };
                B523C76225CA05A300C44061 /* MapStages.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76125CA05A300C44061 /* MapStages.swift */; };
                B523C76725CA071B00C44061 /* MapVertices.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76625CA071B00C44061 /* MapVertices.swift */; };
                B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76B25CA0DFA00C44061 /* MapEdges.swift */; };
                B523C77125CA121300C44061 /* MapBlockers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77025CA121300C44061 /* MapBlockers.swift */; };
                B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C75925C9FD4900C44061 /* MapAxes.swift */; };
                B523C76225CA05A300C44061 /* MapStages.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76125CA05A300C44061 /* MapStages.swift */; };
                B523C76725CA071B00C44061 /* MapVertices.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76625CA071B00C44061 /* MapVertices.swift */; };
                B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C76B25CA0DFA00C44061 /* MapEdges.swift */; };
                B523C77125CA121300C44061 /* MapBlockers.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77025CA121300C44061 /* MapBlockers.swift */; };
-               B523C77625CA181100C44061 /* MapColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77525CA181100C44061 /* MapColor.swift */; };
                B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77D25CA294C00C44061 /* MapOpportunities.swift */; };
                B526257225C874F9003E73B7 /* MapApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257125C874F9003E73B7 /* MapApp.swift */; };
                B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B523C77D25CA294C00C44061 /* MapOpportunities.swift */; };
                B526257225C874F9003E73B7 /* MapApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257125C874F9003E73B7 /* MapApp.swift */; };
-               B526257425C874F9003E73B7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257325C874F9003E73B7 /* ContentView.swift */; };
+               B526257425C874F9003E73B7 /* MapEditorWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257325C874F9003E73B7 /* MapEditorWindow.swift */; };
                B526257625C874FA003E73B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257525C874FA003E73B7 /* Assets.xcassets */; };
                B526257925C874FA003E73B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257825C874FA003E73B7 /* Preview Assets.xcassets */; };
                B526257B25C874FA003E73B7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257A25C874FA003E73B7 /* Persistence.swift */; };
                B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B526257C25C874FA003E73B7 /* Map.xcdatamodeld */; };
                B526258A25C874FA003E73B7 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526258925C874FA003E73B7 /* MapTests.swift */; };
                B526259525C874FA003E73B7 /* MapUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526259425C874FA003E73B7 /* MapUITests.swift */; };
                B526257625C874FA003E73B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257525C874FA003E73B7 /* Assets.xcassets */; };
                B526257925C874FA003E73B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257825C874FA003E73B7 /* Preview Assets.xcassets */; };
                B526257B25C874FA003E73B7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257A25C874FA003E73B7 /* Persistence.swift */; };
                B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B526257C25C874FA003E73B7 /* Map.xcdatamodeld */; };
                B526258A25C874FA003E73B7 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526258925C874FA003E73B7 /* MapTests.swift */; };
                B526259525C874FA003E73B7 /* MapUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526259425C874FA003E73B7 /* MapUITests.swift */; };
-               B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625A525C876C3003E73B7 /* MapDetail.swift */; };
+               B52625A625C876C3003E73B7 /* MapDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625A525C876C3003E73B7 /* MapDetailScreen.swift */; };
                B52625AB25C87909003E73B7 /* Date+format.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AA25C87909003E73B7 /* Date+format.swift */; };
                B52625AB25C87909003E73B7 /* Date+format.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AA25C87909003E73B7 /* Date+format.swift */; };
-               B52625B025C87C14003E73B7 /* MapRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AF25C87C14003E73B7 /* MapRender.swift */; };
+               B52625B025C87C14003E73B7 /* MapRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AF25C87C14003E73B7 /* MapRenderView.swift */; };
                B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625BA25C884C2003E73B7 /* Map+parse.swift */; };
                B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625C525C8BD2A003E73B7 /* Stage.swift */; };
                B539516C25CB0C9300959F72 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539516B25CB0C9200959F72 /* Store.swift */; };
                B539517425CB0CA400959F72 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539517325CB0CA400959F72 /* AppState.swift */; };
                B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539518025CB2D7A00959F72 /* MapTextEditor.swift */; };
                B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625BA25C884C2003E73B7 /* Map+parse.swift */; };
                B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625C525C8BD2A003E73B7 /* Stage.swift */; };
                B539516C25CB0C9300959F72 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539516B25CB0C9200959F72 /* Store.swift */; };
                B539517425CB0CA400959F72 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539517325CB0CA400959F72 /* AppState.swift */; };
                B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B539518025CB2D7A00959F72 /* MapTextEditor.swift */; };
-               B587BB6025CDCECB00F328ED /* SlowMapRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */; };
                B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */; };
                B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75CE25CC7965003BFF3D /* MapParser.swift */; };
                B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */; };
                B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */; };
                B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75CE25CC7965003BFF3D /* MapParser.swift */; };
                B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */; };
                B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */; };
                B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */; };
                B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75F625CC97CA003BFF3D /* Debouncer.swift */; };
                B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */; };
                B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */; };
                B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CF75F625CC97CA003BFF3D /* Debouncer.swift */; };
+               B5F8D3082A06DD8C000EEA24 /* Font+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */; };
+               B5F8D30B2A06E3E6000EEA24 /* Color+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */; };
+               B5F8D30E2A06E5C2000EEA24 /* Patterns in Frameworks */ = {isa = PBXBuildFile; productRef = B5F8D30D2A06E5C2000EEA24 /* Patterns */; };
+               B5F8D3102A07B33D000EEA24 /* NSColor+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */; };
+               B5F8D3122A07B690000EEA24 /* NoteParserStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */; };
+               B5F8D3142A07C05F000EEA24 /* MapNotes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8D3132A07C05F000EEA24 /* MapNotes.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXFileReference section */
                B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+writePNG.swift"; sourceTree = "<group>"; };
                B523C74525C9BD3500C44061 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
 /* Begin PBXFileReference section */
                B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+writePNG.swift"; sourceTree = "<group>"; };
                B523C74525C9BD3500C44061 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
-               B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultMapView.swift; sourceTree = "<group>"; };
+               B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyMapDetailScreen.swift; sourceTree = "<group>"; };
                B523C75925C9FD4900C44061 /* MapAxes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapAxes.swift; sourceTree = "<group>"; };
                B523C76125CA05A300C44061 /* MapStages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapStages.swift; sourceTree = "<group>"; };
                B523C76625CA071B00C44061 /* MapVertices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapVertices.swift; sourceTree = "<group>"; };
                B523C76B25CA0DFA00C44061 /* MapEdges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEdges.swift; sourceTree = "<group>"; };
                B523C77025CA121300C44061 /* MapBlockers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapBlockers.swift; sourceTree = "<group>"; };
                B523C75925C9FD4900C44061 /* MapAxes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapAxes.swift; sourceTree = "<group>"; };
                B523C76125CA05A300C44061 /* MapStages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapStages.swift; sourceTree = "<group>"; };
                B523C76625CA071B00C44061 /* MapVertices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapVertices.swift; sourceTree = "<group>"; };
                B523C76B25CA0DFA00C44061 /* MapEdges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEdges.swift; sourceTree = "<group>"; };
                B523C77025CA121300C44061 /* MapBlockers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapBlockers.swift; sourceTree = "<group>"; };
-               B523C77525CA181100C44061 /* MapColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapColor.swift; sourceTree = "<group>"; };
                B523C77D25CA294C00C44061 /* MapOpportunities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapOpportunities.swift; sourceTree = "<group>"; };
                B526256E25C874F9003E73B7 /* Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Map.app; sourceTree = BUILT_PRODUCTS_DIR; };
                B526257125C874F9003E73B7 /* MapApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapApp.swift; sourceTree = "<group>"; };
                B523C77D25CA294C00C44061 /* MapOpportunities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapOpportunities.swift; sourceTree = "<group>"; };
                B526256E25C874F9003E73B7 /* Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Map.app; sourceTree = BUILT_PRODUCTS_DIR; };
                B526257125C874F9003E73B7 /* MapApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapApp.swift; sourceTree = "<group>"; };
-               B526257325C874F9003E73B7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
+               B526257325C874F9003E73B7 /* MapEditorWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEditorWindow.swift; sourceTree = "<group>"; };
                B526257525C874FA003E73B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
                B526257825C874FA003E73B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
                B526257A25C874FA003E73B7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
                B526257525C874FA003E73B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
                B526257825C874FA003E73B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
                B526257A25C874FA003E73B7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
                B526259025C874FA003E73B7 /* MapUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
                B526259425C874FA003E73B7 /* MapUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUITests.swift; sourceTree = "<group>"; };
                B526259625C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
                B526259025C874FA003E73B7 /* MapUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
                B526259425C874FA003E73B7 /* MapUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUITests.swift; sourceTree = "<group>"; };
                B526259625C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-               B52625A525C876C3003E73B7 /* MapDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDetail.swift; sourceTree = "<group>"; };
+               B52625A525C876C3003E73B7 /* MapDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDetailScreen.swift; sourceTree = "<group>"; };
                B52625AA25C87909003E73B7 /* Date+format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+format.swift"; sourceTree = "<group>"; };
                B52625AA25C87909003E73B7 /* Date+format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+format.swift"; sourceTree = "<group>"; };
-               B52625AF25C87C14003E73B7 /* MapRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRender.swift; sourceTree = "<group>"; };
+               B52625AF25C87C14003E73B7 /* MapRenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRenderView.swift; sourceTree = "<group>"; };
                B52625BA25C884C2003E73B7 /* Map+parse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Map+parse.swift"; sourceTree = "<group>"; };
                B52625C525C8BD2A003E73B7 /* Stage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stage.swift; sourceTree = "<group>"; };
                B539516B25CB0C9200959F72 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
                B539517325CB0CA400959F72 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
                B539518025CB2D7A00959F72 /* MapTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTextEditor.swift; sourceTree = "<group>"; };
                B52625BA25C884C2003E73B7 /* Map+parse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Map+parse.swift"; sourceTree = "<group>"; };
                B52625C525C8BD2A003E73B7 /* Stage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stage.swift; sourceTree = "<group>"; };
                B539516B25CB0C9200959F72 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
                B539517325CB0CA400959F72 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
                B539518025CB2D7A00959F72 /* MapTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTextEditor.swift; sourceTree = "<group>"; };
-               B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlowMapRender.swift; sourceTree = "<group>"; };
                B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvolutionPicker.swift; sourceTree = "<group>"; };
                B5CF75CE25CC7965003BFF3D /* MapParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapParser.swift; sourceTree = "<group>"; };
                B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VertexParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvolutionPicker.swift; sourceTree = "<group>"; };
                B5CF75CE25CC7965003BFF3D /* MapParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapParser.swift; sourceTree = "<group>"; };
                B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VertexParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpportunityParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StageParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75F625CC97CA003BFF3D /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
                B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpportunityParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StageParserStrategy.swift; sourceTree = "<group>"; };
                B5CF75F625CC97CA003BFF3D /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
+               B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+Theme.swift"; sourceTree = "<group>"; };
+               B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Theme.swift"; sourceTree = "<group>"; };
+               B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Theme.swift"; sourceTree = "<group>"; };
+               B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteParserStrategy.swift; sourceTree = "<group>"; };
+               B5F8D3132A07C05F000EEA24 /* MapNotes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapNotes.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                        buildActionMask = 2147483647;
                        files = (
                                B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */,
                        buildActionMask = 2147483647;
                        files = (
                                B523C74625C9BD3500C44061 /* CloudKit.framework in Frameworks */,
+                               B5F8D30E2A06E5C2000EEA24 /* Patterns in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        name = Frameworks;
                        sourceTree = "<group>";
                };
                        name = Frameworks;
                        sourceTree = "<group>";
                };
-               B523C75825C9FD3A00C44061 /* MapRenderComponents */ = {
+               B523C75825C9FD3A00C44061 /* MapRender */ = {
                        isa = PBXGroup;
                        children = (
                                B523C75925C9FD4900C44061 /* MapAxes.swift */,
                                B523C76125CA05A300C44061 /* MapStages.swift */,
                                B523C76625CA071B00C44061 /* MapVertices.swift */,
                        isa = PBXGroup;
                        children = (
                                B523C75925C9FD4900C44061 /* MapAxes.swift */,
                                B523C76125CA05A300C44061 /* MapStages.swift */,
                                B523C76625CA071B00C44061 /* MapVertices.swift */,
+                               B5F8D3132A07C05F000EEA24 /* MapNotes.swift */,
                                B523C76B25CA0DFA00C44061 /* MapEdges.swift */,
                                B523C77025CA121300C44061 /* MapBlockers.swift */,
                                B523C77D25CA294C00C44061 /* MapOpportunities.swift */,
                        );
                                B523C76B25CA0DFA00C44061 /* MapEdges.swift */,
                                B523C77025CA121300C44061 /* MapBlockers.swift */,
                                B523C77D25CA294C00C44061 /* MapOpportunities.swift */,
                        );
-                       path = MapRenderComponents;
+                       path = MapRender;
                        sourceTree = "<group>";
                };
                B526256525C874F9003E73B7 = {
                        sourceTree = "<group>";
                };
                B526256525C874F9003E73B7 = {
                B526257025C874F9003E73B7 /* Map */ = {
                        isa = PBXGroup;
                        children = (
                B526257025C874F9003E73B7 /* Map */ = {
                        isa = PBXGroup;
                        children = (
-                               B523C77525CA181100C44061 /* MapColor.swift */,
-                               B5CF75CD25CC7953003BFF3D /* MapParser */,
-                               B539516A25CB0C7800959F72 /* State */,
-                               B52625B425C87D54003E73B7 /* Extensions */,
-                               B539517925CB0D6100959F72 /* Views */,
-                               B523C75825C9FD3A00C44061 /* MapRenderComponents */,
+                               B526257125C874F9003E73B7 /* MapApp.swift */,
+                               B5F8D2FE2A06DB3A000EEA24 /* Data */,
+                               B5F8D3092A06DE1A000EEA24 /* Logic */,
+                               B5F8D2FF2A06DB40000EEA24 /* Presentation */,
+                               B52625B425C87D54003E73B7 /* Core Extensions */,
                                B526257525C874FA003E73B7 /* Assets.xcassets */,
                                B526257F25C874FA003E73B7 /* Info.plist */,
                                B526258025C874FA003E73B7 /* Map.entitlements */,
                                B526257C25C874FA003E73B7 /* Map.xcdatamodeld */,
                                B526257725C874FA003E73B7 /* Preview Content */,
                                B526257525C874FA003E73B7 /* Assets.xcassets */,
                                B526257F25C874FA003E73B7 /* Info.plist */,
                                B526258025C874FA003E73B7 /* Map.entitlements */,
                                B526257C25C874FA003E73B7 /* Map.xcdatamodeld */,
                                B526257725C874FA003E73B7 /* Preview Content */,
-                               B5CF75F625CC97CA003BFF3D /* Debouncer.swift */,
                        );
                        path = Map;
                        sourceTree = "<group>";
                        );
                        path = Map;
                        sourceTree = "<group>";
                        path = MapUITests;
                        sourceTree = "<group>";
                };
                        path = MapUITests;
                        sourceTree = "<group>";
                };
-               B52625B425C87D54003E73B7 /* Extensions */ = {
+               B52625B425C87D54003E73B7 /* Core Extensions */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
-                               B52625BA25C884C2003E73B7 /* Map+parse.swift */,
                                B52625AA25C87909003E73B7 /* Date+format.swift */,
                                B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */,
                                B52625AA25C87909003E73B7 /* Date+format.swift */,
                                B523C73C25C98D9800C44061 /* NSImage+writePNG.swift */,
+                               B5F8D3072A06DD8C000EEA24 /* Font+Theme.swift */,
+                               B5F8D30F2A07B33D000EEA24 /* NSColor+Theme.swift */,
+                               B5F8D30A2A06E3E6000EEA24 /* Color+Theme.swift */,
                        );
                        );
-                       path = Extensions;
+                       path = "Core Extensions";
                        sourceTree = "<group>";
                };
                        sourceTree = "<group>";
                };
-               B539516A25CB0C7800959F72 /* State */ = {
+               B5CF75CD25CC7953003BFF3D /* MapParser */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
+                               B5CF75D625CC79A4003BFF3D /* Strategies */,
+                               B5CF75CE25CC7965003BFF3D /* MapParser.swift */,
+                       );
+                       path = MapParser;
+                       sourceTree = "<group>";
+               };
+               B5CF75D625CC79A4003BFF3D /* Strategies */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */,
+                               B5F8D3112A07B690000EEA24 /* NoteParserStrategy.swift */,
+                               B5CF75DC25CC79D7003BFF3D /* EdgeParserStrategy.swift */,
+                               B5CF75E125CC79ED003BFF3D /* BlockerParserStrategy.swift */,
+                               B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */,
+                               B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */,
+                       );
+                       path = Strategies;
+                       sourceTree = "<group>";
+               };
+               B5F8D2FE2A06DB3A000EEA24 /* Data */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5F8D3012A06DB75000EEA24 /* Models */,
                                B52625C525C8BD2A003E73B7 /* Stage.swift */,
                                B526257A25C874FA003E73B7 /* Persistence.swift */,
                                B539516B25CB0C9200959F72 /* Store.swift */,
                                B539517325CB0CA400959F72 /* AppState.swift */,
                        );
                                B52625C525C8BD2A003E73B7 /* Stage.swift */,
                                B526257A25C874FA003E73B7 /* Persistence.swift */,
                                B539516B25CB0C9200959F72 /* Store.swift */,
                                B539517325CB0CA400959F72 /* AppState.swift */,
                        );
-                       path = State;
+                       path = Data;
                        sourceTree = "<group>";
                };
                        sourceTree = "<group>";
                };
-               B539517925CB0D6100959F72 /* Views */ = {
+               B5F8D2FF2A06DB40000EEA24 /* Presentation */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5F8D3022A06DBC3000EEA24 /* Windows */,
+                               B5F8D3032A06DC2D000EEA24 /* Screens */,
+                               B5F8D3052A06DCF3000EEA24 /* Complex Components */,
+                               B5F8D3042A06DCC4000EEA24 /* Base Components */,
+                       );
+                       path = Presentation;
+                       sourceTree = "<group>";
+               };
+               B5F8D3012A06DB75000EEA24 /* Models */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B52625BA25C884C2003E73B7 /* Map+parse.swift */,
+                       );
+                       path = Models;
+                       sourceTree = "<group>";
+               };
+               B5F8D3022A06DBC3000EEA24 /* Windows */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B526257325C874F9003E73B7 /* MapEditorWindow.swift */,
+                       );
+                       path = Windows;
+                       sourceTree = "<group>";
+               };
+               B5F8D3032A06DC2D000EEA24 /* Screens */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B523C74A25C9C1BA00C44061 /* EmptyMapDetailScreen.swift */,
+                               B52625A525C876C3003E73B7 /* MapDetailScreen.swift */,
+                       );
+                       path = Screens;
+                       sourceTree = "<group>";
+               };
+               B5F8D3042A06DCC4000EEA24 /* Base Components */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
-                               B526257125C874F9003E73B7 /* MapApp.swift */,
-                               B526257325C874F9003E73B7 /* ContentView.swift */,
-                               B52625A525C876C3003E73B7 /* MapDetail.swift */,
-                               B523C74A25C9C1BA00C44061 /* DefaultMapView.swift */,
-                               B539518025CB2D7A00959F72 /* MapTextEditor.swift */,
-                               B587BB5F25CDCECB00F328ED /* SlowMapRender.swift */,
-                               B52625AF25C87C14003E73B7 /* MapRender.swift */,
                                B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */,
                                B5CF75C825CC19FC003BFF3D /* EvolutionPicker.swift */,
+                               B539518025CB2D7A00959F72 /* MapTextEditor.swift */,
+                               B523C75825C9FD3A00C44061 /* MapRender */,
                        );
                        );
-                       path = Views;
+                       path = "Base Components";
                        sourceTree = "<group>";
                };
                        sourceTree = "<group>";
                };
-               B5CF75CD25CC7953003BFF3D /* MapParser */ = {
+               B5F8D3052A06DCF3000EEA24 /* Complex Components */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
-                               B5CF75D625CC79A4003BFF3D /* Strategies */,
-                               B5CF75CE25CC7965003BFF3D /* MapParser.swift */,
+                               B5F8D3062A06DD2A000EEA24 /* MapRender */,
                        );
                        );
-                       path = MapParser;
+                       path = "Complex Components";
                        sourceTree = "<group>";
                };
                        sourceTree = "<group>";
                };
-               B5CF75D625CC79A4003BFF3D /* Strategies */ = {
+               B5F8D3062A06DD2A000EEA24 /* MapRender */ = {
                        isa = PBXGroup;
                        children = (
                        isa = PBXGroup;
                        children = (
-                               B5CF75D725CC79BC003BFF3D /* VertexParserStrategy.swift */,
-                               B5CF75DC25CC79D7003BFF3D /* EdgeParserStrategy.swift */,
-                               B5CF75E125CC79ED003BFF3D /* BlockerParserStrategy.swift */,
-                               B5CF75E925CC7A13003BFF3D /* OpportunityParserStrategy.swift */,
-                               B5CF75EE25CC7A4A003BFF3D /* StageParserStrategy.swift */,
+                               B52625AF25C87C14003E73B7 /* MapRenderView.swift */,
                        );
                        );
-                       path = Strategies;
+                       path = MapRender;
+                       sourceTree = "<group>";
+               };
+               B5F8D3092A06DE1A000EEA24 /* Logic */ = {
+                       isa = PBXGroup;
+                       children = (
+                               B5CF75CD25CC7953003BFF3D /* MapParser */,
+                               B5CF75F625CC97CA003BFF3D /* Debouncer.swift */,
+                       );
+                       path = Logic;
                        sourceTree = "<group>";
                };
 /* End PBXGroup section */
                        sourceTree = "<group>";
                };
 /* End PBXGroup section */
                        dependencies = (
                        );
                        name = Map;
                        dependencies = (
                        );
                        name = Map;
+                       packageProductDependencies = (
+                               B5F8D30D2A06E5C2000EEA24 /* Patterns */,
+                       );
                        productName = Map;
                        productReference = B526256E25C874F9003E73B7 /* Map.app */;
                        productType = "com.apple.product-type.application";
                        productName = Map;
                        productReference = B526256E25C874F9003E73B7 /* Map.app */;
                        productType = "com.apple.product-type.application";
                B526256625C874F9003E73B7 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
                B526256625C874F9003E73B7 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
+                               BuildIndependentTargetsInParallel = YES;
                                LastSwiftUpdateCheck = 1240;
                                LastSwiftUpdateCheck = 1240;
-                               LastUpgradeCheck = 1240;
+                               LastUpgradeCheck = 1430;
                                TargetAttributes = {
                                        B526256D25C874F9003E73B7 = {
                                                CreatedOnToolsVersion = 12.4;
                                TargetAttributes = {
                                        B526256D25C874F9003E73B7 = {
                                                CreatedOnToolsVersion = 12.4;
                                Base,
                        );
                        mainGroup = B526256525C874F9003E73B7;
                                Base,
                        );
                        mainGroup = B526256525C874F9003E73B7;
+                       packageReferences = (
+                               B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */,
+                       );
                        productRefGroup = B526256F25C874F9003E73B7 /* Products */;
                        projectDirPath = "";
                        projectRoot = "";
                        productRefGroup = B526256F25C874F9003E73B7 /* Products */;
                        projectDirPath = "";
                        projectRoot = "";
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               B52625B025C87C14003E73B7 /* MapRender.swift in Sources */,
+                               B52625B025C87C14003E73B7 /* MapRenderView.swift in Sources */,
                                B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */,
                                B52625AB25C87909003E73B7 /* Date+format.swift in Sources */,
                                B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */,
                                B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */,
                                B523C77E25CA294C00C44061 /* MapOpportunities.swift in Sources */,
                                B52625AB25C87909003E73B7 /* Date+format.swift in Sources */,
                                B5CF75D825CC79BC003BFF3D /* VertexParserStrategy.swift in Sources */,
                                B5CF75EA25CC7A13003BFF3D /* OpportunityParserStrategy.swift in Sources */,
+                               B5F8D3142A07C05F000EEA24 /* MapNotes.swift in Sources */,
                                B523C77125CA121300C44061 /* MapBlockers.swift in Sources */,
                                B523C76725CA071B00C44061 /* MapVertices.swift in Sources */,
                                B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */,
                                B5CF75E225CC79ED003BFF3D /* BlockerParserStrategy.swift in Sources */,
                                B523C77125CA121300C44061 /* MapBlockers.swift in Sources */,
                                B523C76725CA071B00C44061 /* MapVertices.swift in Sources */,
                                B5CF75CF25CC7965003BFF3D /* MapParser.swift in Sources */,
                                B5CF75E225CC79ED003BFF3D /* BlockerParserStrategy.swift in Sources */,
+                               B5F8D3102A07B33D000EEA24 /* NSColor+Theme.swift in Sources */,
+                               B5F8D30B2A06E3E6000EEA24 /* Color+Theme.swift in Sources */,
                                B539517425CB0CA400959F72 /* AppState.swift in Sources */,
                                B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */,
                                B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */,
                                B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */,
                                B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */,
                                B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */,
                                B539517425CB0CA400959F72 /* AppState.swift in Sources */,
                                B523C75A25C9FD4900C44061 /* MapAxes.swift in Sources */,
                                B539518125CB2D7A00959F72 /* MapTextEditor.swift in Sources */,
                                B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */,
                                B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */,
                                B523C73D25C98D9800C44061 /* NSImage+writePNG.swift in Sources */,
+                               B5F8D3122A07B690000EEA24 /* NoteParserStrategy.swift in Sources */,
                                B526257B25C874FA003E73B7 /* Persistence.swift in Sources */,
                                B526257B25C874FA003E73B7 /* Persistence.swift in Sources */,
+                               B5F8D3082A06DD8C000EEA24 /* Font+Theme.swift in Sources */,
                                B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */,
                                B5CF75EF25CC7A4A003BFF3D /* StageParserStrategy.swift in Sources */,
-                               B523C77625CA181100C44061 /* MapColor.swift in Sources */,
                                B5CF75DD25CC79D7003BFF3D /* EdgeParserStrategy.swift in Sources */,
                                B5CF75DD25CC79D7003BFF3D /* EdgeParserStrategy.swift in Sources */,
-                               B526257425C874F9003E73B7 /* ContentView.swift in Sources */,
+                               B526257425C874F9003E73B7 /* MapEditorWindow.swift in Sources */,
                                B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */,
                                B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */,
                                B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */,
                                B5CF75C925CC19FC003BFF3D /* EvolutionPicker.swift in Sources */,
-                               B523C74B25C9C1BA00C44061 /* DefaultMapView.swift in Sources */,
+                               B523C74B25C9C1BA00C44061 /* EmptyMapDetailScreen.swift in Sources */,
                                B539516C25CB0C9300959F72 /* Store.swift in Sources */,
                                B526257225C874F9003E73B7 /* MapApp.swift in Sources */,
                                B539516C25CB0C9300959F72 /* Store.swift in Sources */,
                                B526257225C874F9003E73B7 /* MapApp.swift in Sources */,
-                               B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */,
+                               B52625A625C876C3003E73B7 /* MapDetailScreen.swift in Sources */,
                                B523C76225CA05A300C44061 /* MapStages.swift in Sources */,
                                B523C76225CA05A300C44061 /* MapStages.swift in Sources */,
-                               B587BB6025CDCECB00F328ED /* SlowMapRender.swift in Sources */,
                                B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */,
                                B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */,
                        );
                                B5CF75F725CC97CA003BFF3D /* Debouncer.swift in Sources */,
                                B523C76C25CA0DFA00C44061 /* MapEdges.swift in Sources */,
                        );
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                ENABLE_TESTABILITY = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                ENABLE_TESTABILITY = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                                COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
-                               CURRENT_PROJECT_VERSION = 3;
+                               CURRENT_PROJECT_VERSION = 4;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                ENABLE_HARDENED_RUNTIME = YES;
                                DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                ENABLE_HARDENED_RUNTIME = YES;
                                        "$(inherited)",
                                        "@executable_path/../Frameworks",
                                );
                                        "$(inherited)",
                                        "@executable_path/../Frameworks",
                                );
-                               MACOSX_DEPLOYMENT_TARGET = 11;
-                               MARKETING_VERSION = 1.2.0;
+                               MACOSX_DEPLOYMENT_TARGET = 12.0;
+                               MARKETING_VERSION = 2.0.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_VERSION = 5.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_VERSION = 5.0;
                                CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
-                               CURRENT_PROJECT_VERSION = 3;
+                               CURRENT_PROJECT_VERSION = 4;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                ENABLE_HARDENED_RUNTIME = YES;
                                DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\"";
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                ENABLE_HARDENED_RUNTIME = YES;
                                        "$(inherited)",
                                        "@executable_path/../Frameworks",
                                );
                                        "$(inherited)",
                                        "@executable_path/../Frameworks",
                                );
-                               MACOSX_DEPLOYMENT_TARGET = 11;
-                               MARKETING_VERSION = 1.2.0;
+                               MACOSX_DEPLOYMENT_TARGET = 12.0;
+                               MARKETING_VERSION = 2.0.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_VERSION = 5.0;
                                PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SWIFT_VERSION = 5.0;
                                BUNDLE_LOADER = "$(TEST_HOST)";
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                BUNDLE_LOADER = "$(TEST_HOST)";
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapTests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapTests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                BUNDLE_LOADER = "$(TEST_HOST)";
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                BUNDLE_LOADER = "$(TEST_HOST)";
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapTests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapTests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapUITests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapUITests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
                                ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
                                CODE_SIGN_STYLE = Automatic;
                                COMBINE_HIDPI_IMAGES = YES;
+                               DEAD_CODE_STRIPPING = YES;
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapUITests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                                DEVELOPMENT_TEAM = S68NHQVJXW;
                                INFOPLIST_FILE = MapUITests/Info.plist;
                                LD_RUNPATH_SEARCH_PATHS = (
                };
 /* End XCConfigurationList section */
 
                };
 /* End XCConfigurationList section */
 
+/* Begin XCRemoteSwiftPackageReference section */
+               B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */ = {
+                       isa = XCRemoteSwiftPackageReference;
+                       repositoryURL = "https://git.sr.ht/~rbdr/patterns";
+                       requirement = {
+                               kind = upToNextMajorVersion;
+                               minimumVersion = 2.0.0;
+                       };
+               };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+               B5F8D30D2A06E5C2000EEA24 /* Patterns */ = {
+                       isa = XCSwiftPackageProductDependency;
+                       package = B5F8D30C2A06E5C2000EEA24 /* XCRemoteSwiftPackageReference "patterns" */;
+                       productName = Patterns;
+               };
+/* End XCSwiftPackageProductDependency section */
+
 /* Begin XCVersionGroup section */
                B526257C25C874FA003E73B7 /* Map.xcdatamodeld */ = {
                        isa = XCVersionGroup;
 /* Begin XCVersionGroup section */
                B526257C25C874FA003E73B7 /* Map.xcdatamodeld */ = {
                        isa = XCVersionGroup;
diff --git a/Map.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Map.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644 (file)
index 0000000..9606e6c
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "pins" : [
+    {
+      "identity" : "patterns",
+      "kind" : "remoteSourceControl",
+      "location" : "https://git.sr.ht/~rbdr/patterns",
+      "state" : {
+        "revision" : "ba4ee0edf2aba19ad73fa53cb01dd0fb9b527526",
+        "version" : "2.0.0"
+      }
+    }
+  ],
+  "version" : 2
+}
index c981db70309e4ea0ef77fc1200264bfe44531e87..b90aa6c8521864825d20181884d4ba7bdfadbb4e 100644 (file)
@@ -5,9 +5,9 @@
         "color-space" : "srgb",
         "components" : {
           "alpha" : "1.000",
         "color-space" : "srgb",
         "components" : {
           "alpha" : "1.000",
-          "blue" : "0.969",
-          "green" : "0.871",
-          "red" : "1.000"
+          "blue" : "0xF0",
+          "green" : "0xB3",
+          "red" : "0xFF"
         }
       },
       "idiom" : "universal"
         }
       },
       "idiom" : "universal"
index b4618d40feab2d02a4665d2c6ed8a2ee68e4d3fc..b5ff9833789a4dfeb529c26edfb0cc04c6db01b2 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_128x128.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ
index dfdf3965b8a3b8a0a13b8c6089efe88f0b5ddc5f..109328a5fe660950fd5eadd0b336e1a6dbf5cf59 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ
index fb22da2d950d5d15d5f684981adab0f7ca6f159f..31acec71642a71ab8c0fb832162bffa87e76d1cb 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_16x16.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ
index a2ea18463b2859eab9c20bc7aae06d2bfd3bd7fe..d9a0db3777e65bc15a4bc4902462400700c8c5ae 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ
index cbe1cfc717c7c609fd31fc7ca4a200c3b3f64dac..b6a0286756b06856b52bff250b7b0e1b53adf495 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_256x256.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ
index 64bc6b587b4fbd63730e5e9400b015872d97a28d..d674c53be490d680e45b3c4d82da6410e92add44 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ
index a2ea18463b2859eab9c20bc7aae06d2bfd3bd7fe..d9a0db3777e65bc15a4bc4902462400700c8c5ae 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_32x32.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ
index 5663b5e0ac88020ecab1cd565a874202fa12327b..650d9ff696e29828459bb89b4ef4f1f9e93f7843 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ
index 64bc6b587b4fbd63730e5e9400b015872d97a28d..d674c53be490d680e45b3c4d82da6410e92add44 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_512x512.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ
index bfa436588ad080cfbbd56eff2336ce20078011c5..5b5cdd6e471afcdb6cc8817ac35771c956f85a32 100644 (file)
Binary files a/Map/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png and b/Map/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ
diff --git a/Map/Assets.xcassets/Colors/Background.colorset/Contents.json b/Map/Assets.xcassets/Colors/Background.colorset/Contents.json
new file mode 100644 (file)
index 0000000..d55bcdc
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "1.000",
+          "green" : "1.000",
+          "red" : "1.000"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x1F",
+          "green" : "0x26",
+          "red" : "0x0F"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Contents.json b/Map/Assets.xcassets/Colors/Contents.json
new file mode 100644 (file)
index 0000000..73c0059
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Foreground.colorset/Contents.json b/Map/Assets.xcassets/Colors/Foreground.colorset/Contents.json
new file mode 100644 (file)
index 0000000..6a9d7a1
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x1F",
+          "green" : "0x26",
+          "red" : "0x0F"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "1.000",
+          "green" : "1.000",
+          "red" : "1.000"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Syntax/Contents.json b/Map/Assets.xcassets/Colors/Syntax/Contents.json
new file mode 100644 (file)
index 0000000..73c0059
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Syntax/Number.colorset/Contents.json b/Map/Assets.xcassets/Colors/Syntax/Number.colorset/Contents.json
new file mode 100644 (file)
index 0000000..890992f
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.902",
+          "green" : "0.561",
+          "red" : "0.310"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Syntax/Option.colorset/Contents.json b/Map/Assets.xcassets/Colors/Syntax/Option.colorset/Contents.json
new file mode 100644 (file)
index 0000000..bad8227
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x00",
+          "green" : "0x2B",
+          "red" : "0xFA"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Syntax/Symbol.colorset/Contents.json b/Map/Assets.xcassets/Colors/Syntax/Symbol.colorset/Contents.json
new file mode 100644 (file)
index 0000000..8bea0d3
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xA2",
+          "green" : "0xA6",
+          "red" : "0x90"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Syntax/Vertex.colorset/Contents.json b/Map/Assets.xcassets/Colors/Syntax/Vertex.colorset/Contents.json
new file mode 100644 (file)
index 0000000..2aa2733
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.474",
+          "green" : "0.671",
+          "red" : "0.180"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xCC",
+          "green" : "0xFF",
+          "red" : "0x80"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Contents.json b/Map/Assets.xcassets/Colors/Theme/Contents.json
new file mode 100644 (file)
index 0000000..73c0059
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Dark Neutral Gray.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Dark Neutral Gray.colorset/Contents.json
new file mode 100644 (file)
index 0000000..8bea0d3
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xA2",
+          "green" : "0xA6",
+          "red" : "0x90"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Dark Slate.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Dark Slate.colorset/Contents.json
new file mode 100644 (file)
index 0000000..1d36938
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x1F",
+          "green" : "0x26",
+          "red" : "0x0F"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Jasper Red.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Jasper Red.colorset/Contents.json
new file mode 100644 (file)
index 0000000..bad8227
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0x00",
+          "green" : "0x2B",
+          "red" : "0xFA"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Light Neutral Gray.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Light Neutral Gray.colorset/Contents.json
new file mode 100644 (file)
index 0000000..f6a9b18
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xE3",
+          "green" : "0xE6",
+          "red" : "0xDA"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Neutral Gray.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Neutral Gray.colorset/Contents.json
new file mode 100644 (file)
index 0000000..2e346d1
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xCC",
+          "green" : "0xD1",
+          "red" : "0xB5"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Assets.xcassets/Colors/Theme/Olympic Blue.colorset/Contents.json b/Map/Assets.xcassets/Colors/Theme/Olympic Blue.colorset/Contents.json
new file mode 100644 (file)
index 0000000..9e62414
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0xE6",
+          "green" : "0x8F",
+          "red" : "0x4F"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/Map/Core Extensions/Color+Theme.swift b/Map/Core Extensions/Color+Theme.swift
new file mode 100644 (file)
index 0000000..ffbd224
--- /dev/null
@@ -0,0 +1,27 @@
+import SwiftUI
+
+extension Color {
+  struct theme {
+    static let darkSlate = Color("Dark Slate")
+    static let jasperRed = Color("Jasper Red")
+    static let olympicBlue = Color("Olympic Blue")
+    static let neutralGray = Color("Neutral Gray")
+    static let lightNeutralGray = Color("Light Neutral Gray")
+    static let darkNeutralGray = Color("Dark Neutral Gray")
+  }
+  
+  struct map {
+    static let labelColor = Color.theme.darkSlate
+    static let axisColor = Color.theme.darkSlate
+    static let vertexColor = Color.theme.darkSlate
+    static let blockerColor = Color.theme.jasperRed
+    static let opportunityColor = Color.theme.olympicBlue
+    static let stageForeground = Color.theme.lightNeutralGray
+    static let stageBackground = Color.white
+  }
+  
+  struct ui {
+    static let foreground = Color("Foreground")
+    static let background = Color("Background")
+  }
+}
diff --git a/Map/Core Extensions/Font+Theme.swift b/Map/Core Extensions/Font+Theme.swift
new file mode 100644 (file)
index 0000000..30bc6f9
--- /dev/null
@@ -0,0 +1,8 @@
+import SwiftUI
+
+public extension Font {
+  struct theme {
+    static let axisLabel = Font.custom("Hiragino Mincho ProN", size: 14)
+    static let vertexLabel = Font.custom("Hiragino Mincho ProN", size: 12)
+  }
+}
diff --git a/Map/Core Extensions/NSColor+Theme.swift b/Map/Core Extensions/NSColor+Theme.swift
new file mode 100644 (file)
index 0000000..0ef1094
--- /dev/null
@@ -0,0 +1,10 @@
+import AppKit
+
+extension NSColor {
+  struct syntax {
+    static let vertex = NSColor(named: "Vertex") ?? .textColor
+    static let number = NSColor(named: "Number") ?? .textColor
+    static let option = NSColor(named: "Option") ?? .textColor
+    static let symbol = NSColor(named: "Symbol") ?? .textColor
+  }
+}
similarity index 94%
rename from Map/State/AppState.swift
rename to Map/Data/AppState.swift
index c261725fbae1eb22c893f9d8c886489ab0febab5..d0d5670645dc74d901da41b7e48bafbf5a3eedaa 100644 (file)
@@ -39,7 +39,7 @@ func appStateReducer(state: inout AppState, action: AppAction) {
 
     let renderView = MapRenderView(
       content: Binding.constant(map.content ?? ""),
 
     let renderView = MapRenderView(
       content: Binding.constant(map.content ?? ""),
-      evolution: Binding.constant(Stage.stages(state.selectedEvolution)))
+      evolution: Binding.constant(state.selectedEvolution))
 
     let view = NSHostingView(rootView: renderView)
     window.contentView = view
 
     let view = NSHostingView(rootView: renderView)
     window.contentView = view
@@ -54,7 +54,7 @@ func appStateReducer(state: inout AppState, action: AppAction) {
     dialog.showsResizeIndicator = false
     dialog.canCreateDirectories = true
     dialog.showsHiddenFiles = false
     dialog.showsResizeIndicator = false
     dialog.canCreateDirectories = true
     dialog.showsHiddenFiles = false
-    dialog.allowedFileTypes = ["png"]
+    dialog.allowedContentTypes = [.png]
     dialog.nameFieldStringValue = map.title ?? "Untitled Map"
 
     if dialog.runModal() == NSApplication.ModalResponse.OK {
     dialog.nameFieldStringValue = map.title ?? "Untitled Map"
 
     if dialog.runModal() == NSApplication.ModalResponse.OK {
@@ -77,7 +77,7 @@ func appStateReducer(state: inout AppState, action: AppAction) {
     dialog.showsResizeIndicator = false
     dialog.canCreateDirectories = true
     dialog.showsHiddenFiles = false
     dialog.showsResizeIndicator = false
     dialog.canCreateDirectories = true
     dialog.showsHiddenFiles = false
-    dialog.allowedFileTypes = ["txt"]
+    dialog.allowedContentTypes = [.text]
     dialog.nameFieldStringValue = map.title ?? "Untitled Map"
 
     if let content = map.content {
     dialog.nameFieldStringValue = map.title ?? "Untitled Map"
 
     if let content = map.content {
similarity index 94%
rename from Map/Extensions/Map+parse.swift
rename to Map/Data/Models/Map+parse.swift
index 904f492cb49a063b0726b144b39c9caf305c375e..5181daf9811a4aa41c0e0181c82cdd340a365d56 100644 (file)
@@ -2,6 +2,7 @@ extension Map {
   static func parse(content: String) -> ParsedMap {
 
     let parsers = [
   static func parse(content: String) -> ParsedMap {
 
     let parsers = [
+      AnyMapParserStrategy(NoteParserStrategy()),
       AnyMapParserStrategy(VertexParserStrategy()),
       AnyMapParserStrategy(EdgeParserStrategy()),
       AnyMapParserStrategy(BlockerParserStrategy()),
       AnyMapParserStrategy(VertexParserStrategy()),
       AnyMapParserStrategy(EdgeParserStrategy()),
       AnyMapParserStrategy(BlockerParserStrategy()),
similarity index 100%
rename from Map/State/Stage.swift
rename to Map/Data/Stage.swift
similarity index 100%
rename from Map/State/Store.swift
rename to Map/Data/Store.swift
similarity index 80%
rename from Map/MapParser/MapParser.swift
rename to Map/Logic/MapParser/MapParser.swift
index cce62514d8b29ff9303a759e12dccc5c1aed05a6..5f78d5d46b43887a88d8abead935feb5c3ac98d8 100644 (file)
@@ -6,14 +6,17 @@ import Foundation
 struct MapParsingPatterns {
   static let vertex = try! NSRegularExpression(
     pattern:
 struct MapParsingPatterns {
   static let vertex = try! NSRegularExpression(
     pattern:
-      "([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?",
+      "([^\\(\\[\\]]*?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?",
     options: .caseInsensitive)
   static let edge = try! NSRegularExpression(
     pattern: "(.+?)[\\s]*-([->])[\\s]*(.+)", options: .caseInsensitive)
   static let blocker = try! NSRegularExpression(
     pattern: "\\[(Blocker)\\][\\s]*(.+)", options: .caseInsensitive)
   static let opportunity = try! NSRegularExpression(
     options: .caseInsensitive)
   static let edge = try! NSRegularExpression(
     pattern: "(.+?)[\\s]*-([->])[\\s]*(.+)", options: .caseInsensitive)
   static let blocker = try! NSRegularExpression(
     pattern: "\\[(Blocker)\\][\\s]*(.+)", options: .caseInsensitive)
   static let opportunity = try! NSRegularExpression(
-    pattern: "\\[(Opportunity)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)",
+    pattern: "\\[(Evolution)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)",
+    options: .caseInsensitive)
+  static let note = try! NSRegularExpression(
+    pattern: "\\[(Note)\\][\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(.*)",
     options: .caseInsensitive)
   static let stage = try! NSRegularExpression(
     pattern: "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive)
     options: .caseInsensitive)
   static let stage = try! NSRegularExpression(
     pattern: "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive)
@@ -24,10 +27,11 @@ struct ParsedMap {
   let edges: [MapEdge]
   let blockers: [Blocker]
   let opportunities: [Opportunity]
   let edges: [MapEdge]
   let blockers: [Blocker]
   let opportunities: [Opportunity]
+  let notes: [Note]
   let stages: [CGFloat]
 
   static let empty: ParsedMap = ParsedMap(
   let stages: [CGFloat]
 
   static let empty: ParsedMap = ParsedMap(
-    vertices: [], edges: [], blockers: [], opportunities: [], stages: defaultDimensions)
+    vertices: [], edges: [], blockers: [], opportunities: [], notes: [], stages: defaultDimensions)
 }
 
 struct Vertex {
 }
 
 struct Vertex {
@@ -37,6 +41,12 @@ struct Vertex {
   var shape: VertexShape = .circle
 }
 
   var shape: VertexShape = .circle
 }
 
+struct Note {
+  let id: Int
+  let position: CGPoint
+  let text: String
+}
+
 enum VertexShape: String {
   case circle
   case square
 enum VertexShape: String {
   case circle
   case square
@@ -103,6 +113,7 @@ class MapBuilder {
   private var edges: [MapEdge] = []
   private var blockers: [Blocker] = []
   private var opportunities: [Opportunity] = []
   private var edges: [MapEdge] = []
   private var blockers: [Blocker] = []
   private var opportunities: [Opportunity] = []
+  private var notes: [Note] = []
   private var stages: [CGFloat] = defaultDimensions
 
   func addObjectToMap(type: Any.Type, object: Any) {
   private var stages: [CGFloat] = defaultDimensions
 
   func addObjectToMap(type: Any.Type, object: Any) {
@@ -125,18 +136,22 @@ class MapBuilder {
       let opportunity = object as! Opportunity
       opportunities.append(opportunity)
     }
       let opportunity = object as! Opportunity
       opportunities.append(opportunity)
     }
+    
+    if type == Note.self {
+      let note = object as! Note
+      notes.append(note)
+    }
 
     if type == StageDimensions.self {
       let stageDimensions = object as! StageDimensions
       stages[stageDimensions.index] = stageDimensions.dimensions
 
     if type == StageDimensions.self {
       let stageDimensions = object as! StageDimensions
       stages[stageDimensions.index] = stageDimensions.dimensions
-
     }
   }
 
   func build() -> ParsedMap {
     let mappedVertices = vertices.map { label, vertex in return vertex }
     return ParsedMap(
     }
   }
 
   func build() -> ParsedMap {
     let mappedVertices = vertices.map { label, vertex in return vertex }
     return ParsedMap(
-      vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities,
+      vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities, notes: notes,
       stages: stages)
   }
 }
       stages: stages)
   }
 }
diff --git a/Map/Logic/MapParser/Strategies/NoteParserStrategy.swift b/Map/Logic/MapParser/Strategies/NoteParserStrategy.swift
new file mode 100644 (file)
index 0000000..4dae4c7
--- /dev/null
@@ -0,0 +1,31 @@
+import Foundation
+
+struct NoteParserStrategy: MapParserStrategy {
+  private let regex = MapParsingPatterns.note
+
+  func canHandle(line: String) -> Bool {
+    let range = NSRange(location: 0, length: line.utf16.count)
+    let matches = regex.matches(in: String(line), options: [], range: range)
+    return matches.count > 0 && matches[0].numberOfRanges == 5
+  }
+
+  func handle(index: Int, line: String, vertices: [String: Vertex]) -> (Any.Type, Any) {
+    let range = NSRange(location: 0, length: line.utf16.count)
+    let matches = regex.matches(in: String(line), options: [], range: range)
+
+    let match = matches[0]
+    let text = String(line[Range(match.range(at: 4), in: line)!])
+    let xString = String(line[Range(match.range(at: 2), in: line)!])
+    let yString = String(line[Range(match.range(at: 3), in: line)!])
+    let x = CGFloat(truncating: NumberFormatter().number(from: xString) ?? 0.0)
+    let y = CGFloat(truncating: NumberFormatter().number(from: yString) ?? 0.0)
+
+    let note = Note(
+      id: index,
+      position: CGPoint(x: x, y: y),
+      text: text
+    )
+
+    return (Note.self, note)
+  }
+}
similarity index 95%
rename from Map/Views/MapApp.swift
rename to Map/MapApp.swift
index a0e2d60fc0a7fb52d9fd72307558b31ca67dfdfd..14e5605792713e070c0a57a4790d6a79d20d0395 100644 (file)
@@ -13,7 +13,7 @@ struct MapApp: App {
 
   var body: some Scene {
     WindowGroup {
 
   var body: some Scene {
     WindowGroup {
-      ContentView()
+      MapEditorWindow()
         .environment(\.managedObjectContext, persistenceController.container.viewContext)
         .environmentObject(AppStore(initialState: AppState(), reducer: appStateReducer))
     }.windowStyle(HiddenTitleBarWindowStyle()).commands {
         .environment(\.managedObjectContext, persistenceController.container.viewContext)
         .environmentObject(AppStore(initialState: AppState(), reducer: appStateReducer))
     }.windowStyle(HiddenTitleBarWindowStyle()).commands {
diff --git a/Map/MapColor.swift b/Map/MapColor.swift
deleted file mode 100644 (file)
index 98dd804..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-import SwiftUI
-
-struct MapColor {
-  let foreground: Color
-  let background: Color
-  let secondary: Color
-  let blocker: Color
-  let opportunity: Color
-  let stages: StageColor
-  let syntax: SyntaxColor
-
-  static func colorForScheme(_ colorScheme: ColorScheme) -> MapColor {
-    if colorScheme == .dark {
-      return MapColor(
-        foreground: Color.white,
-        background: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
-        secondary: Color(.sRGB, red: 0.81, green: 0.78, blue: 0.79),
-        blocker: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
-        opportunity: Color(.sRGB, red: 1.0, green: 0.37, blue: 0.34),
-        stages: StageColor(
-          i: Color(.sRGB, red: 0.37, green: 0.16, blue: 0.25),
-          ii: Color(.sRGB, red: 0.30, green: 0.29, blue: 0.26),
-          iii: Color(.sRGB, red: 0.15, green: 0.29, blue: 0.23),
-          iv: Color(.sRGB, red: 0.14, green: 0.22, blue: 0.31)),
-        syntax: SyntaxColor(
-          vertex: NSColor(srgbRed: 0.41, green: 0.84, blue: 0.96, alpha: 1.0),
-          number: NSColor(srgbRed: 0.85, green: 0.78, blue: 0.49, alpha: 1.0),
-          option: NSColor(srgbRed: 1.0, green: 0.48, blue: 0.7, alpha: 1.0),  // #FE7AB3
-          symbol: NSColor(srgbRed: 0.85, green: 0.73, blue: 1.0, alpha: 1.0)  // #DABBFF
-        ))
-    } else {
-      return MapColor(
-        foreground: Color(.sRGB, red: 0.13, green: 0.13, blue: 0.13),
-        background: Color.white,
-        secondary: Color.gray,
-        blocker: Color(.sRGB, red: 0.60, green: 0.52, blue: 0.51),
-        opportunity: Color(.sRGB, red: 1.0, green: 0.37, blue: 0.34),
-        stages: StageColor(
-          i: Color(.sRGB, red: 1.00, green: 0.93, blue: 0.97),
-          ii: Color(.sRGB, red: 1.00, green: 0.98, blue: 0.92),
-          iii: Color(.sRGB, red: 0.93, green: 1.00, blue: 0.97),
-          iv: Color(.sRGB, red: 0.93, green: 0.96, blue: 1.00)),
-        syntax: SyntaxColor(
-          vertex: NSColor(srgbRed: 0.11, green: 0.42, blue: 0.57, alpha: 1.0),
-          number: NSColor(srgbRed: 0.27, green: 0.31, blue: 0.87, alpha: 1.0),
-          option: NSColor(srgbRed: 0.68, green: 0.24, blue: 0.64, alpha: 1.0),
-          symbol: NSColor(srgbRed: 0.29, green: 0.13, blue: 0.69, alpha: 1.0)
-        ))
-    }
-  }
-}
-
-struct StageColor {
-  let i: Color
-  let ii: Color
-  let iii: Color
-  let iv: Color
-}
-
-struct SyntaxColor {
-  let vertex: NSColor
-  let number: NSColor
-  let option: NSColor
-  let symbol: NSColor
-}
diff --git a/Map/MapRenderComponents/MapStages.swift b/Map/MapRenderComponents/MapStages.swift
deleted file mode 100644 (file)
index 052a315..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-import SwiftUI
-
-struct MapStages: View {
-
-  @Environment(\.colorScheme) var colorScheme
-
-  let mapSize: CGSize
-  let lineWidth: CGFloat
-  let stages: [CGFloat]
-  let opacity = 0.1
-
-  var color: MapColor {
-    MapColor.colorForScheme(colorScheme)
-  }
-
-  var body: some View {
-
-    ZStack(alignment: .topLeading) {
-      Path { path in
-        path.addRect(CGRect(x: 0, y: 0, width: w(stages[0]), height: mapSize.height))
-      }.fill(color.stages.i)
-      Path { path in
-        path.addRect(
-          CGRect(x: w(stages[0]), y: 0, width: w(stages[1]) - w(stages[0]), height: mapSize.height))
-      }.fill(color.stages.ii)
-      Path { path in
-        path.addRect(
-          CGRect(x: w(stages[1]), y: 0, width: w(stages[2]) - w(stages[1]), height: mapSize.height))
-      }.fill(color.stages.iii)
-      Path { path in
-        path.addRect(
-          CGRect(x: w(stages[2]), y: 0, width: mapSize.width - w(stages[2]), height: mapSize.height)
-        )
-      }.fill(color.stages.iv)
-
-      Path { path in
-        path.move(to: CGPoint(x: w(stages[0]), y: 0))
-        path.addLine(to: CGPoint(x: w(stages[0]), y: mapSize.height))
-        path.move(to: CGPoint(x: w(stages[1]), y: 0))
-        path.addLine(to: CGPoint(x: w(stages[1]), y: mapSize.height))
-        path.move(to: CGPoint(x: w(stages[2]), y: 0))
-        path.addLine(to: CGPoint(x: w(stages[2]), y: mapSize.height))
-        path.move(to: CGPoint(x: w(stages[0]), y: 0))
-        path.closeSubpath()
-      }.strokedPath(StrokeStyle(lineWidth: lineWidth, dash: [10.0])).stroke(color.foreground)
-    }
-  }
-
-  func w(_ dimension: CGFloat) -> CGFloat {
-    max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
-  }
-}
-
-struct MapStages_Previews: PreviewProvider {
-  static var previews: some View {
-    MapStages(
-      mapSize: CGSize(width: 200.0, height: 200.0), lineWidth: CGFloat(1.0),
-      stages: [25.0, 50.0, 75.0])
-  }
-}
similarity index 92%
rename from Map/Views/EvolutionPicker.swift
rename to Map/Presentation/Base Components/EvolutionPicker.swift
index 815f575a19de93efb141bd499c34dcdfdee14730..c68e90cdf51ce6c8896e0e43119abc1050060fdc 100644 (file)
@@ -1,10 +1,3 @@
-//
-//  EvolutionPicker.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/4/21.
-//
-
 import SwiftUI
 
 struct EvolutionPicker: View {
 import SwiftUI
 
 struct EvolutionPicker: View {
similarity index 72%
rename from Map/MapRenderComponents/MapAxes.swift
rename to Map/Presentation/Base Components/MapRender/MapAxes.swift
index a6e2f870074800b7ef41a8bd1fcad15ab4d1ed6e..6ba758f71675ac2dbe4403d8e0f82a102a8e5faa 100644 (file)
@@ -2,8 +2,6 @@ import SwiftUI
 
 struct MapAxes: View {
 
 
 struct MapAxes: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   let mapSize: CGSize
   let lineWidth: CGFloat
   let evolution: Stage
   let mapSize: CGSize
   let lineWidth: CGFloat
   let evolution: Stage
@@ -11,10 +9,6 @@ struct MapAxes: View {
   let stageHeight = CGFloat(100.0)
   let padding = CGFloat(5.0)
 
   let stageHeight = CGFloat(100.0)
   let padding = CGFloat(5.0)
 
-  var color: Color {
-    MapColor.colorForScheme(colorScheme).foreground
-  }
-
   var body: some View {
     ZStack(alignment: .topLeading) {
 
   var body: some View {
     ZStack(alignment: .topLeading) {
 
@@ -25,50 +19,48 @@ struct MapAxes: View {
         path.addLine(to: CGPoint(x: mapSize.width, y: mapSize.height))
         path.move(to: CGPoint(x: mapSize.width, y: mapSize.height))
         path.closeSubpath()
         path.addLine(to: CGPoint(x: mapSize.width, y: mapSize.height))
         path.move(to: CGPoint(x: mapSize.width, y: mapSize.height))
         path.closeSubpath()
-      }.stroke(color, lineWidth: lineWidth * 2)
+      }.stroke(Color.map.axisColor, lineWidth: lineWidth * 2)
 
       // Y Labels
 
       // Y Labels
-      Text("Visible").font(.title3).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
+      Text("Visible").font(.theme.axisLabel).foregroundColor(.map.labelColor).rotationEffect(Angle(degrees: -90.0))
         .offset(CGSize(width: -35.0, height: 0.0))
         .offset(CGSize(width: -35.0, height: 0.0))
-      Text("Value Chain").font(.title).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
-        .offset(CGSize(width: -72.0, height: mapSize.height / 2 - 20))
-      Text("Invisible").font(.title3).foregroundColor(color).rotationEffect(Angle(degrees: -90.0))
+      Text("Invisible").font(.theme.axisLabel).foregroundColor(.map.labelColor).rotationEffect(Angle(degrees: -90.0))
         .offset(CGSize(width: -40.0, height: mapSize.height - 20))
 
       // X Labels
 
       Text("Uncharted")
         .offset(CGSize(width: -40.0, height: mapSize.height - 20))
 
       // X Labels
 
       Text("Uncharted")
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
         .offset(CGSize(width: 0.0, height: -stageHeight / 4.0))
       Text("Industrialised")
         .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
         .offset(CGSize(width: 0.0, height: -stageHeight / 4.0))
       Text("Industrialised")
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
         .offset(CGSize(width: mapSize.width - 100.0, height: -stageHeight / 4.0))
 
       Text(evolution.i)
         .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading)
         .offset(CGSize(width: mapSize.width - 100.0, height: -stageHeight / 4.0))
 
       Text(evolution.i)
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: w(stages[0]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: 0.0, height: mapSize.height + padding))
 
       Text(evolution.ii)
         .frame(width: w(stages[0]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: 0.0, height: mapSize.height + padding))
 
       Text(evolution.ii)
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: w(stages[1]) - w(stages[0]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[0]), height: mapSize.height + padding))
 
       Text(evolution.iii)
         .frame(width: w(stages[1]) - w(stages[0]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[0]), height: mapSize.height + padding))
 
       Text(evolution.iii)
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: w(stages[2]) - w(stages[1]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[1]), height: mapSize.height + padding))
 
       Text(evolution.iv)
         .frame(width: w(stages[2]) - w(stages[1]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[1]), height: mapSize.height + padding))
 
       Text(evolution.iv)
-        .font(.title3)
-        .foregroundColor(color)
+        .font(.theme.axisLabel)
+        .foregroundColor(.map.labelColor)
         .frame(width: mapSize.width - w(stages[2]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[2]), height: mapSize.height + padding))
     }
         .frame(width: mapSize.width - w(stages[2]), height: stageHeight, alignment: .topLeading)
         .offset(CGSize(width: w(stages[2]), height: mapSize.height + padding))
     }
similarity index 89%
rename from Map/MapRenderComponents/MapBlockers.swift
rename to Map/Presentation/Base Components/MapRender/MapBlockers.swift
index b668b3074483c0840c5479e55e5c72cf63afadda..d943ba8bb61a8a72f00379518b111c10a17659b7 100644 (file)
@@ -2,16 +2,10 @@ import SwiftUI
 
 struct MapBlockers: View {
 
 
 struct MapBlockers: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   let mapSize: CGSize
   let vertexSize: CGSize
   let blockers: [Blocker]
 
   let mapSize: CGSize
   let vertexSize: CGSize
   let blockers: [Blocker]
 
-  var color: Color {
-    MapColor.colorForScheme(colorScheme).blocker
-  }
-
   let cornerSize = CGSize(width: 2.0, height: 2.0)
 
   var body: some View {
   let cornerSize = CGSize(width: 2.0, height: 2.0)
 
   var body: some View {
@@ -24,7 +18,7 @@ struct MapBlockers: View {
               y: h(vertex.position.y) - vertexSize.height * 2 / 3),
             size: CGSize(width: vertexSize.width / 2, height: vertexSize.height * 2)
           ), cornerSize: cornerSize)
               y: h(vertex.position.y) - vertexSize.height * 2 / 3),
             size: CGSize(width: vertexSize.width / 2, height: vertexSize.height * 2)
           ), cornerSize: cornerSize)
-      }.fill(color)
+      }.fill(Color.map.blockerColor)
     }
   }
 
     }
   }
 
similarity index 91%
rename from Map/MapRenderComponents/MapEdges.swift
rename to Map/Presentation/Base Components/MapRender/MapEdges.swift
index d40d2aa3e1fdc33d3bdfe983653a47e5e21d5bdd..38144951d20e224c5f4c4b094f0a64baf77c5cc0 100644 (file)
@@ -1,16 +1,7 @@
-//
-//  MapEdges.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/2/21.
-//
-
 import SwiftUI
 
 struct MapEdges: View {
 
 import SwiftUI
 
 struct MapEdges: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   let mapSize: CGSize
   let lineWidth: CGFloat
   let vertexSize: CGSize
   let mapSize: CGSize
   let lineWidth: CGFloat
   let vertexSize: CGSize
@@ -18,10 +9,6 @@ struct MapEdges: View {
 
   let arrowheadSize = CGFloat(10.0)
 
 
   let arrowheadSize = CGFloat(10.0)
 
-  var color: Color {
-    MapColor.colorForScheme(colorScheme).foreground
-  }
-
   var body: some View {
     ForEach(edges, id: \.id) { edge in
       Path { path in
   var body: some View {
     ForEach(edges, id: \.id) { edge in
       Path { path in
@@ -65,7 +52,7 @@ struct MapEdges: View {
         path.closeSubpath()
       }.applying(
         CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
         path.closeSubpath()
       }.applying(
         CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
-      ).stroke(color, lineWidth: lineWidth)
+      ).stroke(Color.map.vertexColor, lineWidth: lineWidth)
     }
   }
 
     }
   }
 
diff --git a/Map/Presentation/Base Components/MapRender/MapNotes.swift b/Map/Presentation/Base Components/MapRender/MapNotes.swift
new file mode 100644 (file)
index 0000000..f35b3fe
--- /dev/null
@@ -0,0 +1,46 @@
+import SwiftUI
+
+struct MapNotes: View {
+
+  let mapSize: CGSize
+  let lineWidth: CGFloat
+  let notes: [Note]
+  
+  let maxWidth = 400.0
+  
+
+  var body: some View {
+    ForEach(notes, id: \.id) { note in
+      Text(note.text.replacingOccurrences(of: "\\n", with: "\n")).font(.theme.axisLabel)
+        .padding(2.0)
+        .background(.white)
+        .foregroundColor(.map.labelColor)
+        .border(Color.map.vertexColor, width: lineWidth)
+        .frame(minWidth: 16.0, maxWidth: maxWidth, alignment: .topLeading)
+        .offset(
+          CGSize(
+            width: w(note.position.x),
+            height: h(note.position.y)
+          )
+        )
+    }
+  }
+
+  func h(_ dimension: CGFloat) -> CGFloat {
+    max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0))
+  }
+
+  func w(_ dimension: CGFloat) -> CGFloat {
+    max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
+  }
+}
+
+struct MapNotes_Previews: PreviewProvider {
+  static var previews: some View {
+    MapNotes(
+      mapSize: CGSize(width: 400.0, height: 400.0), lineWidth: 1.0,
+        notes: [
+          Note(id: 0, position: CGPoint(x: 50.0, y: 50.0), text: "Notes can have a lot more text, so we need to make sure that they're resized correctly"),
+        ])
+  }
+}
similarity index 88%
rename from Map/MapRenderComponents/MapOpportunities.swift
rename to Map/Presentation/Base Components/MapRender/MapOpportunities.swift
index 68d3fb5fffdfb1bd50030c4213d7f599524c7edd..7fcadff8ffb46bfe26aa52b336a90ff6da08e24b 100644 (file)
@@ -1,16 +1,7 @@
-//
-//  MapEdges.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/2/21.
-//
-
 import SwiftUI
 
 struct MapOpportunities: View {
 
 import SwiftUI
 
 struct MapOpportunities: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   let mapSize: CGSize
   let lineWidth: CGFloat
   let vertexSize: CGSize
   let mapSize: CGSize
   let lineWidth: CGFloat
   let vertexSize: CGSize
@@ -18,10 +9,6 @@ struct MapOpportunities: View {
 
   let arrowheadSize = CGFloat(10.0)
 
 
   let arrowheadSize = CGFloat(10.0)
 
-  var color: Color {
-    MapColor.colorForScheme(colorScheme).opportunity
-  }
-
   var body: some View {
     ForEach(opportunities, id: \.id) { edge in
       Path { path in
   var body: some View {
     ForEach(opportunities, id: \.id) { edge in
       Path { path in
@@ -59,7 +46,7 @@ struct MapOpportunities: View {
         path.closeSubpath()
       }.applying(
         CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
         path.closeSubpath()
       }.applying(
         CGAffineTransform(translationX: vertexSize.width / 2.0, y: vertexSize.height / 2.0)
-      ).strokedPath(StrokeStyle(lineWidth: lineWidth * 2, dash: [10.0])).stroke(color)
+      ).strokedPath(StrokeStyle(lineWidth: lineWidth / 4, dash: [10.0])).stroke(Color.map.opportunityColor)
     }
   }
 
     }
   }
 
diff --git a/Map/Presentation/Base Components/MapRender/MapStages.swift b/Map/Presentation/Base Components/MapRender/MapStages.swift
new file mode 100644 (file)
index 0000000..0fc8f48
--- /dev/null
@@ -0,0 +1,52 @@
+import SwiftUI
+import Patterns
+
+struct MapStages: View {
+
+  let mapSize: CGSize
+  let lineWidth: CGFloat
+  let stages: [CGFloat]
+  let opacity = 0.1
+
+  var body: some View {
+    ZStack(alignment: .topLeading) {
+      PatternView(design: .constant(.stitch), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+        .frame(width: w(stages[0]), height: mapSize.height)
+      PatternView(design: .constant(.shingles), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+        .offset(CGSize(width: w(stages[0]), height: 0))
+        .frame(width: w(stages[1]) - w(stages[0]), height: mapSize.height)
+      PatternView(design: .constant(.shadowGrid), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+        .offset(CGSize(width: w(stages[1]), height: 0))
+        .frame(width: w(stages[2]) - w(stages[1]), height: mapSize.height)
+      PatternView(design: .constant(.wicker), pixelSize: 1.0, foregroundColor: .map.stageForeground, backgroundColor: .map.stageBackground)
+        .offset(CGSize(width: w(stages[2]), height: 0))
+        .frame(width: mapSize.width - w(stages[2]), height: mapSize.height)
+
+      Path { path in
+        path.move(to: CGPoint(x: w(stages[0]), y: 0))
+        path.addLine(to: CGPoint(x: w(stages[0]), y: mapSize.height))
+        path.closeSubpath()
+        path.move(to: CGPoint(x: w(stages[1]), y: 0))
+        path.addLine(to: CGPoint(x: w(stages[1]), y: mapSize.height))
+        path.closeSubpath()
+        path.move(to: CGPoint(x: w(stages[2]), y: 0))
+        path.addLine(to: CGPoint(x: w(stages[2]), y: mapSize.height))
+        path.closeSubpath()
+        path.move(to: CGPoint(x: w(stages[0]), y: 0))
+        path.closeSubpath()
+      }.strokedPath(StrokeStyle(lineWidth: lineWidth / 4, dash: [10.0, 18.0])).stroke(Color.map.axisColor)
+    }
+  }
+
+  func w(_ dimension: CGFloat) -> CGFloat {
+    max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0))
+  }
+}
+
+struct MapStages_Previews: PreviewProvider {
+  static var previews: some View {
+    MapStages(
+      mapSize: CGSize(width: 200.0, height: 200.0), lineWidth: CGFloat(0.5),
+      stages: [25.0, 50.0, 75.0])
+  }
+}
similarity index 64%
rename from Map/MapRenderComponents/MapVertices.swift
rename to Map/Presentation/Base Components/MapRender/MapVertices.swift
index 24d49b8095061abc8567298fcaa3828c50128b96..74cac6d6ccb4b171e426cc4b93dae740dae034d5 100644 (file)
@@ -2,24 +2,24 @@ import SwiftUI
 
 struct MapVertices: View {
 
 
 struct MapVertices: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   let mapSize: CGSize
   let vertexSize: CGSize
   let vertices: [Vertex]
   let mapSize: CGSize
   let vertexSize: CGSize
   let vertices: [Vertex]
-  let padding = CGFloat(4.0)
-
-  var color: MapColor {
-    MapColor.colorForScheme(colorScheme)
-  }
+  let padding = CGFloat(5.0)
 
   var body: some View {
 
   var body: some View {
-    ForEach(vertices, id: \.id) { vertex in
-      getVertexShape(vertex).fill(color.foreground)
-      Text(vertex.label).foregroundColor(color.secondary).offset(
-        CGSize(
-          width: w(vertex.position.x) + vertexSize.width + padding,
-          height: h(vertex.position.y)))
+    ZStack(alignment: .topLeading) {
+      ForEach(vertices, id: \.id) { vertex in
+        getVertexShape(vertex).fill(Color.map.vertexColor)
+        Text(vertex.label.replacingOccurrences(of: "\\n", with: "\n")).font(.theme.vertexLabel)
+            .foregroundColor(.map.labelColor)
+            .shadow(color: .white, radius: 0, x: -0.5, y: -0.5)
+            .shadow(color: .white, radius: 0, x: 0.5, y: 0.5)
+            .offset(
+          CGSize(
+            width: w(vertex.position.x) + vertexSize.width + padding,
+            height: h(vertex.position.y) + 7.0))
+      }
     }
   }
 
     }
   }
 
@@ -73,18 +73,20 @@ struct MapVertices: View {
         path.addLine(
           to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
         path.closeSubpath()
         path.addLine(
           to: CGPoint(x: w(vertex.position.x), y: h(vertex.position.y) + vertexSize.height))
         path.closeSubpath()
-      }.strokedPath(StrokeStyle(lineWidth: 5.0, lineCap: .butt))
+      }.strokedPath(StrokeStyle(lineWidth: 2.0, lineCap: .butt))
     }
   }
 }
 
 struct MapVertices_Previews: PreviewProvider {
   static var previews: some View {
     }
   }
 }
 
 struct MapVertices_Previews: PreviewProvider {
   static var previews: some View {
-    MapVertices(
-      mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0),
-      vertices: [
-        Vertex(id: 0, label: "A", position: CGPoint(x: 50.0, y: 50.0)),
-        Vertex(id: 0, label: "A", position: CGPoint(x: 10.0, y: 20.0)),
-      ])
+      MapVertices(
+        mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0),
+        vertices: [
+          Vertex(id: 0, label: "A Circle", position: CGPoint(x: 50.0, y: 50.0)),
+          Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square),
+          Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle),
+          Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x),
+        ])
   }
 }
   }
 }
similarity index 55%
rename from Map/Views/MapTextEditor.swift
rename to Map/Presentation/Base Components/MapTextEditor.swift
index 4fbff82f673b329f9b22e1a9b2130e0c6a46dbc4..f3838a663aa83d63dc22bf688326b93176a704a5 100644 (file)
@@ -4,19 +4,20 @@ import SwiftUI
 class MapTextEditorController: NSViewController {
 
   @Binding var text: String
 class MapTextEditorController: NSViewController {
 
   @Binding var text: String
-  var colorScheme: ColorScheme
+  let onChange: () -> Void
 
   private let vertexRegex = MapParsingPatterns.vertex
   private let edgeRegex = MapParsingPatterns.edge
   private let blockerRegex = MapParsingPatterns.blocker
   private let opportunityRegex = MapParsingPatterns.opportunity
 
   private let vertexRegex = MapParsingPatterns.vertex
   private let edgeRegex = MapParsingPatterns.edge
   private let blockerRegex = MapParsingPatterns.blocker
   private let opportunityRegex = MapParsingPatterns.opportunity
+  private let noteRegex = MapParsingPatterns.note
   private let stageRegex = MapParsingPatterns.stage
 
   private let stageRegex = MapParsingPatterns.stage
 
-  private let debouncer: Debouncer = Debouncer(seconds: 0.2)
+  private let changeDebouncer: Debouncer = Debouncer(seconds: 1)
 
 
-  init(text: Binding<String>, colorScheme: ColorScheme) {
+    init(text: Binding<String>, onChange: @escaping () -> Void) {
     self._text = text
     self._text = text
-    self.colorScheme = colorScheme
+    self.onChange = onChange
     super.init(nibName: nil, bundle: nil)
   }
 
     super.init(nibName: nil, bundle: nil)
   }
 
@@ -42,10 +43,6 @@ class MapTextEditorController: NSViewController {
   override func viewDidAppear() {
     self.view.window?.makeFirstResponder(self.view)
   }
   override func viewDidAppear() {
     self.view.window?.makeFirstResponder(self.view)
   }
-
-  func updateColorScheme(_ colorScheme: ColorScheme) {
-    self.colorScheme = colorScheme
-  }
 }
 
 extension MapTextEditorController: NSTextViewDelegate {
 }
 
 extension MapTextEditorController: NSTextViewDelegate {
@@ -53,6 +50,13 @@ extension MapTextEditorController: NSTextViewDelegate {
   func textDidChange(_ obj: Notification) {
     if let textField = obj.object as? NSTextView {
       self.text = textField.string
   func textDidChange(_ obj: Notification) {
     if let textField = obj.object as? NSTextView {
       self.text = textField.string
+        
+        
+        changeDebouncer.debounce {
+          DispatchQueue.main.async {
+            self.onChange()
+          }
+        }
     }
   }
 
     }
   }
 
@@ -70,60 +74,64 @@ extension MapTextEditorController: NSTextViewDelegate {
 }
 
 extension MapTextEditorController: NSTextStorageDelegate {
 }
 
 extension MapTextEditorController: NSTextStorageDelegate {
+
   override func textStorageDidProcessEditing(_ obj: Notification) {
     if let textStorage = obj.object as? NSTextStorage {
   override func textStorageDidProcessEditing(_ obj: Notification) {
     if let textStorage = obj.object as? NSTextStorage {
-      debouncer.debounce {
-        DispatchQueue.main.async {
-          self.colorizeText(textStorage: textStorage)
-        }
-      }
+      self.colorizeText(textStorage: textStorage)
     }
   }
 
   private func colorizeText(textStorage: NSTextStorage) {
     let range = NSMakeRange(0, textStorage.length)
     var matches = vertexRegex.matches(in: textStorage.string, options: [], range: range)
     }
   }
 
   private func colorizeText(textStorage: NSTextStorage) {
     let range = NSMakeRange(0, textStorage.length)
     var matches = vertexRegex.matches(in: textStorage.string, options: [], range: range)
-    let colors = MapColor.colorForScheme(colorScheme)
 
     for match in matches {
 
     for match in matches {
-      textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 3))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 4))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 4))
     }
 
     matches = edgeRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
     }
 
     matches = edgeRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
-      textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 1))
       let arrowRange = match.range(at: 2)
       textStorage.addAttributes(
       let arrowRange = match.range(at: 2)
       textStorage.addAttributes(
-        [.foregroundColor: colors.syntax.symbol],
+        [.foregroundColor: NSColor.syntax.symbol],
         range: NSMakeRange(arrowRange.lowerBound - 1, arrowRange.length + 1))
         range: NSMakeRange(arrowRange.lowerBound - 1, arrowRange.length + 1))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 3))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 3))
     }
 
     matches = opportunityRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
     }
 
     matches = opportunityRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
-      textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.symbol], range: match.range(at: 3))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 4))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.symbol], range: match.range(at: 3))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 4))
     }
 
     matches = blockerRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
     }
 
     matches = blockerRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
-      textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.vertex], range: match.range(at: 2))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.vertex], range: match.range(at: 2))
+    }
+    
+    matches = noteRegex.matches(in: textStorage.string, options: [], range: range)
+
+    for match in matches {
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 3))
     }
 
     matches = stageRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
     }
 
     matches = stageRegex.matches(in: textStorage.string, options: [], range: range)
 
     for match in matches {
-      textStorage.addAttributes([.foregroundColor: colors.syntax.option], range: match.range(at: 1))
-      textStorage.addAttributes([.foregroundColor: colors.syntax.number], range: match.range(at: 2))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.option], range: match.range(at: 1))
+      textStorage.addAttributes([.foregroundColor: NSColor.syntax.number], range: match.range(at: 2))
     }
   }
 }
     }
   }
 }
@@ -131,18 +139,16 @@ extension MapTextEditorController: NSTextStorageDelegate {
 struct MapTextEditor: NSViewControllerRepresentable {
 
   @Binding var text: String
 struct MapTextEditor: NSViewControllerRepresentable {
 
   @Binding var text: String
-  let colorScheme: ColorScheme
+  var onChange: () -> Void = {}
 
   func makeNSViewController(
     context: NSViewControllerRepresentableContext<MapTextEditor>
   ) -> MapTextEditorController {
 
   func makeNSViewController(
     context: NSViewControllerRepresentableContext<MapTextEditor>
   ) -> MapTextEditorController {
-    return MapTextEditorController(text: $text, colorScheme: colorScheme)
+    return MapTextEditorController(text: $text, onChange: onChange)
   }
 
   func updateNSViewController(
     _ nsViewController: MapTextEditorController,
     context: NSViewControllerRepresentableContext<MapTextEditor>
   }
 
   func updateNSViewController(
     _ nsViewController: MapTextEditorController,
     context: NSViewControllerRepresentableContext<MapTextEditor>
-  ) {
-    nsViewController.updateColorScheme(context.environment.colorScheme)
-  }
+  ) {}
 }
 }
similarity index 81%
rename from Map/Views/MapRender.swift
rename to Map/Presentation/Complex Components/MapRender/MapRenderView.swift
index aa97e22ab12a883a247a5a259b8dae64dce5a8c4..b12dabce12393e4de8752055b4939b0adbaac0f4 100644 (file)
@@ -1,10 +1,3 @@
-//
-//  ContentView.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/1/21.
-//
-
 import Combine
 import CoreData
 import CoreGraphics
 import Combine
 import CoreData
 import CoreGraphics
@@ -12,16 +5,18 @@ import SwiftUI
 
 struct MapRenderView: View {
 
 
 struct MapRenderView: View {
 
-  @Environment(\.colorScheme) var colorScheme
-
   @Binding var content: String
   @Binding var content: String
-  @Binding var evolution: Stage
+  @Binding var evolution: StageType
+  
+  var stage: Stage {
+    Stage.stages(evolution)
+  }
 
   @State var parsedMap: ParsedMap = ParsedMap.empty
 
   let mapSize = CGSize(width: 1300.0, height: 1000.0)
 
 
   @State var parsedMap: ParsedMap = ParsedMap.empty
 
   let mapSize = CGSize(width: 1300.0, height: 1000.0)
 
-  let lineWidth = CGFloat(1.0)
+  let lineWidth = CGFloat(0.5)
   let vertexSize = CGSize(width: 25.0, height: 25.0)
   let padding = CGFloat(30.0)
 
   let vertexSize = CGSize(width: 25.0, height: 25.0)
   let padding = CGFloat(30.0)
 
@@ -33,18 +28,20 @@ struct MapRenderView: View {
           CGRect(
             x: -padding, y: -padding, width: mapSize.width + padding * 2,
             height: mapSize.height + padding * 4))
           CGRect(
             x: -padding, y: -padding, width: mapSize.width + padding * 2,
             height: mapSize.height + padding * 4))
-      }.fill(MapColor.colorForScheme(colorScheme).background)
+      }.fill(.white)
 
       MapStages(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages)
       MapAxes(
 
       MapStages(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages)
       MapAxes(
-        mapSize: mapSize, lineWidth: lineWidth, evolution: evolution, stages: parsedMap.stages)
+        mapSize: mapSize, lineWidth: lineWidth, evolution: stage, stages: parsedMap.stages)
+      MapEdges(
+        mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges)
+      MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers)
+      MapVertices(mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices)
       MapOpportunities(
         mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize,
         opportunities: parsedMap.opportunities)
       MapOpportunities(
         mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize,
         opportunities: parsedMap.opportunities)
-      MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers)
-      MapVertices(mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices)
-      MapEdges(
-        mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges)
+      MapNotes(
+        mapSize: mapSize, lineWidth: lineWidth, notes: parsedMap.notes)
     }.frame(
       width: mapSize.width,
       height: mapSize.height + 2 * padding, alignment: .topLeading
     }.frame(
       width: mapSize.width,
       height: mapSize.height + 2 * padding, alignment: .topLeading
@@ -59,7 +56,7 @@ struct MapRenderView: View {
 struct MapRenderView_Previews: PreviewProvider {
   static var previews: some View {
     MapRenderView(
 struct MapRenderView_Previews: PreviewProvider {
   static var previews: some View {
     MapRenderView(
-      content: Binding.constant(""), evolution: Binding.constant(Stage.stages(.general))
+      content: Binding.constant(""), evolution: Binding.constant(StageType.general)
     ).environment(
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
     ).environment(
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
similarity index 67%
rename from Map/Views/DefaultMapView.swift
rename to Map/Presentation/Screens/EmptyMapDetailScreen.swift
index 1f624d2daace7df3e61adf60c7a16a8061e2520a..f3c6d755a30a3793b7d50a2207ae73ea42cd0e02 100644 (file)
@@ -1,14 +1,7 @@
-//
-//  ContentView.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/1/21.
-//
-
 import CoreData
 import SwiftUI
 
 import CoreData
 import SwiftUI
 
-struct DefaultMapView: View {
+struct EmptyMapDetailScreen: View {
 
   var body: some View {
     Text("Select a map from the left hand side, or click on + to create one.")
 
   var body: some View {
     Text("Select a map from the left hand side, or click on + to create one.")
@@ -17,7 +10,7 @@ struct DefaultMapView: View {
 
 struct DefaultMapView_Previews: PreviewProvider {
   static var previews: some View {
 
 struct DefaultMapView_Previews: PreviewProvider {
   static var previews: some View {
-    DefaultMapView().environment(
+    EmptyMapDetailScreen().environment(
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
similarity index 57%
rename from Map/Views/MapDetail.swift
rename to Map/Presentation/Screens/MapDetailScreen.swift
index bebe3a12414a5e9be8f5a9ca46de107d4442c530..a5c32fd16083f527c94ec2945a8aca44958c5fef 100644 (file)
@@ -1,41 +1,14 @@
-//
-//  ContentView.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/1/21.
-//
-
 import Combine
 import CoreData
 import SwiftUI
 
 import Combine
 import CoreData
 import SwiftUI
 
-class SaveTimer {
-  let currentTimePublisher = Timer.TimerPublisher(interval: 1, runLoop: .main, mode: .default)
-  let cancellable: AnyCancellable?
-
-  init() {
-    self.cancellable = currentTimePublisher.connect() as? AnyCancellable
-  }
-
-  deinit {
-    self.cancellable?.cancel()
-  }
-}
-
-let timer = SaveTimer()
-
-struct MapDetailView: View {
+struct MapDetailScreen: View {
   @Environment(\.managedObjectContext) private var viewContext
   @Environment(\.managedObjectContext) private var viewContext
-  @Environment(\.colorScheme) var colorScheme
 
   @EnvironmentObject var store: AppStore
 
   @ObservedObject var map: Map
 
 
   @EnvironmentObject var store: AppStore
 
   @ObservedObject var map: Map
 
-  private var mapColor: MapColor {
-    MapColor.colorForScheme(colorScheme)
-  }
-
   @State var title: String
   @State var content: String
 
   @State var title: String
   @State var content: String
 
@@ -45,9 +18,9 @@ struct MapDetailView: View {
         VStack {
           HStack {
             TextField(
         VStack {
           HStack {
             TextField(
-              "Title", text: $title
+                "Title", text: $title, onCommit: saveModel
             ).font(.title2).textFieldStyle(PlainTextFieldStyle()).padding(.vertical, 4.0).padding(
             ).font(.title2).textFieldStyle(PlainTextFieldStyle()).padding(.vertical, 4.0).padding(
-              .leading, 4.0)
+                .leading, 4.0)
             Button(action: saveText) {
               Image(systemName: "doc.text")
             }.padding(.vertical, 4.0).padding(.leading, 4.0)
             Button(action: saveText) {
               Image(systemName: "doc.text")
             }.padding(.vertical, 4.0).padding(.leading, 4.0)
@@ -58,25 +31,21 @@ struct MapDetailView: View {
           EvolutionPicker()
 
           ZStack(alignment: .topLeading) {
           EvolutionPicker()
 
           ZStack(alignment: .topLeading) {
-            MapTextEditor(text: $content, colorScheme: colorScheme)
-              .background(mapColor.background)
-              .foregroundColor(mapColor.foreground)
+            MapTextEditor(text: $content, onChange: saveModel)
+              .background(Color.ui.background)
+              .foregroundColor(Color.ui.foreground)
               .frame(minHeight: 96.0)
               .frame(minHeight: 96.0)
-          }.padding(.top, 8.0).padding(.leading, 8.0).background(mapColor.background).cornerRadius(
+          }.padding(.top, 8.0).padding(.leading, 8.0).background(Color.ui.background).cornerRadius(
             5.0)
         }.padding(.horizontal, 8.0)
         ScrollView([.horizontal, .vertical]) {
             5.0)
         }.padding(.horizontal, 8.0)
         ScrollView([.horizontal, .vertical]) {
-          SlowMapRender(
-            content: content, evolution: Stage.stages(store.state.selectedEvolution),
-            colorScheme: colorScheme)
-        }.background(mapColor.background)
-      }.onReceive(timer.currentTimePublisher) { _ in
-        saveModel()
+          MapRenderView(content: $content, evolution:  .constant(store.state.selectedEvolution))
+        }
       }.onDisappear {
         saveModel()
       }
     } else {
       }.onDisappear {
         saveModel()
       }
     } else {
-      DefaultMapView()
+      EmptyMapDetailScreen()
     }
   }
 
     }
   }
 
@@ -97,7 +66,7 @@ struct MapDetailView: View {
 
 struct MapDetailView_Previews: PreviewProvider {
   static var previews: some View {
 
 struct MapDetailView_Previews: PreviewProvider {
   static var previews: some View {
-    MapDetailView(map: Map(), title: "", content: "").environment(
+    MapDetailScreen(map: Map(), title: "", content: "").environment(
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
similarity index 90%
rename from Map/Views/ContentView.swift
rename to Map/Presentation/Windows/MapEditorWindow.swift
index 534c14c41761453e6666776ae58100d51943b9ef..65316cc8686bc69e1d1a44483bcfa3739cfd2843 100644 (file)
@@ -1,14 +1,7 @@
-//
-//  ContentView.swift
-//  Map
-//
-//  Created by Ruben Beltran del Rio on 2/1/21.
-//
-
 import CoreData
 import SwiftUI
 
 import CoreData
 import SwiftUI
 
-struct ContentView: View {
+struct MapEditorWindow: View {
   @Environment(\.managedObjectContext) private var viewContext
 
   @EnvironmentObject var store: AppStore
   @Environment(\.managedObjectContext) private var viewContext
 
   @EnvironmentObject var store: AppStore
@@ -22,11 +15,11 @@ struct ContentView: View {
     NavigationView {
       List {
         if maps.count == 0 {
     NavigationView {
       List {
         if maps.count == 0 {
-          DefaultMapView()
+          EmptyMapDetailScreen()
         }
         ForEach(maps) { map in
           NavigationLink(
         }
         ForEach(maps) { map in
           NavigationLink(
-            destination: MapDetailView(map: map, title: map.title ?? "", content: map.content ?? "")
+            destination: MapDetailScreen(map: map, title: map.title ?? "", content: map.content ?? "")
           ) {
             HStack {
               Text(map.title ?? "Untitled Map")
           ) {
             HStack {
               Text(map.title ?? "Untitled Map")
@@ -60,7 +53,7 @@ struct ContentView: View {
             }
           }
         }
             }
           }
         }
-      DefaultMapView()
+      EmptyMapDetailScreen()
     }
   }
 
     }
   }
 
@@ -110,7 +103,7 @@ private let mapFormatter: DateFormatter = {
 
 struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
 
 struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
-    ContentView().environment(
+    MapEditorWindow().environment(
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
       \.managedObjectContext, PersistenceController.preview.container.viewContext)
   }
 }
diff --git a/Map/Views/SlowMapRender.swift b/Map/Views/SlowMapRender.swift
deleted file mode 100644 (file)
index b7cd842..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-import Cocoa
-import SwiftUI
-
-class SlowMapRenderController: NSViewController {
-
-  var content: String
-  private var contentBinding: Binding<String> {
-    Binding(
-      get: { self.content },
-      set: { content in
-        self.content = content
-      }
-    )
-  }
-
-  var evolution: Stage
-  private var evolutionBinding: Binding<Stage> {
-    Binding(
-      get: { self.evolution },
-      set: { evolution in
-        self.evolution = evolution
-      }
-    )
-  }
-  private var colorSchemeEnvironment: ObservableColorSchemeEnvironment
-
-  private let debouncer: Debouncer = Debouncer(seconds: 0.1)
-
-  init(content: String, evolution: Stage, colorScheme: ColorScheme) {
-
-    self.content = content
-    self.evolution = evolution
-    self.colorSchemeEnvironment = ObservableColorSchemeEnvironment(colorScheme: colorScheme)
-    super.init(nibName: nil, bundle: nil)
-  }
-
-  required init?(coder: NSCoder) {
-    fatalError("init(coder:) has not been implemented")
-  }
-
-  override func loadView() {
-
-    let renderView = MapRenderView(content: self.contentBinding, evolution: self.evolutionBinding)
-      .environmentObject(self.colorSchemeEnvironment)
-    let hostingView = NSHostingView(rootView: renderView)
-
-    self.view = hostingView
-  }
-
-  func update(content: String, evolution: Stage, colorScheme: ColorScheme) {
-    self.debouncer.debounce {
-      DispatchQueue.main.async {
-        print("Updating: START")
-        self.content = content
-        self.evolution = evolution
-        self.colorSchemeEnvironment.colorScheme = colorScheme
-        print("Updating: END")
-      }
-    }
-  }
-
-  private class ObservableColorSchemeEnvironment: ObservableObject {
-    @Published var colorScheme: ColorScheme
-
-    init(colorScheme: ColorScheme) {
-      self.colorScheme = colorScheme
-    }
-  }
-}
-
-struct SlowMapRender: NSViewControllerRepresentable {
-
-  var content: String
-  var evolution: Stage
-  let colorScheme: ColorScheme
-
-  func makeNSViewController(
-    context: NSViewControllerRepresentableContext<SlowMapRender>
-  ) -> SlowMapRenderController {
-    return SlowMapRenderController(content: content, evolution: evolution, colorScheme: colorScheme)
-  }
-
-  func updateNSViewController(
-    _ nsViewController: SlowMapRenderController,
-    context: NSViewControllerRepresentableContext<SlowMapRender>
-  ) {
-    nsViewController.update(
-      content: content, evolution: evolution, colorScheme: context.environment.colorScheme)
-  }
-}