Refactor page content generators
This commit is contained in:
parent
245534e989
commit
301dbad0a5
@ -77,7 +77,7 @@
|
||||
E25DA57A2D01C64400AEF16D /* PageContentGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5792D01C63E00AEF16D /* PageContentGenerator.swift */; };
|
||||
E25DA57D2D01C67900AEF16D /* Ink in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA57C2D01C67900AEF16D /* Ink */; };
|
||||
E25DA5802D01C6AC00AEF16D /* Splash in Frameworks */ = {isa = PBXBuildFile; productRef = E25DA57F2D01C6AC00AEF16D /* Splash */; };
|
||||
E25DA5852D01C92700AEF16D /* ShorthandMarkdownKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5842D01C92600AEF16D /* ShorthandMarkdownKey.swift */; };
|
||||
E25DA5852D01C92700AEF16D /* CommandType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5842D01C92600AEF16D /* CommandType.swift */; };
|
||||
E25DA5892D01CBD300AEF16D /* Content+Generation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA5882D01CBCE00AEF16D /* Content+Generation.swift */; };
|
||||
E25DA58B2D020C9500AEF16D /* PageImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58A2D020C9200AEF16D /* PageImage.swift */; };
|
||||
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E25DA58E2D02368A00AEF16D /* PageSettings.swift */; };
|
||||
@ -89,7 +89,7 @@
|
||||
E29D31202D0320E70051B7F4 /* ContentLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D311F2D0320E20051B7F4 /* ContentLabels.swift */; };
|
||||
E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31212D0363FA0051B7F4 /* ContentButtons.swift */; };
|
||||
E29D31242D0366860051B7F4 /* TagList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31232D0366820051B7F4 /* TagList.swift */; };
|
||||
E29D31262D0370A80051B7F4 /* VideoOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31252D0370A50051B7F4 /* VideoOption.swift */; };
|
||||
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */; };
|
||||
E29D31282D0371930051B7F4 /* ContentPageVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31272D0371870051B7F4 /* ContentPageVideo.swift */; };
|
||||
E29D312A2D039B090051B7F4 /* FileDescriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D31292D039B050051B7F4 /* FileDescriptions.swift */; };
|
||||
E29D312C2D039DB80051B7F4 /* PageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E29D312B2D039DB30051B7F4 /* PageDetailView.swift */; };
|
||||
@ -190,10 +190,10 @@
|
||||
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */; };
|
||||
E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; };
|
||||
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; };
|
||||
E2FE0EEE2D1C22F3002963B7 /* InlineLinkProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */; };
|
||||
E2FE0EEE2D1C22F3002963B7 /* MarkdownLinkProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EED2D1C22EF002963B7 /* MarkdownLinkProcessor.swift */; };
|
||||
E2FE0EF42D1D6D2E002963B7 /* GeneralIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF32D1D6D22002963B7 /* GeneralIcons.swift */; };
|
||||
E2FE0EF62D1D6DF1002963B7 /* Icon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF52D1D6DEE002963B7 /* Icon.swift */; };
|
||||
E2FE0EF82D1D8110002963B7 /* IconCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF72D1D810C002963B7 /* IconCommandProcessor.swift */; };
|
||||
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF72D1D810C002963B7 /* IconCommand.swift */; };
|
||||
E2FE0EFA2D25AFBA002963B7 /* PageHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */; };
|
||||
E2FE0EFC2D266D22002963B7 /* NavigationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */; };
|
||||
E2FE0EFE2D266DA5002963B7 /* NavigationSettingsFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EFD2D266DA1002963B7 /* NavigationSettingsFile.swift */; };
|
||||
@ -204,20 +204,20 @@
|
||||
E2FE0F092D2689F0002963B7 /* TagPageGeneratorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F082D2689F0002963B7 /* TagPageGeneratorSource.swift */; };
|
||||
E2FE0F0B2D2689FF002963B7 /* FeedGeneratorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F0A2D2689FF002963B7 /* FeedGeneratorSource.swift */; };
|
||||
E2FE0F0D2D268A09002963B7 /* PostListPageGeneratorSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F0C2D268A09002963B7 /* PostListPageGeneratorSource.swift */; };
|
||||
E2FE0F0F2D268D4F002963B7 /* BoxCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F0E2D268D4B002963B7 /* BoxCommandProcessor.swift */; };
|
||||
E2FE0F112D268E7E002963B7 /* CodeBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F102D268E78002963B7 /* CodeBlockProcessor.swift */; };
|
||||
E2FE0F152D26918F002963B7 /* PageHtmlProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F142D269188002963B7 /* PageHtmlProcessor.swift */; };
|
||||
E2FE0F0F2D268D4F002963B7 /* BoxCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F0E2D268D4B002963B7 /* BoxCommand.swift */; };
|
||||
E2FE0F112D268E7E002963B7 /* MarkdownCodeProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F102D268E78002963B7 /* MarkdownCodeProcessor.swift */; };
|
||||
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F142D269188002963B7 /* HtmlCommand.swift */; };
|
||||
E2FE0F172D2698D5002963B7 /* LocalizedPageId.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */; };
|
||||
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F182D2723E3002963B7 /* ImageSet.swift */; };
|
||||
E2FE0F1B2D274FDF002963B7 /* LinkPreviewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */; };
|
||||
E2FE0F1E2D281AE1002963B7 /* TagOverviewGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */; };
|
||||
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */; };
|
||||
E2FE0F222D2A84A0002963B7 /* VideoCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */; };
|
||||
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F212D2A849B002963B7 /* VideoCommand.swift */; };
|
||||
E2FE0F242D2A8C21002963B7 /* TagDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */; };
|
||||
E2FE0F262D2AF9B0002963B7 /* ImageCompareCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F252D2AF9AA002963B7 /* ImageCompareCommand.swift */; };
|
||||
E2FE0F282D2AFB11002963B7 /* ImageCompare.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F272D2AFB0A002963B7 /* ImageCompare.swift */; };
|
||||
E2FE0F2A2D2AFBE6002963B7 /* ImageCompareIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F292D2AFBE3002963B7 /* ImageCompareIcons.swift */; };
|
||||
E2FE0F2C2D2B119A002963B7 /* ImageCommandProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F2B2D2B1196002963B7 /* ImageCommandProcessor.swift */; };
|
||||
E2FE0F2C2D2B119A002963B7 /* ImageCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F2B2D2B1196002963B7 /* ImageCommand.swift */; };
|
||||
E2FE0F312D2B1952002963B7 /* PartialSvgImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F302D2B1952002963B7 /* PartialSvgImage.swift */; };
|
||||
E2FE0F332D2B2665002963B7 /* AudioBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F322D2B265F002963B7 /* AudioBlockProcessor.swift */; };
|
||||
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */; };
|
||||
@ -227,6 +227,17 @@
|
||||
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */; };
|
||||
E2FE0F402D2B45D3002963B7 /* SwiftProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F3F2D2B45CD002963B7 /* SwiftProcessor.swift */; };
|
||||
E2FE0F422D2B4821002963B7 /* OtherCodeProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F412D2B480B002963B7 /* OtherCodeProcessor.swift */; };
|
||||
E2FE0F462D2BC777002963B7 /* MarkdownImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F452D2BC772002963B7 /* MarkdownImageProcessor.swift */; };
|
||||
E2FE0F482D2BC7D1002963B7 /* MarkdownProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F472D2BC7CD002963B7 /* MarkdownProcessor.swift */; };
|
||||
E2FE0F4B2D2BCCAA002963B7 /* MarkdownHeadlineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F4A2D2BCCA5002963B7 /* MarkdownHeadlineProcessor.swift */; };
|
||||
E2FE0F4D2D2BCD30002963B7 /* PageLinkCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F4C2D2BCD2D002963B7 /* PageLinkCommand.swift */; };
|
||||
E2FE0F4F2D2BCD80002963B7 /* TagLinkCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F4E2D2BCD7D002963B7 /* TagLinkCommand.swift */; };
|
||||
E2FE0F512D2BCDC8002963B7 /* ModelCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F502D2BCDC4002963B7 /* ModelCommand.swift */; };
|
||||
E2FE0F532D2BCE17002963B7 /* SvgCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F522D2BCE15002963B7 /* SvgCommand.swift */; };
|
||||
E2FE0F552D2BCFC4002963B7 /* ContentBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F542D2BCFC4002963B7 /* ContentBlock.swift */; };
|
||||
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */; };
|
||||
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */; };
|
||||
E2FE0F5B2D2BCFF2002963B7 /* KeyedBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -296,7 +307,7 @@
|
||||
E25DA5742D018B6100AEF16D /* FileDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDetailView.swift; sourceTree = "<group>"; };
|
||||
E25DA5762D018B9500AEF16D /* File+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "File+Mock.swift"; sourceTree = "<group>"; };
|
||||
E25DA5792D01C63E00AEF16D /* PageContentGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageContentGenerator.swift; sourceTree = "<group>"; };
|
||||
E25DA5842D01C92600AEF16D /* ShorthandMarkdownKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShorthandMarkdownKey.swift; sourceTree = "<group>"; };
|
||||
E25DA5842D01C92600AEF16D /* CommandType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandType.swift; sourceTree = "<group>"; };
|
||||
E25DA5882D01CBCE00AEF16D /* Content+Generation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Generation.swift"; sourceTree = "<group>"; };
|
||||
E25DA58A2D020C9200AEF16D /* PageImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageImage.swift; sourceTree = "<group>"; };
|
||||
E25DA58E2D02368A00AEF16D /* PageSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageSettings.swift; sourceTree = "<group>"; };
|
||||
@ -308,7 +319,7 @@
|
||||
E29D311F2D0320E20051B7F4 /* ContentLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentLabels.swift; sourceTree = "<group>"; };
|
||||
E29D31212D0363FA0051B7F4 /* ContentButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentButtons.swift; sourceTree = "<group>"; };
|
||||
E29D31232D0366820051B7F4 /* TagList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagList.swift; sourceTree = "<group>"; };
|
||||
E29D31252D0370A50051B7F4 /* VideoOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoOption.swift; sourceTree = "<group>"; };
|
||||
E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VideoCommand+Option.swift"; sourceTree = "<group>"; };
|
||||
E29D31272D0371870051B7F4 /* ContentPageVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentPageVideo.swift; sourceTree = "<group>"; };
|
||||
E29D31292D039B050051B7F4 /* FileDescriptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileDescriptions.swift; sourceTree = "<group>"; };
|
||||
E29D312B2D039DB30051B7F4 /* PageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageDetailView.swift; sourceTree = "<group>"; };
|
||||
@ -408,10 +419,10 @@
|
||||
E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerationResults.swift; sourceTree = "<group>"; };
|
||||
E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertThrowing.swift; sourceTree = "<group>"; };
|
||||
E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFileSelectionView.swift; sourceTree = "<group>"; };
|
||||
E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineLinkProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0EED2D1C22EF002963B7 /* MarkdownLinkProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownLinkProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0EF32D1D6D22002963B7 /* GeneralIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralIcons.swift; sourceTree = "<group>"; };
|
||||
E2FE0EF52D1D6DEE002963B7 /* Icon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icon.swift; sourceTree = "<group>"; };
|
||||
E2FE0EF72D1D810C002963B7 /* IconCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconCommandProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0EF72D1D810C002963B7 /* IconCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0EF92D25AFB5002963B7 /* PageHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHeader.swift; sourceTree = "<group>"; };
|
||||
E2FE0EFB2D266D18002963B7 /* NavigationSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettings.swift; sourceTree = "<group>"; };
|
||||
E2FE0EFD2D266DA1002963B7 /* NavigationSettingsFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSettingsFile.swift; sourceTree = "<group>"; };
|
||||
@ -422,20 +433,20 @@
|
||||
E2FE0F082D2689F0002963B7 /* TagPageGeneratorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagPageGeneratorSource.swift; sourceTree = "<group>"; };
|
||||
E2FE0F0A2D2689FF002963B7 /* FeedGeneratorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedGeneratorSource.swift; sourceTree = "<group>"; };
|
||||
E2FE0F0C2D268A09002963B7 /* PostListPageGeneratorSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListPageGeneratorSource.swift; sourceTree = "<group>"; };
|
||||
E2FE0F0E2D268D4B002963B7 /* BoxCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxCommandProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F102D268E78002963B7 /* CodeBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeBlockProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F142D269188002963B7 /* PageHtmlProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageHtmlProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F0E2D268D4B002963B7 /* BoxCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F102D268E78002963B7 /* MarkdownCodeProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownCodeProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F142D269188002963B7 /* HtmlCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HtmlCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F162D2698D5002963B7 /* LocalizedPageId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedPageId.swift; sourceTree = "<group>"; };
|
||||
E2FE0F182D2723E3002963B7 /* ImageSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSet.swift; sourceTree = "<group>"; };
|
||||
E2FE0F1A2D274FDA002963B7 /* LinkPreviewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewItem.swift; sourceTree = "<group>"; };
|
||||
E2FE0F1D2D281ACE002963B7 /* TagOverviewGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagOverviewGenerator.swift; sourceTree = "<group>"; };
|
||||
E2FE0F1F2D29A709002963B7 /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; };
|
||||
E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommandProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F212D2A849B002963B7 /* VideoCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F232D2A8C1A002963B7 /* TagDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagDisplayView.swift; sourceTree = "<group>"; };
|
||||
E2FE0F252D2AF9AA002963B7 /* ImageCompareCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCompareCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F272D2AFB0A002963B7 /* ImageCompare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCompare.swift; sourceTree = "<group>"; };
|
||||
E2FE0F292D2AFBE3002963B7 /* ImageCompareIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCompareIcons.swift; sourceTree = "<group>"; };
|
||||
E2FE0F2B2D2B1196002963B7 /* ImageCommandProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCommandProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F2B2D2B1196002963B7 /* ImageCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F302D2B1952002963B7 /* PartialSvgImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartialSvgImage.swift; sourceTree = "<group>"; };
|
||||
E2FE0F322D2B265F002963B7 /* AudioBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioBlockProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockProcessor.swift; sourceTree = "<group>"; };
|
||||
@ -445,6 +456,17 @@
|
||||
E2FE0F3D2D2B4225002963B7 /* AudioSettingsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioSettingsDetailView.swift; sourceTree = "<group>"; };
|
||||
E2FE0F3F2D2B45CD002963B7 /* SwiftProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F412D2B480B002963B7 /* OtherCodeProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherCodeProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F452D2BC772002963B7 /* MarkdownImageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownImageProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F472D2BC7CD002963B7 /* MarkdownProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F4A2D2BCCA5002963B7 /* MarkdownHeadlineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHeadlineProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F4C2D2BCD2D002963B7 /* PageLinkCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageLinkCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F4E2D2BCD7D002963B7 /* TagLinkCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagLinkCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F502D2BCDC4002963B7 /* ModelCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F522D2BCE15002963B7 /* SvgCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SvgCommand.swift; sourceTree = "<group>"; };
|
||||
E2FE0F542D2BCFC4002963B7 /* ContentBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBlock.swift; sourceTree = "<group>"; };
|
||||
E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockLineProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedKeyBlockProcessor.swift; sourceTree = "<group>"; };
|
||||
E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedBlockProcessor.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -525,9 +547,11 @@
|
||||
E25DA5782D01C56200AEF16D /* Generator */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2FE0F492D2BC9EF002963B7 /* Commands */,
|
||||
E2FE0F442D2BC76C002963B7 /* Markdown */,
|
||||
E2FE0F432D2BC718002963B7 /* Results */,
|
||||
E2FE0F342D2B27E6002963B7 /* Blocks */,
|
||||
E2FE0F072D2689DC002963B7 /* Post Lists */,
|
||||
E29D31B62D0DAC030051B7F4 /* Page Content */,
|
||||
E2FE0F1C2D281A7B002963B7 /* Page Generators */,
|
||||
E22990232D0EDBD0009F8D77 /* HeaderElement.swift */,
|
||||
E29D31842D0AE8EE0051B7F4 /* KnownHeaderElement.swift */,
|
||||
@ -535,11 +559,6 @@
|
||||
E22990412D107A94009F8D77 /* ImageVersion.swift */,
|
||||
E2FE0F182D2723E3002963B7 /* ImageSet.swift */,
|
||||
E25DA5792D01C63E00AEF16D /* PageContentGenerator.swift */,
|
||||
E29D316C2D07A4FF0051B7F4 /* PageGenerationResults.swift */,
|
||||
E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */,
|
||||
E25DA5842D01C92600AEF16D /* ShorthandMarkdownKey.swift */,
|
||||
E29D31252D0370A50051B7F4 /* VideoOption.swift */,
|
||||
E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */,
|
||||
);
|
||||
path = Generator;
|
||||
sourceTree = "<group>";
|
||||
@ -610,24 +629,6 @@
|
||||
path = Icons;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E29D31B62D0DAC030051B7F4 /* Page Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E29D31BD2D0DB8560051B7F4 /* AudioPlayerCommand.swift */,
|
||||
E2FE0F0E2D268D4B002963B7 /* BoxCommandProcessor.swift */,
|
||||
E29D31B72D0DAC1D0051B7F4 /* ButtonCommand.swift */,
|
||||
E29D31BB2D0DB5110051B7F4 /* CommandProcessor.swift */,
|
||||
E2FE0EF72D1D810C002963B7 /* IconCommandProcessor.swift */,
|
||||
E2FE0F2B2D2B1196002963B7 /* ImageCommandProcessor.swift */,
|
||||
E2FE0F252D2AF9AA002963B7 /* ImageCompareCommand.swift */,
|
||||
E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */,
|
||||
E29D31B92D0DB4EF0051B7F4 /* LabelsCommand.swift */,
|
||||
E2FE0F142D269188002963B7 /* PageHtmlProcessor.swift */,
|
||||
E2FE0F212D2A849B002963B7 /* VideoCommandProcessor.swift */,
|
||||
);
|
||||
path = "Page Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E29D31C12D0DBED70051B7F4 /* AudioPlayer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -917,15 +918,71 @@
|
||||
E2FE0F342D2B27E6002963B7 /* Blocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2FE0F5C2D2BD006002963B7 /* Types */,
|
||||
E2FE0F322D2B265F002963B7 /* AudioBlockProcessor.swift */,
|
||||
E2FE0F542D2BCFC4002963B7 /* ContentBlock.swift */,
|
||||
E2FE0F412D2B480B002963B7 /* OtherCodeProcessor.swift */,
|
||||
E2FE0F3F2D2B45CD002963B7 /* SwiftProcessor.swift */,
|
||||
E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */,
|
||||
E2FE0F322D2B265F002963B7 /* AudioBlockProcessor.swift */,
|
||||
E2FE0F102D268E78002963B7 /* CodeBlockProcessor.swift */,
|
||||
);
|
||||
path = Blocks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E2FE0F432D2BC718002963B7 /* Results */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E29D316C2D07A4FF0051B7F4 /* PageGenerationResults.swift */,
|
||||
E2FE0EE52D15A0B1002963B7 /* GenerationResults.swift */,
|
||||
E29D318F2D0B34870051B7F4 /* GenerationAnomaly.swift */,
|
||||
);
|
||||
path = Results;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E2FE0F442D2BC76C002963B7 /* Markdown */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2FE0F102D268E78002963B7 /* MarkdownCodeProcessor.swift */,
|
||||
E2FE0F4A2D2BCCA5002963B7 /* MarkdownHeadlineProcessor.swift */,
|
||||
E2FE0F452D2BC772002963B7 /* MarkdownImageProcessor.swift */,
|
||||
E2FE0EED2D1C22EF002963B7 /* MarkdownLinkProcessor.swift */,
|
||||
E2FE0F472D2BC7CD002963B7 /* MarkdownProcessor.swift */,
|
||||
);
|
||||
path = Markdown;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E2FE0F492D2BC9EF002963B7 /* Commands */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E29D31BD2D0DB8560051B7F4 /* AudioPlayerCommand.swift */,
|
||||
E2FE0F0E2D268D4B002963B7 /* BoxCommand.swift */,
|
||||
E29D31B72D0DAC1D0051B7F4 /* ButtonCommand.swift */,
|
||||
E29D31BB2D0DB5110051B7F4 /* CommandProcessor.swift */,
|
||||
E25DA5842D01C92600AEF16D /* CommandType.swift */,
|
||||
E2FE0F142D269188002963B7 /* HtmlCommand.swift */,
|
||||
E2FE0EF72D1D810C002963B7 /* IconCommand.swift */,
|
||||
E2FE0F2B2D2B1196002963B7 /* ImageCommand.swift */,
|
||||
E2FE0F252D2AF9AA002963B7 /* ImageCompareCommand.swift */,
|
||||
E29D31B92D0DB4EF0051B7F4 /* LabelsCommand.swift */,
|
||||
E2FE0F502D2BCDC4002963B7 /* ModelCommand.swift */,
|
||||
E2FE0F4C2D2BCD2D002963B7 /* PageLinkCommand.swift */,
|
||||
E2FE0F522D2BCE15002963B7 /* SvgCommand.swift */,
|
||||
E2FE0F4E2D2BCD7D002963B7 /* TagLinkCommand.swift */,
|
||||
E2FE0F212D2A849B002963B7 /* VideoCommand.swift */,
|
||||
E29D31252D0370A50051B7F4 /* VideoCommand+Option.swift */,
|
||||
);
|
||||
path = Commands;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E2FE0F5C2D2BD006002963B7 /* Types */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E2FE0F562D2BCFD4002963B7 /* BlockLineProcessor.swift */,
|
||||
E2FE0F352D2B27F6002963B7 /* BlockProcessor.swift */,
|
||||
E2FE0F5A2D2BCFF2002963B7 /* KeyedBlockProcessor.swift */,
|
||||
E2FE0F582D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift */,
|
||||
);
|
||||
path = Types;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -1016,9 +1073,10 @@
|
||||
files = (
|
||||
E29D31242D0366860051B7F4 /* TagList.swift in Sources */,
|
||||
E22990282D0F596C009F8D77 /* IntegerPropertyView.swift in Sources */,
|
||||
E2FE0F482D2BC7D1002963B7 /* MarkdownProcessor.swift in Sources */,
|
||||
E2FE0EFE2D266DA5002963B7 /* NavigationSettingsFile.swift in Sources */,
|
||||
E2FE0EE62D15A0B5002963B7 /* GenerationResults.swift in Sources */,
|
||||
E2FE0F152D26918F002963B7 /* PageHtmlProcessor.swift in Sources */,
|
||||
E2FE0F152D26918F002963B7 /* HtmlCommand.swift in Sources */,
|
||||
E2FE0F202D29A70E002963B7 /* Array+Remove.swift in Sources */,
|
||||
E25DA5772D018B9900AEF16D /* File+Mock.swift in Sources */,
|
||||
E25DA5892D01CBD300AEF16D /* Content+Generation.swift in Sources */,
|
||||
@ -1027,6 +1085,7 @@
|
||||
E29D317D2D086AB00051B7F4 /* Int+Random.swift in Sources */,
|
||||
E25DA56F2D00F9A100AEF16D /* PostFeedSettingsView.swift in Sources */,
|
||||
E29D313B2D04464A0051B7F4 /* LocalizedTagDetailView.swift in Sources */,
|
||||
E2FE0F552D2BCFC4002963B7 /* ContentBlock.swift in Sources */,
|
||||
E2A37D212CEA94EC0000979F /* Sequence+Sorted.swift in Sources */,
|
||||
E29D31BA2D0DB5080051B7F4 /* LabelsCommand.swift in Sources */,
|
||||
E2FE0EFC2D266D22002963B7 /* NavigationSettings.swift in Sources */,
|
||||
@ -1039,6 +1098,7 @@
|
||||
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */,
|
||||
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */,
|
||||
E2FE0F3C2D2B3F45002963B7 /* AudioPlayerSettingsFile.swift in Sources */,
|
||||
E2FE0F462D2BC777002963B7 /* MarkdownImageProcessor.swift in Sources */,
|
||||
E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */,
|
||||
E25DA5452D00952E00AEF16D /* SettingsSection.swift in Sources */,
|
||||
E2A21C462CBAE2E60060935B /* FeedEntryContent.swift in Sources */,
|
||||
@ -1068,6 +1128,7 @@
|
||||
E2FE0F362D2B27F9002963B7 /* BlockProcessor.swift in Sources */,
|
||||
E22990462D10B7A7009F8D77 /* SecurityScopeStatus.swift in Sources */,
|
||||
E29D31512D06168E0051B7F4 /* PostListView.swift in Sources */,
|
||||
E2FE0F4F2D2BCD80002963B7 /* TagLinkCommand.swift in Sources */,
|
||||
E22990342D0F77E9009F8D77 /* PagePropertyView.swift in Sources */,
|
||||
E2A37D0C2CE4036B0000979F /* LocalizedPage.swift in Sources */,
|
||||
E2A21C102CB18B3A0060935B /* FlowHStack.swift in Sources */,
|
||||
@ -1092,8 +1153,8 @@
|
||||
E22990302D0F75DE009F8D77 /* BoolPropertyView.swift in Sources */,
|
||||
E21850392CFCA6C00090B18B /* WebsiteData+Mock.swift in Sources */,
|
||||
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */,
|
||||
E29D31262D0370A80051B7F4 /* VideoOption.swift in Sources */,
|
||||
E2FE0EF82D1D8110002963B7 /* IconCommandProcessor.swift in Sources */,
|
||||
E29D31262D0370A80051B7F4 /* VideoCommand+Option.swift in Sources */,
|
||||
E2FE0EF82D1D8110002963B7 /* IconCommand.swift in Sources */,
|
||||
E21850272CF3B42D0090B18B /* PostDetailView.swift in Sources */,
|
||||
E2A37D172CE73F1A0000979F /* TagFile.swift in Sources */,
|
||||
E29D318E2D0B2E680051B7F4 /* PageSettingsContentView.swift in Sources */,
|
||||
@ -1118,7 +1179,7 @@
|
||||
E29D31A32D0CC98C0051B7F4 /* Item.swift in Sources */,
|
||||
E25DA57A2D01C64400AEF16D /* PageContentGenerator.swift in Sources */,
|
||||
E2A21C202CB28ED20060935B /* MockImage.swift in Sources */,
|
||||
E2FE0EEE2D1C22F3002963B7 /* InlineLinkProcessor.swift in Sources */,
|
||||
E2FE0EEE2D1C22F3002963B7 /* MarkdownLinkProcessor.swift in Sources */,
|
||||
E2FE0F022D266FCB002963B7 /* LocalizedNavigationSettings.swift in Sources */,
|
||||
E29D313F2D04822C0051B7F4 /* AddPostView.swift in Sources */,
|
||||
E25DA5752D018B6100AEF16D /* FileDetailView.swift in Sources */,
|
||||
@ -1138,6 +1199,8 @@
|
||||
E29D316B2D07488B0051B7F4 /* PostListPageGenerator.swift in Sources */,
|
||||
E218501D2CEE6CB60090B18B /* VerticalCenter.swift in Sources */,
|
||||
E22990382D0F7B32009F8D77 /* OptionalImagePropertyView.swift in Sources */,
|
||||
E2FE0F512D2BCDC8002963B7 /* ModelCommand.swift in Sources */,
|
||||
E2FE0F592D2BCFE4002963B7 /* OrderedKeyBlockProcessor.swift in Sources */,
|
||||
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */,
|
||||
E29D31B32D0DA6E80051B7F4 /* ButtonIcons.swift in Sources */,
|
||||
E29D319D2D0C45B90051B7F4 /* PageIssueView.swift in Sources */,
|
||||
@ -1147,6 +1210,7 @@
|
||||
E2FE0F002D266E0A002963B7 /* LocalizedNavigationSettingsFile.swift in Sources */,
|
||||
E2A9CB7E2C7BCF2A005C89CC /* Page.swift in Sources */,
|
||||
E29D31202D0320E70051B7F4 /* ContentLabels.swift in Sources */,
|
||||
E2FE0F572D2BCFD4002963B7 /* BlockLineProcessor.swift in Sources */,
|
||||
E229904A2D10BB90009F8D77 /* SecurityScopeBookmark.swift in Sources */,
|
||||
E29D314B2D04FC950051B7F4 /* FileToAdd.swift in Sources */,
|
||||
E2E06DFB2CA4A65E0019C2AF /* Content.swift in Sources */,
|
||||
@ -1177,13 +1241,13 @@
|
||||
E29D31C32D0DBEF20051B7F4 /* Song.swift in Sources */,
|
||||
E229902A2D0F5A14009F8D77 /* DetailTitle.swift in Sources */,
|
||||
E29D31532D0618740051B7F4 /* AddPageView.swift in Sources */,
|
||||
E2FE0F2C2D2B119A002963B7 /* ImageCommandProcessor.swift in Sources */,
|
||||
E2FE0F112D268E7E002963B7 /* CodeBlockProcessor.swift in Sources */,
|
||||
E2FE0F2C2D2B119A002963B7 /* ImageCommand.swift in Sources */,
|
||||
E2FE0F112D268E7E002963B7 /* MarkdownCodeProcessor.swift in Sources */,
|
||||
E22990202D0ECBE5009F8D77 /* TagOverviewDetailView.swift in Sources */,
|
||||
E29D31C02D0DB9F20051B7F4 /* AudioPlayerContent.swift in Sources */,
|
||||
E22990192D0E3546009F8D77 /* ItemType.swift in Sources */,
|
||||
E25DA51F2CFF15C400AEF16D /* NavigationBar.swift in Sources */,
|
||||
E2FE0F0F2D268D4F002963B7 /* BoxCommandProcessor.swift in Sources */,
|
||||
E2FE0F0F2D268D4F002963B7 /* BoxCommand.swift in Sources */,
|
||||
E22990482D10B7B7009F8D77 /* StorageAccessError.swift in Sources */,
|
||||
E29D31832D0A43DB0051B7F4 /* RelatedPageLink.swift in Sources */,
|
||||
E2A21C512CBBD53F0060935B /* FileResource.swift in Sources */,
|
||||
@ -1202,9 +1266,11 @@
|
||||
E25DA5712D01015400AEF16D /* GenerationContentView.swift in Sources */,
|
||||
E29D316F2D0822770051B7F4 /* SettingsListView.swift in Sources */,
|
||||
E2A21C0E2CB189DC0060935B /* Color+RGB.swift in Sources */,
|
||||
E25DA5852D01C92700AEF16D /* ShorthandMarkdownKey.swift in Sources */,
|
||||
E25DA5852D01C92700AEF16D /* CommandType.swift in Sources */,
|
||||
E229903A2D0F7E48009F8D77 /* GenericPropertyView.swift in Sources */,
|
||||
E29D31AA2D0CEE3F0051B7F4 /* AudioPlayer.swift in Sources */,
|
||||
E2FE0F4B2D2BCCAA002963B7 /* MarkdownHeadlineProcessor.swift in Sources */,
|
||||
E2FE0F532D2BCE17002963B7 /* SvgCommand.swift in Sources */,
|
||||
E25DA5152CFF00C100AEF16D /* Content+Load.swift in Sources */,
|
||||
E2FE0F3E2D2B4225002963B7 /* AudioSettingsDetailView.swift in Sources */,
|
||||
E25DA58F2D02368D00AEF16D /* PageSettings.swift in Sources */,
|
||||
@ -1213,9 +1279,10 @@
|
||||
E229903C2D0F8A7B009F8D77 /* OptionalTextFieldPropertyView.swift in Sources */,
|
||||
E25DA5932D023B3C00AEF16D /* PageSettingsFile.swift in Sources */,
|
||||
E29D31222D0363FD0051B7F4 /* ContentButtons.swift in Sources */,
|
||||
E2FE0F222D2A84A0002963B7 /* VideoCommandProcessor.swift in Sources */,
|
||||
E2FE0F222D2A84A0002963B7 /* VideoCommand.swift in Sources */,
|
||||
E2FE0F192D2723E3002963B7 /* ImageSet.swift in Sources */,
|
||||
E2A21C362CB9A3D70060935B /* PathSettingsView.swift in Sources */,
|
||||
E2FE0F5B2D2BCFF2002963B7 /* KeyedBlockProcessor.swift in Sources */,
|
||||
E29D31362D0435430051B7F4 /* TabSelection.swift in Sources */,
|
||||
E2FE0F312D2B1952002963B7 /* PartialSvgImage.swift in Sources */,
|
||||
E2FE0F262D2AF9B0002963B7 /* ImageCompareCommand.swift in Sources */,
|
||||
@ -1225,6 +1292,7 @@
|
||||
E29D31792D083DE50051B7F4 /* PageContentResultsView.swift in Sources */,
|
||||
E25DA5912D023A8400AEF16D /* IntegerField.swift in Sources */,
|
||||
E29D318B2D0B07EE0051B7F4 /* ContentBox.swift in Sources */,
|
||||
E2FE0F4D2D2BCD30002963B7 /* PageLinkCommand.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1,86 +0,0 @@
|
||||
|
||||
enum ContentBlock: String, CaseIterable {
|
||||
|
||||
case audio
|
||||
|
||||
case swift
|
||||
|
||||
var processor: BlockProcessor.Type {
|
||||
switch self {
|
||||
case .audio: return AudioBlockProcessor.self
|
||||
case .swift: return SwiftBlockProcessor.self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol BlockProcessor {
|
||||
|
||||
static var blockId: ContentBlock { get }
|
||||
|
||||
var results: PageGenerationResults { get }
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
|
||||
|
||||
func process(_ markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension BlockProcessor {
|
||||
|
||||
func invalid(_ markdown: Substring) {
|
||||
results.invalid(block: Self.blockId, markdown)
|
||||
}
|
||||
}
|
||||
|
||||
protocol BlockLineProcessor: BlockProcessor {
|
||||
|
||||
func process(_ lines: [String], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension BlockLineProcessor {
|
||||
|
||||
func process(_ markdown: Substring) -> String {
|
||||
let lines = markdown
|
||||
.between("```\(Self.blockId.self)", and: "```")
|
||||
.components(separatedBy: "\n")
|
||||
return process(lines, markdown: markdown)
|
||||
}
|
||||
}
|
||||
|
||||
protocol OrderedKeyBlockProcessor: BlockLineProcessor {
|
||||
|
||||
associatedtype Key: Hashable, RawRepresentable where Key.RawValue == String
|
||||
|
||||
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension OrderedKeyBlockProcessor {
|
||||
|
||||
func process(_ lines: [String], markdown: Substring) -> String {
|
||||
let result: [(key: Key, value: String)] = lines.compactMap { line in
|
||||
guard line.trimmed != "" else {
|
||||
return nil
|
||||
}
|
||||
let (rawKey, rawValue) = line.splitAtFirst(":")
|
||||
guard let key = Key(rawValue: rawKey.trimmed) else {
|
||||
print("Invalid key \(rawKey)")
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
return (key, rawValue.trimmed)
|
||||
}
|
||||
return process(result, markdown: markdown)
|
||||
}
|
||||
}
|
||||
|
||||
protocol KeyedBlockProcessor: OrderedKeyBlockProcessor {
|
||||
|
||||
func process(_ arguments: [Key : String], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension KeyedBlockProcessor {
|
||||
|
||||
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String {
|
||||
let result = arguments.reduce(into: [:]) { $0[$1.key] = $1.value }
|
||||
return process(result, markdown: markdown)
|
||||
}
|
||||
}
|
14
CHDataManagement/Generator/Blocks/ContentBlock.swift
Normal file
14
CHDataManagement/Generator/Blocks/ContentBlock.swift
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
enum ContentBlock: String, CaseIterable {
|
||||
|
||||
case audio
|
||||
|
||||
case swift
|
||||
|
||||
var processor: BlockProcessor.Type {
|
||||
switch self {
|
||||
case .audio: return AudioBlockProcessor.self
|
||||
case .swift: return SwiftBlockProcessor.self
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
|
||||
protocol BlockLineProcessor: BlockProcessor {
|
||||
|
||||
func process(_ lines: [String], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension BlockLineProcessor {
|
||||
|
||||
func process(_ markdown: Substring) -> String {
|
||||
let lines = markdown
|
||||
.between("```\(Self.blockId.self)", and: "```")
|
||||
.components(separatedBy: "\n")
|
||||
return process(lines, markdown: markdown)
|
||||
}
|
||||
}
|
18
CHDataManagement/Generator/Blocks/Types/BlockProcessor.swift
Normal file
18
CHDataManagement/Generator/Blocks/Types/BlockProcessor.swift
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
protocol BlockProcessor {
|
||||
|
||||
static var blockId: ContentBlock { get }
|
||||
|
||||
var results: PageGenerationResults { get }
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
|
||||
|
||||
func process(_ markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension BlockProcessor {
|
||||
|
||||
func invalid(_ markdown: Substring) {
|
||||
results.invalid(block: Self.blockId, markdown)
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
|
||||
protocol KeyedBlockProcessor: OrderedKeyBlockProcessor {
|
||||
|
||||
func process(_ arguments: [Key : String], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension KeyedBlockProcessor {
|
||||
|
||||
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String {
|
||||
let result = arguments.reduce(into: [:]) { $0[$1.key] = $1.value }
|
||||
return process(result, markdown: markdown)
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
|
||||
protocol OrderedKeyBlockProcessor: BlockLineProcessor {
|
||||
|
||||
associatedtype Key: Hashable, RawRepresentable where Key.RawValue == String
|
||||
|
||||
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension OrderedKeyBlockProcessor {
|
||||
|
||||
func process(_ lines: [String], markdown: Substring) -> String {
|
||||
let result: [(key: Key, value: String)] = lines.compactMap { line in
|
||||
guard line.trimmed != "" else {
|
||||
return nil
|
||||
}
|
||||
let (rawKey, rawValue) = line.splitAtFirst(":")
|
||||
guard let key = Key(rawValue: rawKey.trimmed) else {
|
||||
print("Invalid key \(rawKey)")
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
return (key, rawValue.trimmed)
|
||||
}
|
||||
return process(result, markdown: markdown)
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import Foundation
|
||||
|
||||
struct AudioPlayerCommandProcessor: CommandProcessor {
|
||||
struct AudioPlayerCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .audioPlayer
|
||||
static let commandType: CommandType = .audioPlayer
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -15,14 +15,14 @@ struct AudioPlayerCommandProcessor: CommandProcessor {
|
||||
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 2 else {
|
||||
results.invalid(command: .audioPlayer, "Invalid audio player arguments")
|
||||
invalid("Invalid audio player arguments")
|
||||
return ""
|
||||
}
|
||||
let fileId = arguments[0]
|
||||
let titleText = arguments[1]
|
||||
|
||||
guard content.isValidIdForFile(fileId) else {
|
||||
results.invalid(command: .audioPlayer, "Invalid file id \(fileId) for audio player")
|
||||
invalid("Invalid file id \(fileId) for audio player")
|
||||
return ""
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct BoxCommandProcessor: CommandProcessor {
|
||||
struct BoxCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .box
|
||||
static let commandType: CommandType = .box
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
@ -14,7 +14,7 @@ struct BoxCommandProcessor: CommandProcessor {
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count > 1 else {
|
||||
results.invalid(command: .box, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let title = arguments[0]
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct ButtonCommandProcessor: CommandProcessor {
|
||||
struct ButtonCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .buttons
|
||||
static let commandType: CommandType = .buttons
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -27,7 +27,7 @@ struct ButtonCommandProcessor: CommandProcessor {
|
||||
|
||||
private func convert(button: String, markdown: Substring) -> ContentButtons.Item? {
|
||||
guard let type = PageIcon(rawValue: button.dropAfterFirst("=").trimmed) else {
|
||||
results.invalid(command: commandType, markdown)
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
let parts = button.dropBeforeFirst("=").components(separatedBy: ",").map { $0.trimmed }
|
||||
@ -41,14 +41,14 @@ struct ButtonCommandProcessor: CommandProcessor {
|
||||
case .buttonPlay:
|
||||
return play(arguments: parts, markdown: markdown)
|
||||
default:
|
||||
results.invalid(command: commandType, markdown)
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func download(arguments: [String], markdown: Substring) -> ContentButtons.Item? {
|
||||
guard (2...3).contains(arguments.count) else {
|
||||
results.invalid(command: commandType, markdown)
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
let fileId = arguments[0].trimmed
|
18
CHDataManagement/Generator/Commands/CommandProcessor.swift
Normal file
18
CHDataManagement/Generator/Commands/CommandProcessor.swift
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
protocol CommandProcessor {
|
||||
|
||||
static var commandType: CommandType { get }
|
||||
|
||||
var results: PageGenerationResults { get }
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
|
||||
|
||||
func process(_ arguments: [String], markdown: Substring) -> String
|
||||
}
|
||||
|
||||
extension CommandProcessor {
|
||||
|
||||
func invalid(_ markdown: Substring) {
|
||||
results.invalid(command: Self.commandType, markdown)
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import Foundation
|
||||
/**
|
||||
A string key used in markdown to indicate special elements
|
||||
*/
|
||||
enum ShorthandMarkdownKey: String {
|
||||
enum CommandType: String {
|
||||
|
||||
/// An image
|
||||
/// Format: ` -> String {
|
||||
guard arguments.count == 1 else {
|
||||
results.invalid(command: .includedHtml, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let fileId = arguments[0]
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct IconCommandProcessor: CommandProcessor {
|
||||
struct IconCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .icons
|
||||
static let commandType: CommandType = .icons
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
@ -13,7 +13,7 @@ struct IconCommandProcessor: CommandProcessor {
|
||||
var icons = [PageIcon]()
|
||||
for argument in arguments {
|
||||
guard let icon = PageIcon(rawValue: argument) else {
|
||||
results.invalid(command: .icons, markdown)
|
||||
invalid(markdown)
|
||||
continue
|
||||
}
|
||||
icons.append(icon)
|
@ -1,6 +1,7 @@
|
||||
struct ImageCommandProcessor: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .image
|
||||
struct ImageCommand: CommandProcessor {
|
||||
|
||||
static let commandType: CommandType = .image
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -27,7 +28,7 @@ struct ImageCommandProcessor: CommandProcessor {
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard (1...2).contains(arguments.count) else {
|
||||
results.invalid(command: .image, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let imageId = arguments[0]
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct ImageCompareCommandProcessor: CommandProcessor {
|
||||
struct ImageCompareCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .imageCompare
|
||||
static let commandType: CommandType = .imageCompare
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -17,7 +17,7 @@ struct ImageCompareCommandProcessor: CommandProcessor {
|
||||
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 2 else {
|
||||
results.invalid(command: .imageCompare, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let leftImageId = arguments[0]
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct LabelsCommandProcessor: CommandProcessor {
|
||||
struct LabelsCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .labels
|
||||
static let commandType: CommandType = .labels
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -16,11 +16,11 @@ struct LabelsCommandProcessor: CommandProcessor {
|
||||
let labels: [ContentLabel] = arguments.compactMap { arg in
|
||||
let parts = arg.components(separatedBy: "=")
|
||||
guard parts.count == 2 else {
|
||||
results.invalid(command: .labels, markdown)
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
guard let icon = PageIcon(rawValue: parts[0].trimmed) else {
|
||||
results.invalid(command: .labels, markdown)
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
results.require(icon: icon)
|
44
CHDataManagement/Generator/Commands/ModelCommand.swift
Normal file
44
CHDataManagement/Generator/Commands/ModelCommand.swift
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
struct ModelCommand: CommandProcessor {
|
||||
|
||||
static let commandType: CommandType = .model
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let fileId = arguments[0]
|
||||
guard fileId.hasSuffix(".glb") else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
guard let file = content.file(fileId) else {
|
||||
results.missing(file: fileId, source: "Model command")
|
||||
return ""
|
||||
}
|
||||
results.require(file: file)
|
||||
results.require(header: .modelViewer)
|
||||
|
||||
let description = file.localized(in: language)
|
||||
return ModelViewer(file: file.absoluteUrl, description: description).content
|
||||
}
|
||||
|
||||
|
||||
}
|
61
CHDataManagement/Generator/Commands/PageLinkCommand.swift
Normal file
61
CHDataManagement/Generator/Commands/PageLinkCommand.swift
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
struct PageLinkCommand: CommandProcessor {
|
||||
|
||||
static let commandType: CommandType = .pageLink
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let pageId = arguments[0]
|
||||
|
||||
guard let page = content.page(pageId) else {
|
||||
results.missing(page: pageId, source: "Page link command")
|
||||
return ""
|
||||
}
|
||||
guard !page.isDraft else {
|
||||
// Prevent linking to unpublished content
|
||||
return ""
|
||||
}
|
||||
|
||||
results.linked(to: page)
|
||||
|
||||
let localized = page.localized(in: language)
|
||||
let url = page.absoluteUrl(in: language)
|
||||
let title = localized.linkPreviewTitle ?? localized.title
|
||||
let description = localized.linkPreviewDescription ?? ""
|
||||
let image = makePageImage(item: localized)
|
||||
|
||||
return RelatedPageLink(
|
||||
title: title,
|
||||
description: description,
|
||||
url: url,
|
||||
image: image)
|
||||
.content
|
||||
}
|
||||
|
||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
||||
item.linkPreviewImage.map { image in
|
||||
let size = content.settings.pages.pageLinkImageSize
|
||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
||||
results.require(imageSet: imageSet)
|
||||
return imageSet
|
||||
}
|
||||
}
|
||||
}
|
52
CHDataManagement/Generator/Commands/SvgCommand.swift
Normal file
52
CHDataManagement/Generator/Commands/SvgCommand.swift
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
struct SvgCommand: CommandProcessor {
|
||||
|
||||
static let commandType: CommandType = .svg
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 5 else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
guard let x = Int(arguments[1]),
|
||||
let y = Int(arguments[2]),
|
||||
let partWidth = Int(arguments[3]),
|
||||
let partHeight = Int(arguments[4]) else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
let imageId = arguments[0]
|
||||
|
||||
guard let image = content.image(imageId) else {
|
||||
results.missing(file: imageId, source: "SVG command")
|
||||
return ""
|
||||
}
|
||||
guard image.type == .svg else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
return PartialSvgImage(
|
||||
imagePath: image.absoluteUrl,
|
||||
altText: image.localized(in: language),
|
||||
x: x,
|
||||
y: y,
|
||||
width: partWidth,
|
||||
height: partHeight)
|
||||
.content
|
||||
}
|
||||
}
|
55
CHDataManagement/Generator/Commands/TagLinkCommand.swift
Normal file
55
CHDataManagement/Generator/Commands/TagLinkCommand.swift
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
struct TagLinkCommand: CommandProcessor {
|
||||
|
||||
static let commandType: CommandType = .tagLink
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let tagId = arguments[0]
|
||||
|
||||
guard let tag = content.tag(tagId) else {
|
||||
results.missing(tag: tagId, source: "Tag link command")
|
||||
return ""
|
||||
}
|
||||
|
||||
let localized = tag.localized(in: language)
|
||||
let url = tag.absoluteUrl(in: language)
|
||||
let title = localized.name
|
||||
let description = localized.linkPreviewDescription ?? ""
|
||||
let image = makePageImage(item: localized)
|
||||
|
||||
return RelatedPageLink(
|
||||
title: title,
|
||||
description: description,
|
||||
url: url,
|
||||
image: image)
|
||||
.content
|
||||
}
|
||||
|
||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
||||
item.linkPreviewImage.map { image in
|
||||
let size = content.settings.pages.pageLinkImageSize
|
||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
||||
results.require(imageSet: imageSet)
|
||||
return imageSet
|
||||
}
|
||||
}
|
||||
}
|
129
CHDataManagement/Generator/Commands/VideoCommand+Option.swift
Normal file
129
CHDataManagement/Generator/Commands/VideoCommand+Option.swift
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
extension VideoCommand {
|
||||
|
||||
/// HTML video options
|
||||
enum Option {
|
||||
|
||||
/// Specifies that video controls should be displayed (such as a play/pause button etc).
|
||||
case controls
|
||||
|
||||
/// Specifies that the video will start playing as soon as it is ready
|
||||
case autoplay
|
||||
|
||||
/// Specifies that the video will start over again, every time it is finished
|
||||
case loop
|
||||
|
||||
/// Specifies that the audio output of the video should be muted
|
||||
case muted
|
||||
|
||||
/// Mobile browsers will play the video right where it is instead of the default, which is to open it up fullscreen while it plays
|
||||
case playsinline
|
||||
|
||||
/// Sets the height of the video player
|
||||
case height(Int)
|
||||
|
||||
/// Sets the width of the video player
|
||||
case width(Int)
|
||||
|
||||
/// Specifies if and how the author thinks the video should be loaded when the page loads
|
||||
case preload(Preload)
|
||||
|
||||
/// Specifies an image to be shown while the video is downloading, or until the user hits the play button
|
||||
case poster(image: String)
|
||||
|
||||
/// Specifies the URL of the video file
|
||||
case src(String)
|
||||
|
||||
init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "controls":
|
||||
self = .controls
|
||||
return
|
||||
case "autoplay":
|
||||
self = .autoplay
|
||||
return
|
||||
case "muted":
|
||||
self = .muted
|
||||
return
|
||||
case "loop":
|
||||
self = .loop
|
||||
return
|
||||
case "playsinline":
|
||||
self = .playsinline
|
||||
return
|
||||
default: break
|
||||
}
|
||||
|
||||
let parts = rawValue.components(separatedBy: "=")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let optionName = parts[0]
|
||||
let value = parts[1].removingSurroundingQuotes
|
||||
|
||||
switch optionName {
|
||||
case "height":
|
||||
guard let height = Int(value) else {
|
||||
return nil
|
||||
}
|
||||
self = .height(height)
|
||||
case "width":
|
||||
guard let width = Int(value) else {
|
||||
return nil
|
||||
}
|
||||
self = .width(width)
|
||||
case "preload":
|
||||
guard let preloadOption = Preload(rawValue: value) else {
|
||||
return nil
|
||||
}
|
||||
self = .preload(preloadOption)
|
||||
case "poster":
|
||||
self = .poster(image: value)
|
||||
case "src":
|
||||
self = .src(value)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var rawValue: String {
|
||||
switch self {
|
||||
case .controls: return "controls"
|
||||
case .autoplay: return "autoplay"
|
||||
case .muted: return "muted"
|
||||
case .loop: return "loop"
|
||||
case .playsinline: return "playsinline"
|
||||
case .height(let height): return "height='\(height)'"
|
||||
case .width(let width): return "width='\(width)'"
|
||||
case .preload(let option): return "preload='\(option)'"
|
||||
case .poster(let image): return "poster='\(image)'"
|
||||
case .src(let url): return "src='\(url)'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension VideoCommand.Option {
|
||||
|
||||
/**
|
||||
The `preload` attribute specifies if and how the author thinks that the video should be loaded when the page loads.
|
||||
|
||||
The `preload` attribute allows the author to provide a hint to the browser about what he/she thinks will lead to the best user experience.
|
||||
This attribute may be ignored in some instances.
|
||||
|
||||
Note: The `preload` attribute is ignored if `autoplay` is present.
|
||||
*/
|
||||
enum Preload: String {
|
||||
|
||||
/// The author thinks that the browser should load the entire video when the page loads
|
||||
case auto
|
||||
|
||||
/// The author thinks that the browser should load only metadata when the page loads
|
||||
case metadata
|
||||
|
||||
/// The author thinks that the browser should NOT load the video when the page loads
|
||||
case none
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
struct VideoCommandProcessor: CommandProcessor {
|
||||
struct VideoCommand: CommandProcessor {
|
||||
|
||||
let commandType: ShorthandMarkdownKey = .video
|
||||
static let commandType: CommandType = .video
|
||||
|
||||
let content: Content
|
||||
|
||||
@ -17,7 +17,7 @@ struct VideoCommandProcessor: CommandProcessor {
|
||||
*/
|
||||
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count >= 1 else {
|
||||
results.invalid(command: .video, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
let fileId = arguments[0].trimmed
|
||||
@ -28,11 +28,10 @@ struct VideoCommandProcessor: CommandProcessor {
|
||||
results.missing(file: fileId, source: "Video command")
|
||||
return ""
|
||||
}
|
||||
#warning("Create/specify video alternatives")
|
||||
results.require(file: file)
|
||||
|
||||
guard let videoType = file.type.htmlType else {
|
||||
results.invalid(command: .video, markdown)
|
||||
invalid(markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -43,12 +42,12 @@ struct VideoCommandProcessor: CommandProcessor {
|
||||
.content
|
||||
}
|
||||
|
||||
private func convertVideoOption(_ videoOption: String, markdown: Substring) -> VideoOption? {
|
||||
private func convertVideoOption(_ videoOption: String, markdown: Substring) -> Option? {
|
||||
guard let optionText = videoOption.trimmed.nonEmpty else {
|
||||
return nil
|
||||
}
|
||||
guard let option = VideoOption(rawValue: optionText) else {
|
||||
results.invalid(command: .video, markdown)
|
||||
guard let option = Option(rawValue: optionText) else {
|
||||
invalid(markdown)
|
||||
return nil
|
||||
}
|
||||
switch option {
|
@ -1,9 +1,10 @@
|
||||
import Ink
|
||||
|
||||
struct CodeBlockProcessor {
|
||||
struct MarkdownCodeProcessor: MarkdownProcessor {
|
||||
|
||||
private let codeHighlightFooter = "<script>hljs.highlightAll();</script>"
|
||||
static let modifier: Modifier.Target = .codeBlocks
|
||||
|
||||
let results: PageGenerationResults
|
||||
private let results: PageGenerationResults
|
||||
|
||||
private let blocks: [ContentBlock : BlockProcessor]
|
||||
|
||||
@ -18,16 +19,14 @@ struct CodeBlockProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
func process(_ html: String, markdown: Substring) -> String {
|
||||
private let codeHighlightFooter = "<script>hljs.highlightAll();</script>"
|
||||
|
||||
func process(html: String, markdown: Substring) -> String {
|
||||
let input = String(markdown)
|
||||
let rawBlockId = input.dropAfterFirst("\n").dropBeforeFirst("```").trimmed
|
||||
guard let blockId = ContentBlock(rawValue: rawBlockId) else {
|
||||
return other.process(html: html)
|
||||
}
|
||||
guard let processor = self.blocks[blockId] else {
|
||||
results.invalid(block: blockId, markdown)
|
||||
return ""
|
||||
}
|
||||
return processor.process(markdown)
|
||||
return blocks[blockId]!.process(markdown)
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import Ink
|
||||
|
||||
struct MarkdownHeadlineProcessor: MarkdownProcessor {
|
||||
|
||||
static let modifier: Modifier.Target = .headings
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
/**
|
||||
Modify headlines by extracting an id from the headline and adding it into the html element
|
||||
|
||||
Format: ##<title>#<id>
|
||||
|
||||
The id is created by lowercasing the string, removing all special characters, and replacing spaces with scores
|
||||
*/
|
||||
func process(html: String, markdown: Substring) -> String {
|
||||
let id = markdown
|
||||
.last(after: "#")
|
||||
.trimmed
|
||||
.filter { $0.isNumber || $0.isLetter || $0 == " " }
|
||||
.lowercased()
|
||||
.components(separatedBy: " ")
|
||||
.filter { $0 != "" }
|
||||
.joined(separator: "-")
|
||||
let parts = html.components(separatedBy: ">")
|
||||
return parts[0] + " id=\"\(id)\">" + parts.dropFirst().joined(separator: ">")
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import Ink
|
||||
|
||||
struct MarkdownImageProcessor: MarkdownProcessor {
|
||||
|
||||
static var modifier: Modifier.Target = .images
|
||||
|
||||
private let content: Content
|
||||
|
||||
private let results: PageGenerationResults
|
||||
|
||||
private let language: ContentLanguage
|
||||
|
||||
private let commands: [CommandType : CommandProcessor]
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
|
||||
self.commands = CommandType.allCases.reduce(into: [:]) { commands, command in
|
||||
commands[command] = command.processor.init(content: content, results: results, language: language)
|
||||
}
|
||||
}
|
||||
|
||||
var html: HtmlCommand {
|
||||
commands[.includedHtml] as! HtmlCommand
|
||||
}
|
||||
|
||||
func process(html: String, markdown: Substring) -> String {
|
||||
let argumentList = markdown.between(first: "](", andLast: ")").percentDecoded()
|
||||
let arguments = argumentList.components(separatedBy: ";")
|
||||
|
||||
let rawCommand = markdown.between("![", and: "]").trimmed.percentDecoded()
|
||||
guard rawCommand != "" else {
|
||||
return commands[.image]!.process(arguments, markdown: markdown)
|
||||
}
|
||||
|
||||
guard let command = CommandType(rawValue: rawCommand) else {
|
||||
// Treat unknown commands as normal links
|
||||
results.invalid(command: nil, markdown)
|
||||
return html
|
||||
}
|
||||
return commands[command]!.process(arguments, markdown: markdown)
|
||||
}
|
||||
}
|
@ -1,5 +1,20 @@
|
||||
import Ink
|
||||
|
||||
struct InlineLinkProcessor {
|
||||
struct MarkdownLinkProcessor: MarkdownProcessor {
|
||||
|
||||
static let modifier: Modifier.Target = .links
|
||||
|
||||
private let content: Content
|
||||
|
||||
private let results: PageGenerationResults
|
||||
|
||||
private let language: ContentLanguage
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
}
|
||||
|
||||
private let pageLinkMarker = "page:"
|
||||
|
||||
@ -7,12 +22,6 @@ struct InlineLinkProcessor {
|
||||
|
||||
private let fileLinkMarker = "file:"
|
||||
|
||||
let content: Content
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
let language: ContentLanguage
|
||||
|
||||
func process(html: String, markdown: Substring) -> String {
|
||||
let url = markdown.between("(", and: ")")
|
||||
if url.hasPrefix(pageLinkMarker) {
|
10
CHDataManagement/Generator/Markdown/MarkdownProcessor.swift
Normal file
10
CHDataManagement/Generator/Markdown/MarkdownProcessor.swift
Normal file
@ -0,0 +1,10 @@
|
||||
import Ink
|
||||
|
||||
protocol MarkdownProcessor {
|
||||
|
||||
static var modifier: Modifier.Target { get }
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
|
||||
|
||||
func process(html: String, markdown: Substring) -> String
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
|
||||
protocol CommandProcessor {
|
||||
|
||||
var commandType: ShorthandMarkdownKey { get }
|
||||
|
||||
init(content: Content, results: PageGenerationResults, language: ContentLanguage)
|
||||
|
||||
func process(_ arguments: [String], markdown: Substring) -> String
|
||||
}
|
@ -9,257 +9,35 @@ final class PageContentParser {
|
||||
|
||||
private let results: PageGenerationResults
|
||||
|
||||
// MARK: Command handlers
|
||||
// MARK: Markdown handlers
|
||||
|
||||
private let buttonHandler: ButtonCommandProcessor
|
||||
private let code: MarkdownCodeProcessor
|
||||
|
||||
private let labelHandler: LabelsCommandProcessor
|
||||
private let headlines: MarkdownHeadlineProcessor
|
||||
|
||||
private let audioPlayer: AudioPlayerCommandProcessor
|
||||
private let image: MarkdownImageProcessor
|
||||
|
||||
private let icons: IconCommandProcessor
|
||||
|
||||
private let box: BoxCommandProcessor
|
||||
|
||||
private let html: PageHtmlProcessor
|
||||
|
||||
private let video: VideoCommandProcessor
|
||||
|
||||
private let imageCompare: ImageCompareCommandProcessor
|
||||
|
||||
private let images: ImageCommandProcessor
|
||||
|
||||
// MARK: Other handlers
|
||||
|
||||
private let inlineLink: InlineLinkProcessor
|
||||
|
||||
private let code: CodeBlockProcessor
|
||||
private let link: MarkdownLinkProcessor
|
||||
|
||||
init(content: Content, language: ContentLanguage, results: PageGenerationResults) {
|
||||
self.content = content
|
||||
self.results = results
|
||||
self.language = language
|
||||
self.buttonHandler = .init(content: content, results: results, language: language)
|
||||
self.labelHandler = .init(content: content, results: results, language: language)
|
||||
self.audioPlayer = .init(content: content, results: results, language: language)
|
||||
self.icons = .init(content: content, results: results, language: language)
|
||||
self.box = .init(content: content, results: results, language: language)
|
||||
self.html = .init(content: content, results: results, language: language)
|
||||
self.video = .init(content: content, results: results, language: language)
|
||||
self.imageCompare = .init(content: content, results: results, language: language)
|
||||
self.images = .init(content: content, results: results, language: language)
|
||||
|
||||
self.inlineLink = .init(content: content, results: results, language: language)
|
||||
self.code = .init(content: content, results: results, language: language)
|
||||
self.headlines = .init(content: content, results: results, language: language)
|
||||
self.image = .init(content: content, results: results, language: language)
|
||||
self.link = .init(content: content, results: results, language: language)
|
||||
}
|
||||
|
||||
func generatePage(from content: String) -> String {
|
||||
let parser = MarkdownParser(modifiers: [
|
||||
Modifier(target: .images, closure: processMarkdownImage),
|
||||
Modifier(target: .images, closure: image.process),
|
||||
Modifier(target: .codeBlocks, closure: code.process),
|
||||
Modifier(target: .links, closure: inlineLink.process),
|
||||
Modifier(target: .html, closure: html.process),
|
||||
Modifier(target: .headings, closure: handleHeadlines)
|
||||
Modifier(target: .links, closure: link.process),
|
||||
Modifier(target: .html, closure: image.html.process),
|
||||
Modifier(target: .headings, closure: headlines.process)
|
||||
])
|
||||
return parser.html(from: content)
|
||||
}
|
||||
|
||||
/**
|
||||
Modify headlines by extracting an id from the headline and adding it into the html element
|
||||
|
||||
Format: ##<title>#<id>
|
||||
|
||||
The id is created by lowercasing the string, removing all special characters, and replacing spaces with scores
|
||||
*/
|
||||
private func handleHeadlines(html: String, markdown: Substring) -> String {
|
||||
let id = markdown
|
||||
.last(after: "#")
|
||||
.trimmed
|
||||
.filter { $0.isNumber || $0.isLetter || $0 == " " }
|
||||
.lowercased()
|
||||
.components(separatedBy: " ")
|
||||
.filter { $0 != "" }
|
||||
.joined(separator: "-")
|
||||
let parts = html.components(separatedBy: ">")
|
||||
return parts[0] + " id=\"\(id)\">" + parts.dropFirst().joined(separator: ">")
|
||||
}
|
||||
|
||||
private func processMarkdownImage(html: String, markdown: Substring) -> String {
|
||||
//
|
||||
let argumentList = markdown.between(first: "](", andLast: ")").percentDecoded()
|
||||
let arguments = argumentList.components(separatedBy: ";")
|
||||
|
||||
let rawCommand = markdown.between("![", and: "]").trimmed.percentDecoded()
|
||||
guard rawCommand != "" else {
|
||||
return images.process(arguments, markdown: markdown)
|
||||
}
|
||||
|
||||
guard let command = ShorthandMarkdownKey(rawValue: rawCommand) else {
|
||||
// Treat unknown commands as normal links
|
||||
results.invalid(command: nil, markdown)
|
||||
return html
|
||||
}
|
||||
|
||||
switch command {
|
||||
case .image:
|
||||
return images.process(arguments, markdown: markdown)
|
||||
case .labels:
|
||||
return labelHandler.process(arguments, markdown: markdown)
|
||||
case .buttons:
|
||||
return buttonHandler.process(arguments, markdown: markdown)
|
||||
case .video:
|
||||
return video.process(arguments, markdown: markdown)
|
||||
case .pageLink:
|
||||
return handlePageLink(arguments, markdown: markdown)
|
||||
case .includedHtml:
|
||||
return self.html.process(arguments, markdown: markdown)
|
||||
case .box:
|
||||
return box.process(arguments, markdown: markdown)
|
||||
case .model:
|
||||
return handleModel(arguments, markdown: markdown)
|
||||
case .svg:
|
||||
return handleSvg(arguments, markdown: markdown)
|
||||
case .audioPlayer:
|
||||
return audioPlayer.process(arguments, markdown: markdown)
|
||||
case .tagLink:
|
||||
return handleTagLink(arguments, markdown: markdown)
|
||||
case .icons:
|
||||
return icons.process(arguments, markdown: markdown)
|
||||
case .imageCompare:
|
||||
return imageCompare.process(arguments, markdown: markdown)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
private func handlePageLink(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
results.invalid(command: .pageLink, markdown)
|
||||
return ""
|
||||
}
|
||||
let pageId = arguments[0]
|
||||
|
||||
guard let page = content.page(pageId) else {
|
||||
results.missing(page: pageId, source: "Page link command")
|
||||
return ""
|
||||
}
|
||||
guard !page.isDraft else {
|
||||
// Prevent linking to unpublished content
|
||||
return ""
|
||||
}
|
||||
|
||||
results.linked(to: page)
|
||||
|
||||
let localized = page.localized(in: language)
|
||||
let url = page.absoluteUrl(in: language)
|
||||
let title = localized.linkPreviewTitle ?? localized.title
|
||||
let description = localized.linkPreviewDescription ?? ""
|
||||
let image = makePageImage(item: localized)
|
||||
|
||||
return RelatedPageLink(
|
||||
title: title,
|
||||
description: description,
|
||||
url: url,
|
||||
image: image)
|
||||
.content
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
private func handleTagLink(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
results.invalid(command: .tagLink, markdown)
|
||||
return ""
|
||||
}
|
||||
let tagId = arguments[0]
|
||||
|
||||
guard let tag = content.tag(tagId) else {
|
||||
results.missing(tag: tagId, source: "Tag link command")
|
||||
return ""
|
||||
}
|
||||
|
||||
let localized = tag.localized(in: language)
|
||||
let url = tag.absoluteUrl(in: language)
|
||||
let title = localized.name
|
||||
let description = localized.linkPreviewDescription ?? ""
|
||||
let image = makePageImage(item: localized)
|
||||
|
||||
return RelatedPageLink(
|
||||
title: title,
|
||||
description: description,
|
||||
url: url,
|
||||
image: image)
|
||||
.content
|
||||
}
|
||||
|
||||
private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
|
||||
item.linkPreviewImage.map { image in
|
||||
let size = content.settings.pages.pageLinkImageSize
|
||||
let imageSet = image.imageSet(width: size, height: size, language: language)
|
||||
results.require(imageSet: imageSet)
|
||||
return imageSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Format: ``
|
||||
*/
|
||||
private func handleModel(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 1 else {
|
||||
results.invalid(command: .model, markdown)
|
||||
return ""
|
||||
}
|
||||
let fileId = arguments[0]
|
||||
guard fileId.hasSuffix(".glb") else {
|
||||
results.invalid(command: .model, markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
guard let file = content.file(fileId) else {
|
||||
results.missing(file: fileId, source: "Model command")
|
||||
return ""
|
||||
}
|
||||
results.require(file: file)
|
||||
results.require(header: .modelViewer)
|
||||
|
||||
let description = file.localized(in: language)
|
||||
return ModelViewer(file: file.absoluteUrl, description: description).content
|
||||
}
|
||||
|
||||
private func handleSvg(_ arguments: [String], markdown: Substring) -> String {
|
||||
guard arguments.count == 5 else {
|
||||
results.invalid(command: .svg, markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
guard let x = Int(arguments[1]),
|
||||
let y = Int(arguments[2]),
|
||||
let partWidth = Int(arguments[3]),
|
||||
let partHeight = Int(arguments[4]) else {
|
||||
results.invalid(command: .svg, markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
let imageId = arguments[0]
|
||||
|
||||
guard let image = content.image(imageId) else {
|
||||
results.missing(file: imageId, source: "SVG command")
|
||||
return ""
|
||||
}
|
||||
guard image.type == .svg else {
|
||||
results.invalid(command: .svg, markdown)
|
||||
return ""
|
||||
}
|
||||
|
||||
return PartialSvgImage(
|
||||
imagePath: image.absoluteUrl,
|
||||
altText: image.localized(in: language),
|
||||
x: x,
|
||||
y: y,
|
||||
width: partWidth,
|
||||
height: partHeight)
|
||||
.content
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ enum GenerationAnomaly {
|
||||
case missingFile(file: String, markdown: String)
|
||||
case missingPage(page: String, markdown: String)
|
||||
case missingTag(tag: String, markdown: String)
|
||||
case invalidCommand(command: ShorthandMarkdownKey?, markdown: String)
|
||||
case invalidCommand(command: CommandType?, markdown: String)
|
||||
case warning(String)
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ final class PageGenerationResults: ObservableObject {
|
||||
/// The image versions required for this page
|
||||
private(set) var imagesToGenerate: Set<ImageVersion>
|
||||
|
||||
private(set) var invalidCommands: [(command: ShorthandMarkdownKey?, markdown: String)]
|
||||
private(set) var invalidCommands: [(command: CommandType?, markdown: String)]
|
||||
|
||||
private(set) var invalidBlocks: [(block: ContentBlock?, markdown: String)]
|
||||
|
||||
@ -132,7 +132,7 @@ final class PageGenerationResults: ObservableObject {
|
||||
delegate.inaccessibleContent(file: file)
|
||||
}
|
||||
|
||||
func invalid(command: ShorthandMarkdownKey?, _ markdown: Substring) {
|
||||
func invalid(command: CommandType?, _ markdown: Substring) {
|
||||
let markdown = String(markdown)
|
||||
invalidCommands.append((command, markdown))
|
||||
delegate.invalidCommand(markdown)
|
@ -1,123 +0,0 @@
|
||||
|
||||
/// HTML video options
|
||||
enum VideoOption {
|
||||
|
||||
/// Specifies that video controls should be displayed (such as a play/pause button etc).
|
||||
case controls
|
||||
|
||||
/// Specifies that the video will start playing as soon as it is ready
|
||||
case autoplay
|
||||
|
||||
/// Specifies that the video will start over again, every time it is finished
|
||||
case loop
|
||||
|
||||
/// Specifies that the audio output of the video should be muted
|
||||
case muted
|
||||
|
||||
/// Mobile browsers will play the video right where it is instead of the default, which is to open it up fullscreen while it plays
|
||||
case playsinline
|
||||
|
||||
/// Sets the height of the video player
|
||||
case height(Int)
|
||||
|
||||
/// Sets the width of the video player
|
||||
case width(Int)
|
||||
|
||||
/// Specifies if and how the author thinks the video should be loaded when the page loads
|
||||
case preload(VideoPreloadOption)
|
||||
|
||||
/// Specifies an image to be shown while the video is downloading, or until the user hits the play button
|
||||
case poster(image: String)
|
||||
|
||||
/// Specifies the URL of the video file
|
||||
case src(String)
|
||||
|
||||
init?(rawValue: String) {
|
||||
switch rawValue {
|
||||
case "controls":
|
||||
self = .controls
|
||||
return
|
||||
case "autoplay":
|
||||
self = .autoplay
|
||||
return
|
||||
case "muted":
|
||||
self = .muted
|
||||
return
|
||||
case "loop":
|
||||
self = .loop
|
||||
return
|
||||
case "playsinline":
|
||||
self = .playsinline
|
||||
return
|
||||
default: break
|
||||
}
|
||||
|
||||
let parts = rawValue.components(separatedBy: "=")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let optionName = parts[0]
|
||||
let value = parts[1].removingSurroundingQuotes
|
||||
|
||||
switch optionName {
|
||||
case "height":
|
||||
guard let height = Int(value) else {
|
||||
return nil
|
||||
}
|
||||
self = .height(height)
|
||||
case "width":
|
||||
guard let width = Int(value) else {
|
||||
return nil
|
||||
}
|
||||
self = .width(width)
|
||||
case "preload":
|
||||
guard let preloadOption = VideoPreloadOption(rawValue: value) else {
|
||||
return nil
|
||||
}
|
||||
self = .preload(preloadOption)
|
||||
case "poster":
|
||||
self = .poster(image: value)
|
||||
case "src":
|
||||
self = .src(value)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var rawValue: String {
|
||||
switch self {
|
||||
case .controls: return "controls"
|
||||
case .autoplay: return "autoplay"
|
||||
case .muted: return "muted"
|
||||
case .loop: return "loop"
|
||||
case .playsinline: return "playsinline"
|
||||
case .height(let height): return "height='\(height)'"
|
||||
case .width(let width): return "width='\(width)'"
|
||||
case .preload(let option): return "preload='\(option)'"
|
||||
case .poster(let image): return "poster='\(image)'"
|
||||
case .src(let url): return "src='\(url)'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The `preload` attribute specifies if and how the author thinks that the video should be loaded when the page loads.
|
||||
|
||||
The `preload` attribute allows the author to provide a hint to the browser about what he/she thinks will lead to the best user experience.
|
||||
This attribute may be ignored in some instances.
|
||||
|
||||
Note: The `preload` attribute is ignored if `autoplay` is present.
|
||||
*/
|
||||
enum VideoPreloadOption: String {
|
||||
|
||||
/// The author thinks that the browser should load the entire video when the page loads
|
||||
case auto
|
||||
|
||||
/// The author thinks that the browser should load only metadata when the page loads
|
||||
case metadata
|
||||
|
||||
/// The author thinks that the browser should NOT load the video when the page loads
|
||||
case none
|
||||
}
|
@ -3,8 +3,6 @@ import SFSafeSymbols
|
||||
|
||||
/**
|
||||
**Content**
|
||||
- Podcast: Fix audio player, preview image
|
||||
- Article Cap Mosaic: -> GIF feature
|
||||
- iPhone Backgrounds: Add page, html
|
||||
|
||||
**UI**
|
||||
@ -20,7 +18,7 @@ import SFSafeSymbols
|
||||
- Posts: Generate separate pages for posts to link to
|
||||
- Settings: Introduce `Authors` (`name`, `image`, `description`)
|
||||
- Page: Property `author`
|
||||
- Video: Specify versions
|
||||
- Video: Specify versions -> Block
|
||||
|
||||
**Generation**
|
||||
- ImageSet: Specify image aspect ratio (width, height) to prevent page jumps
|
||||
|
@ -5,7 +5,7 @@ struct ContentPageVideo: HtmlProducer {
|
||||
|
||||
let videoType: String
|
||||
|
||||
let options: [VideoOption]
|
||||
let options: [VideoCommand.Option]
|
||||
|
||||
func populate(_ result: inout String) {
|
||||
result += "<video\(optionString)>Video not supported."
|
||||
|
Loading…
x
Reference in New Issue
Block a user