Update icons, hide page title
This commit is contained in:
parent
1e4682dad1
commit
922ba4ebe7
@ -193,6 +193,9 @@
|
|||||||
E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; };
|
E2FE0EE82D16D4A3002963B7 /* ConvertThrowing.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */; };
|
||||||
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; };
|
E2FE0EEC2D1C1253002963B7 /* MultiFileSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EEB2D1C124E002963B7 /* MultiFileSelectionView.swift */; };
|
||||||
E2FE0EEE2D1C22F3002963B7 /* InlineLinkProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */; };
|
E2FE0EEE2D1C22F3002963B7 /* InlineLinkProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.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 */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@ -377,6 +380,9 @@
|
|||||||
E2FE0EE72D16D4A3002963B7 /* ConvertThrowing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertThrowing.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>"; };
|
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 /* InlineLinkProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineLinkProcessor.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>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -520,6 +526,8 @@
|
|||||||
E29D31AB2D0DA52C0051B7F4 /* Icons */ = {
|
E29D31AB2D0DA52C0051B7F4 /* Icons */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2FE0EF52D1D6DEE002963B7 /* Icon.swift */,
|
||||||
|
E2FE0EF32D1D6D22002963B7 /* GeneralIcons.swift */,
|
||||||
E29D31B42D0DA8490051B7F4 /* PageIcon.swift */,
|
E29D31B42D0DA8490051B7F4 /* PageIcon.swift */,
|
||||||
E29D31B22D0DA6E50051B7F4 /* ButtonIcons.swift */,
|
E29D31B22D0DA6E50051B7F4 /* ButtonIcons.swift */,
|
||||||
E29D31B02D0DA5510051B7F4 /* ContentIcon.swift */,
|
E29D31B02D0DA5510051B7F4 /* ContentIcon.swift */,
|
||||||
@ -532,6 +540,7 @@
|
|||||||
E29D31B62D0DAC030051B7F4 /* Page Content */ = {
|
E29D31B62D0DAC030051B7F4 /* Page Content */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E2FE0EF72D1D810C002963B7 /* IconCommandProcessor.swift */,
|
||||||
E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */,
|
E2FE0EED2D1C22EF002963B7 /* InlineLinkProcessor.swift */,
|
||||||
E29D31BD2D0DB8560051B7F4 /* AudioPlayerCommand.swift */,
|
E29D31BD2D0DB8560051B7F4 /* AudioPlayerCommand.swift */,
|
||||||
E29D31BB2D0DB5110051B7F4 /* CommandProcessor.swift */,
|
E29D31BB2D0DB5110051B7F4 /* CommandProcessor.swift */,
|
||||||
@ -905,6 +914,7 @@
|
|||||||
E2A37D192CEA36A90000979F /* LocalizedTag.swift in Sources */,
|
E2A37D192CEA36A90000979F /* LocalizedTag.swift in Sources */,
|
||||||
E25DA59B2D024A2B00AEF16D /* DateItem.swift in Sources */,
|
E25DA59B2D024A2B00AEF16D /* DateItem.swift in Sources */,
|
||||||
E29D31902D0B34870051B7F4 /* GenerationAnomaly.swift in Sources */,
|
E29D31902D0B34870051B7F4 /* GenerationAnomaly.swift in Sources */,
|
||||||
|
E2FE0EF42D1D6D2E002963B7 /* GeneralIcons.swift in Sources */,
|
||||||
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */,
|
E25DA5172CFF00F500AEF16D /* Content+Save.swift in Sources */,
|
||||||
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */,
|
E229902E2D0F7280009F8D77 /* IdPropertyView.swift in Sources */,
|
||||||
E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */,
|
E29D31AD2D0DA5360051B7F4 /* AudioPlayerIcons.swift in Sources */,
|
||||||
@ -956,6 +966,7 @@
|
|||||||
E21850392CFCA6C00090B18B /* WebsiteData+Mock.swift in Sources */,
|
E21850392CFCA6C00090B18B /* WebsiteData+Mock.swift in Sources */,
|
||||||
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */,
|
E229901E2D0E4364009F8D77 /* LocalizedItem.swift in Sources */,
|
||||||
E29D31262D0370A80051B7F4 /* VideoOption.swift in Sources */,
|
E29D31262D0370A80051B7F4 /* VideoOption.swift in Sources */,
|
||||||
|
E2FE0EF82D1D8110002963B7 /* IconCommandProcessor.swift in Sources */,
|
||||||
E21850272CF3B42D0090B18B /* PostDetailView.swift in Sources */,
|
E21850272CF3B42D0090B18B /* PostDetailView.swift in Sources */,
|
||||||
E2A37D172CE73F1A0000979F /* TagFile.swift in Sources */,
|
E2A37D172CE73F1A0000979F /* TagFile.swift in Sources */,
|
||||||
E29D318E2D0B2E680051B7F4 /* PageSettingsContentView.swift in Sources */,
|
E29D318E2D0B2E680051B7F4 /* PageSettingsContentView.swift in Sources */,
|
||||||
@ -1009,6 +1020,7 @@
|
|||||||
E218500B2CEE02FD0090B18B /* Content+Mock.swift in Sources */,
|
E218500B2CEE02FD0090B18B /* Content+Mock.swift in Sources */,
|
||||||
E29D31472D04892E0051B7F4 /* FileListView.swift in Sources */,
|
E29D31472D04892E0051B7F4 /* FileListView.swift in Sources */,
|
||||||
E218503D2CFCFD910090B18B /* LocalizedPostFeedSettingsView.swift in Sources */,
|
E218503D2CFCFD910090B18B /* LocalizedPostFeedSettingsView.swift in Sources */,
|
||||||
|
E2FE0EF62D1D6DF1002963B7 /* Icon.swift in Sources */,
|
||||||
E29D31A52D0CD03F0051B7F4 /* FileSelectionView.swift in Sources */,
|
E29D31A52D0CD03F0051B7F4 /* FileSelectionView.swift in Sources */,
|
||||||
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */,
|
E22990322D0F767B009F8D77 /* DatePropertyView.swift in Sources */,
|
||||||
E2A21C302CB490F90060935B /* HorizontalCenter.swift in Sources */,
|
E2A21C302CB490F90060935B /* HorizontalCenter.swift in Sources */,
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
struct IconCommandProcessor: CommandProcessor {
|
||||||
|
|
||||||
|
let commandType: ShorthandMarkdownKey = .icons
|
||||||
|
|
||||||
|
|
||||||
|
let results: PageGenerationResults
|
||||||
|
|
||||||
|
init(content: Content, results: PageGenerationResults) {
|
||||||
|
self.results = results
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(_ arguments: [String], markdown: Substring) -> String {
|
||||||
|
var icons = [PageIcon]()
|
||||||
|
for argument in arguments {
|
||||||
|
guard let icon = PageIcon(rawValue: argument) else {
|
||||||
|
results.invalid(command: .icons, markdown)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
icons.append(icon)
|
||||||
|
}
|
||||||
|
results.require(icons: icons)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,8 @@ final class PageContentParser {
|
|||||||
|
|
||||||
private let inlineLink: InlineLinkProcessor
|
private let inlineLink: InlineLinkProcessor
|
||||||
|
|
||||||
|
private let icons: IconCommandProcessor
|
||||||
|
|
||||||
var largeImageWidth: Int {
|
var largeImageWidth: Int {
|
||||||
content.settings.pages.largeImageWidth
|
content.settings.pages.largeImageWidth
|
||||||
}
|
}
|
||||||
@ -39,6 +41,7 @@ final class PageContentParser {
|
|||||||
self.labelHandler = .init(content: content, results: results)
|
self.labelHandler = .init(content: content, results: results)
|
||||||
self.audioPlayer = .init(content: content, results: results)
|
self.audioPlayer = .init(content: content, results: results)
|
||||||
self.inlineLink = .init(content: content, results: results, language: language)
|
self.inlineLink = .init(content: content, results: results, language: language)
|
||||||
|
self.icons = .init(content: content, results: results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePage(from content: String) -> String {
|
func generatePage(from content: String) -> String {
|
||||||
@ -226,6 +229,8 @@ final class PageContentParser {
|
|||||||
return audioPlayer.process(arguments, markdown: markdown)
|
return audioPlayer.process(arguments, markdown: markdown)
|
||||||
case .tagLink:
|
case .tagLink:
|
||||||
return handleTagLink(arguments, markdown: markdown)
|
return handleTagLink(arguments, markdown: markdown)
|
||||||
|
case .icons:
|
||||||
|
return icons.process(arguments, markdown: markdown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ final class PageGenerator {
|
|||||||
language: language,
|
language: language,
|
||||||
dateString: page.dateText(in: language),
|
dateString: page.dateText(in: language),
|
||||||
title: localized.title,
|
title: localized.title,
|
||||||
|
showTitle: !localized.hideTitle,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
linkTitle: localized.linkPreviewTitle ?? localized.title,
|
linkTitle: localized.linkPreviewTitle ?? localized.title,
|
||||||
description: localized.linkPreviewDescription ?? "",
|
description: localized.linkPreviewDescription ?? "",
|
||||||
|
@ -49,4 +49,8 @@ enum ShorthandMarkdownKey: String {
|
|||||||
/// Format: ``
|
/// Format: ``
|
||||||
case audioPlayer = "audio-player"
|
case audioPlayer = "audio-player"
|
||||||
|
|
||||||
|
/// Add svg icons to the page for use in html components
|
||||||
|
/// Format: ``
|
||||||
|
case icons
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,15 @@ extension Content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func endCurrentGeneration() {
|
||||||
|
guard isGeneratingWebsite, shouldGenerateWebsite else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.set(shouldGenerate: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func generatePostFeedPages() {
|
func generatePostFeedPages() {
|
||||||
performGenerationIfIdle {
|
performGenerationIfIdle {
|
||||||
self.generatePostFeedPagesInternal()
|
self.generatePostFeedPagesInternal()
|
||||||
@ -36,6 +45,7 @@ extension Content {
|
|||||||
let count = results.requiredFiles.count
|
let count = results.requiredFiles.count
|
||||||
var completed = 0
|
var completed = 0
|
||||||
for file in results.requiredFiles {
|
for file in results.requiredFiles {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
defer {
|
defer {
|
||||||
completed += 1
|
completed += 1
|
||||||
status("Copying required files: \(completed) / \(count)")
|
status("Copying required files: \(completed) / \(count)")
|
||||||
@ -59,6 +69,7 @@ extension Content {
|
|||||||
let count = images.count
|
let count = images.count
|
||||||
var completed = 0
|
var completed = 0
|
||||||
for image in images {
|
for image in images {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
defer {
|
defer {
|
||||||
completed += 1
|
completed += 1
|
||||||
status("Generating required images: \(completed) / \(count)")
|
status("Generating required images: \(completed) / \(count)")
|
||||||
@ -156,10 +167,12 @@ extension Content {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.set(isGenerating: true)
|
self.set(isGenerating: true)
|
||||||
|
self.set(shouldGenerate: true)
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
operation()
|
operation()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.set(isGenerating: false)
|
self.set(isGenerating: false)
|
||||||
|
self.set(shouldGenerate: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,6 +190,7 @@ extension Content {
|
|||||||
private func generatePagesInternal() {
|
private func generatePagesInternal() {
|
||||||
let count = pages.count
|
let count = pages.count
|
||||||
for index in pages.indices {
|
for index in pages.indices {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
let page = pages[index]
|
let page = pages[index]
|
||||||
status("Generating pages: \(index) / \(count)")
|
status("Generating pages: \(index) / \(count)")
|
||||||
guard !page.isExternalUrl else {
|
guard !page.isExternalUrl else {
|
||||||
@ -197,6 +211,7 @@ extension Content {
|
|||||||
private func generatePostFeedPagesInternal() {
|
private func generatePostFeedPagesInternal() {
|
||||||
status("Generating post feed")
|
status("Generating post feed")
|
||||||
for language in ContentLanguage.allCases {
|
for language in ContentLanguage.allCases {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
let results = results.makeResults(for: .feed, in: language)
|
let results = results.makeResults(for: .feed, in: language)
|
||||||
let generator = PostListPageGenerator(
|
let generator = PostListPageGenerator(
|
||||||
language: language,
|
language: language,
|
||||||
@ -217,6 +232,7 @@ extension Content {
|
|||||||
private func generateTagPagesInternal() {
|
private func generateTagPagesInternal() {
|
||||||
let count = tags.count
|
let count = tags.count
|
||||||
for index in tags.indices {
|
for index in tags.indices {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
let tag = tags[index]
|
let tag = tags[index]
|
||||||
status("Generating tag pages: \(index) / \(count)")
|
status("Generating tag pages: \(index) / \(count)")
|
||||||
generatePagesInternal(for: tag)
|
generatePagesInternal(for: tag)
|
||||||
@ -253,6 +269,7 @@ extension Content {
|
|||||||
private func generateTagOverviewPagesInternal() {
|
private func generateTagOverviewPagesInternal() {
|
||||||
status("Generating tag overview page")
|
status("Generating tag overview page")
|
||||||
for language in ContentLanguage.allCases {
|
for language in ContentLanguage.allCases {
|
||||||
|
guard shouldGenerateWebsite else { return }
|
||||||
let results = results.makeResults(for: .tagOverview, in: language)
|
let results = results.makeResults(for: .tagOverview, in: language)
|
||||||
#warning("Create layout for tag overview page")
|
#warning("Create layout for tag overview page")
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ extension Content {
|
|||||||
originalUrl: page.originalURL,
|
originalUrl: page.originalURL,
|
||||||
linkPreviewImage: page.linkPreviewImage.map { images[$0] },
|
linkPreviewImage: page.linkPreviewImage.map { images[$0] },
|
||||||
linkPreviewTitle: page.linkPreviewTitle,
|
linkPreviewTitle: page.linkPreviewTitle,
|
||||||
linkPreviewDescription: page.linkPreviewDescription)
|
linkPreviewDescription: page.linkPreviewDescription,
|
||||||
|
hideTitle: page.hideTitle ?? false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFromDisk() throws {
|
func loadFromDisk() throws {
|
||||||
@ -204,8 +205,8 @@ extension Content {
|
|||||||
endDate: page.endDate,
|
endDate: page.endDate,
|
||||||
german: convert(page.german, images: files),
|
german: convert(page.german, images: files),
|
||||||
english: convert(page.english, images: files),
|
english: convert(page.english, images: files),
|
||||||
tags: page.tags.map { tags[$0]! },
|
tags: page.tags.compactMap { tags[$0] },
|
||||||
requiredFiles: page.requiredFiles?.map { files[$0]! } ?? [])
|
requiredFiles: page.requiredFiles?.compactMap { files[$0] } ?? [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,8 @@ private extension LocalizedPage {
|
|||||||
linkPreviewTitle: linkPreviewTitle,
|
linkPreviewTitle: linkPreviewTitle,
|
||||||
linkPreviewDescription: linkPreviewDescription,
|
linkPreviewDescription: linkPreviewDescription,
|
||||||
lastModifiedDate: lastModified,
|
lastModifiedDate: lastModified,
|
||||||
originalURL: originalUrl)
|
originalURL: originalUrl,
|
||||||
|
hideTitle: hideTitle ? true : nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ final class Content: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
private(set) var isGeneratingWebsite = false
|
private(set) var isGeneratingWebsite = false
|
||||||
|
|
||||||
|
private(set) var shouldGenerateWebsite = false
|
||||||
|
|
||||||
init(settings: Settings,
|
init(settings: Settings,
|
||||||
posts: [Post],
|
posts: [Post],
|
||||||
pages: [Page],
|
pages: [Page],
|
||||||
@ -84,6 +86,10 @@ final class Content: ObservableObject {
|
|||||||
self.isGeneratingWebsite = isGenerating
|
self.isGeneratingWebsite = isGenerating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func set(shouldGenerate: Bool) {
|
||||||
|
self.shouldGenerateWebsite = shouldGenerate
|
||||||
|
}
|
||||||
|
|
||||||
func add(_ file: FileResource) {
|
func add(_ file: FileResource) {
|
||||||
// TODO: Insert at correct index?
|
// TODO: Insert at correct index?
|
||||||
files.insert(file, at: 0)
|
files.insert(file, at: 0)
|
||||||
|
@ -95,7 +95,7 @@ final class FileResource: Item {
|
|||||||
return content.settings.paths.videosOutputFolderPath
|
return content.settings.paths.videosOutputFolderPath
|
||||||
}
|
}
|
||||||
if type.isAudio {
|
if type.isAudio {
|
||||||
|
return content.settings.paths.audioOutputFolderPath
|
||||||
}
|
}
|
||||||
if type.isAsset {
|
if type.isAsset {
|
||||||
return content.settings.paths.assetsOutputFolderPath
|
return content.settings.paths.assetsOutputFolderPath
|
||||||
|
@ -43,6 +43,9 @@ final class LocalizedPage: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
var linkPreviewDescription: String?
|
var linkPreviewDescription: String?
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var hideTitle: Bool
|
||||||
|
|
||||||
init(content: Content,
|
init(content: Content,
|
||||||
urlString: String,
|
urlString: String,
|
||||||
title: String,
|
title: String,
|
||||||
@ -50,7 +53,8 @@ final class LocalizedPage: ObservableObject {
|
|||||||
originalUrl: String? = nil,
|
originalUrl: String? = nil,
|
||||||
linkPreviewImage: FileResource? = nil,
|
linkPreviewImage: FileResource? = nil,
|
||||||
linkPreviewTitle: String? = nil,
|
linkPreviewTitle: String? = nil,
|
||||||
linkPreviewDescription: String? = nil) {
|
linkPreviewDescription: String? = nil,
|
||||||
|
hideTitle: Bool = false) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.urlString = urlString
|
self.urlString = urlString
|
||||||
self.title = title
|
self.title = title
|
||||||
@ -59,6 +63,7 @@ final class LocalizedPage: ObservableObject {
|
|||||||
self.linkPreviewImage = linkPreviewImage
|
self.linkPreviewImage = linkPreviewImage
|
||||||
self.linkPreviewTitle = linkPreviewTitle
|
self.linkPreviewTitle = linkPreviewTitle
|
||||||
self.linkPreviewDescription = linkPreviewDescription
|
self.linkPreviewDescription = linkPreviewDescription
|
||||||
|
self.hideTitle = hideTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValid(urlComponent: String) -> Bool {
|
func isValid(urlComponent: String) -> Bool {
|
||||||
|
@ -17,6 +17,9 @@ final class PathSettings: ObservableObject {
|
|||||||
@Published
|
@Published
|
||||||
var videosOutputFolderPath: String
|
var videosOutputFolderPath: String
|
||||||
|
|
||||||
|
@Published
|
||||||
|
var audioOutputFolderPath: String
|
||||||
|
|
||||||
@Published
|
@Published
|
||||||
var tagsOutputFolderPath: String
|
var tagsOutputFolderPath: String
|
||||||
|
|
||||||
@ -27,6 +30,7 @@ final class PathSettings: ObservableObject {
|
|||||||
self.filesOutputFolderPath = file.filesOutputFolderPath
|
self.filesOutputFolderPath = file.filesOutputFolderPath
|
||||||
self.videosOutputFolderPath = file.videosOutputFolderPath
|
self.videosOutputFolderPath = file.videosOutputFolderPath
|
||||||
self.tagsOutputFolderPath = file.tagsOutputFolderPath
|
self.tagsOutputFolderPath = file.tagsOutputFolderPath
|
||||||
|
self.audioOutputFolderPath = file.audioOutputFolderPath
|
||||||
}
|
}
|
||||||
|
|
||||||
var file: PathSettingsFile {
|
var file: PathSettingsFile {
|
||||||
@ -35,6 +39,7 @@ final class PathSettings: ObservableObject {
|
|||||||
imagesOutputFolderPath: imagesOutputFolderPath,
|
imagesOutputFolderPath: imagesOutputFolderPath,
|
||||||
filesOutputFolderPath: filesOutputFolderPath,
|
filesOutputFolderPath: filesOutputFolderPath,
|
||||||
videosOutputFolderPath: videosOutputFolderPath,
|
videosOutputFolderPath: videosOutputFolderPath,
|
||||||
tagsOutputFolderPath: tagsOutputFolderPath)
|
tagsOutputFolderPath: tagsOutputFolderPath,
|
||||||
|
audioOutputFolderPath: audioOutputFolderPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,47 @@
|
|||||||
|
|
||||||
struct ButtonDownloadIcon: ContentIcon {
|
extension Icon {
|
||||||
|
struct ArrowDown: ContentIcon {
|
||||||
|
|
||||||
static let name = "icon-download"
|
static let name = "icon-download"
|
||||||
|
|
||||||
static let content = """
|
static let content = """
|
||||||
<svg id="icon-download" viewBox="0 0 40 40"><path fill="currentColor" fill-rule="evenodd" stroke="none" d="M20 40a20 20 0 1 1 20-20 20 20 0 0 1-20 20zm0-36.8A16.8 16.8 0 1 0 36.8 20 16.8 16.8 0 0 0 20 3.2zm.8 27a1 1 0 0 1-1.6 0L12.1 21c-.4-.4 0-1 .7-1H17v-8.7a.8.8 0 0 1 .8-.8h4.4a.8.8 0 0 1 .8.8V20h4.2c.6 0 1.1.5.7 1l-7.1 9.2z"/></svg>
|
<svg id="icon-download" viewBox="0 0 40 40"><path fill="currentColor" fill-rule="evenodd" stroke="none" d="M20 40a20 20 0 1 1 20-20 20 20 0 0 1-20 20zm0-36.8A16.8 16.8 0 1 0 36.8 20 16.8 16.8 0 0 0 20 3.2zm.8 27a1 1 0 0 1-1.6 0L12.1 21c-.4-.4 0-1 .7-1H17v-8.7a.8.8 0 0 1 .8-.8h4.4a.8.8 0 0 1 .8.8V20h4.2c.6 0 1.1.5.7 1l-7.1 9.2z"/></svg>
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ButtonExternalIcon: ContentIcon {
|
extension Icon {
|
||||||
|
|
||||||
|
struct ArrowRight: ContentIcon {
|
||||||
|
|
||||||
static let name = "icon-external"
|
static let name = "icon-external"
|
||||||
|
|
||||||
static let content = """
|
static let content = """
|
||||||
<svg id="icon-external" viewBox="0 0 16 16"><path fill="currentColor" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z"/></svg>
|
<svg id="icon-external" viewBox="0 0 16 16"><path fill="currentColor" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z"/></svg>
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ButtonGitIcon: ContentIcon {
|
extension Icon {
|
||||||
|
|
||||||
|
struct Git: ContentIcon {
|
||||||
|
|
||||||
static let name = "icon-git"
|
static let name = "icon-git"
|
||||||
|
|
||||||
static let content = """
|
static let content = """
|
||||||
<svg id="icon-git" viewBox="0 0 16 16"><path fill="currentColor" d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/></svg>
|
<svg id="icon-git" viewBox="0 0 16 16"><path fill="currentColor" d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/></svg>
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ButtonPlayIcon: ContentIcon {
|
extension Icon {
|
||||||
|
|
||||||
|
struct Play: ContentIcon {
|
||||||
|
|
||||||
static let name = "icon-play-circle"
|
static let name = "icon-play-circle"
|
||||||
|
|
||||||
static let content = """
|
static let content = """
|
||||||
<svg id='icon-play-circle' viewBox="0 0 1000 1000"><g fill="currentColor"><path d="M452.6 11.2A495 495 0 0 0 90.1 229.8 525.5 525.5 0 0 0 19.8 398c-8.3 40.7-9.8 56.6-9.8 101.6s1.5 60.5 9.8 101.5A529.7 529.7 0 0 0 90 769.5 493.9 493.9 0 0 0 499.6 990c185 0 355.6-106 438.6-272.3a486.8 486.8 0 0 0-46.8-512.3A494.2 494.2 0 0 0 568.6 13.9c-24-3.7-91.5-5.4-116-2.7zm85.5 76.4c31 3.1 59.6 9.2 90.6 19.4a413.4 413.4 0 0 1 263.9 264.2 412 412 0 0 1-100.8 420.6A413.7 413.7 0 0 1 460 911.4 415.2 415.2 0 0 1 87.6 538a416.4 416.4 0 0 1 143.7-353.3 417.2 417.2 0 0 1 306.8-97.2z"/><path d="M375.4 291.7c-4.2 2-7.5 5.4-10 11-3.6 8-3.8 14.1-3.8 196.9 0 183 .2 189 3.8 197 5 10.9 14.4 15.3 26 12.2 11.4-3 320-183.1 329.5-192.3 6.9-6.8 7.6-8.3 7.6-17s-.7-10-7.6-16.8c-7.9-7.6-314-187.2-326.5-191.6-8.3-2.9-11.1-2.9-19 .6z"/></g></svg>
|
<svg id='icon-play-circle' viewBox="0 0 1000 1000"><g fill="currentColor"><path d="M452.6 11.2A495 495 0 0 0 90.1 229.8 525.5 525.5 0 0 0 19.8 398c-8.3 40.7-9.8 56.6-9.8 101.6s1.5 60.5 9.8 101.5A529.7 529.7 0 0 0 90 769.5 493.9 493.9 0 0 0 499.6 990c185 0 355.6-106 438.6-272.3a486.8 486.8 0 0 0-46.8-512.3A494.2 494.2 0 0 0 568.6 13.9c-24-3.7-91.5-5.4-116-2.7zm85.5 76.4c31 3.1 59.6 9.2 90.6 19.4a413.4 413.4 0 0 1 263.9 264.2 412 412 0 0 1-100.8 420.6A413.7 413.7 0 0 1 460 911.4 415.2 415.2 0 0 1 87.6 538a416.4 416.4 0 0 1 143.7-353.3 417.2 417.2 0 0 1 306.8-97.2z"/><path d="M375.4 291.7c-4.2 2-7.5 5.4-10 11-3.6 8-3.8 14.1-3.8 196.9 0 183 .2 189 3.8 197 5 10.9 14.4 15.3 26 12.2 11.4-3 320-183.1 329.5-192.3 6.9-6.8 7.6-8.3 7.6-17s-.7-10-7.6-16.8c-7.9-7.6-314-187.2-326.5-191.6-8.3-2.9-11.1-2.9-19 .6z"/></g></svg>
|
||||||
"""
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct Calendar: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-calendar"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-calendar" viewBox="0 0 141 146" fill="currentColor"><path d="m13.3 126.4v-89a9 9 0 0 1 2.6-6.3 8.2 8.2 0 0 1 6.2-2.6h8.8v-6.7a11 11 0 0 1 3.2-7.9c2.2-2.2 4.7-3.3 7.8-3.3h4.4c3 0 5.6 1.1 7.8 3.3s3.2 4.8 3.2 7.9v6.7h26.4v-6.7a11 11 0 0 1 3.2-7.9c2.2-2.2 4.7-3.3 7.8-3.3h4.4c3 0 5.6 1.1 7.8 3.3s3.2 4.8 3.2 7.9v6.7h8.8c2.4 0 4.4.9 6.2 2.6a8.8 8.8 0 0 1 2.6 6.3v88.9a9 9 0 0 1 -2.6 6.3 8.2 8.2 0 0 1 -6.2 2.6h-96.8c-2.4 0-4.4-.9-6.2-2.6a8.7 8.7 0 0 1 -2.6-6.2zm8.8 0h96.8v-71.2h-96.8zm17.6-84.5c0 .6.2 1.2.6 1.6s.9.6 1.6.6h4.4c.6 0 1.2-.2 1.6-.6s.6-.9.6-1.6v-20c0-.6-.2-1.2-.6-1.6s-.9-.6-1.6-.6h-4.4c-.6 0-1.2.2-1.6.6s-.6 1-.6 1.6zm52.8 0c0 .6.2 1.2.6 1.6s.9.6 1.6.6h4.4c.6 0 1.2-.2 1.6-.6s.6-.9.6-1.6v-20c0-.6-.2-1.2-.6-1.6s-.9-.6-1.6-.6h-4.4c-.6 0-1.2.2-1.6.6s-.6 1-.6 1.6z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct ClockFill: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-clock-fill"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-clock-fill" viewBox="0 0 16 16" fill="currentColor"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .3.4l3.5 2a.5.5 0 0 0 .4-.8L8 8.7V3.5z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct File: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-file"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-file" viewBox="0 0 16 16" fill="currentColor"><path d="M9.3 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.7a1 1 0 0 0-.3-.7L10 .3a1 1 0 0 0-.7-.3zm.2 3.5v-2l3 3h-2a1 1 0 0 1-1-1zM4.5 9a.5.5 0 0 1 0-1h7a.5.5 0 0 1 0 1h-7zM4 10.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1h-4z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct Globe: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-globe"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-globe" viewBox="0 0 16 16" fill="currentColor"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-7c-.7.3-1.3.9-1.9 2l-.4.8c.7.2 1.5.3 2.3.3v-3zM4.2 3.6l.5-1a6.7 6.7 0 0 1 .6-1A7 7 0 0 0 3.1 3l1.2.5zm-.7 4c0-1 .2-2 .4-3a9.1 9.1 0 0 1-1.5-.7A7 7 0 0 0 1 7.5h2.5zm1.4-2.7a12.3 12.3 0 0 0-.4 2.7h3V5.1a13.5 13.5 0 0 1-2.6-.3zm3.6.3v2.4h3a12.3 12.3 0 0 0-.4-2.7c-.8.1-1.7.3-2.6.3zm-4 3.4c0 1 .2 2 .4 2.7a13.6 13.6 0 0 1 2.6-.3V8.5h-3zm4 0V11c1 0 1.8.1 2.6.3.2-.8.4-1.7.4-2.7h-3zm-3.3 3.7.4.9c.6 1 1.2 1.6 1.9 1.8v-3c-.8 0-1.6.1-2.3.3zm.1 2.3a6.7 6.7 0 0 1-.6-1 8.8 8.8 0 0 1-.4-1 8.4 8.4 0 0 0-1.2.4 7 7 0 0 0 2.2 1.6zm-1.4-3a13.4 13.4 0 0 1-.4-3H1a7 7 0 0 0 1.4 3.7c.4-.3 1-.5 1.5-.7zm6.8 3A7 7 0 0 0 13 13a8.4 8.4 0 0 0-1.2-.4 8.8 8.8 0 0 1-.5 1 6.7 6.7 0 0 1-.6 1zM8.5 12v3c.7-.2 1.3-.8 1.9-1.8l.4-.9a12.6 12.6 0 0 0-2.3-.3zm3.6-.4 1.5.7A7 7 0 0 0 15 8.5h-2.5a13.4 13.4 0 0 1-.4 3zm2.9-4a7 7 0 0 0-1.4-3.7c-.4.3-1 .5-1.5.7.2 1 .4 2 .4 3H15zm-3.7-5 .4 1A8.4 8.4 0 0 0 13 3a7 7 0 0 0-2.3-1.5l.6 1zm-.5 1.3a7.8 7.8 0 0 0-.4-.9c-.6-1-1.2-1.6-1.9-1.8v3a12.7 12.7 0 0 0 2.3-.3z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct Location: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-location"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-location" viewBox="0 0 16 16" fill="currentColor"><path d="M8 16s6-6 6-10A6 6 0 0 0 2 6c0 4 6 10 6 10zm0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct Poster: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-poster"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-poster" viewBox="0 0 16 16" fill="currentColor"><path d="M9.3 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4.7a1 1 0 0 0-.3-.7L10 .3a1 1 0 0 0-.7-.3zm.2 3.5v-2l3 3h-2a1 1 0 0 1-1-1zm.5 10v-6a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5zm-2.5.5a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5h-1zm-3 0a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-1z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Icon {
|
||||||
|
|
||||||
|
struct Video: ContentIcon {
|
||||||
|
|
||||||
|
static let name = "icon-video"
|
||||||
|
|
||||||
|
static let content = """
|
||||||
|
<svg id="icon-video" fill="currentColor" viewBox="0 0 122.9 111.3"><path d="M24 0h75a24 24 0 0 1 24 24v64a24 24 0 0 1-7 16v1a24 24 0 0 1-17 6H24a24 24 0 0 1-17-7 23 23 0 0 1-7-16V24A24 24 0 0 1 24 0Zm30 48 25 17a4 4 0 0 1 0 7L54 89a4 4 0 0 1-2 1 4 4 0 0 1-4-4V51a4 4 0 0 1 6-3ZM7 26h14l9-19h-6A16 16 0 0 0 7 24v2ZM37 7l-9 19h25l9-19Zm32 0-9 19h25l9-19Zm31 0-9 19h25v-2a16 16 0 0 0-16-17Zm16 27H7v54a16 16 0 0 0 5 11 16 16 0 0 0 12 5h75a16 16 0 0 0 12-5 16 16 0 0 0 5-11V34Z"/></svg>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
enum Icon {
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,22 @@
|
|||||||
|
|
||||||
enum PageIcon: String, CaseIterable {
|
enum PageIcon: String, CaseIterable {
|
||||||
|
|
||||||
|
// MARK: General
|
||||||
|
|
||||||
|
case calendar
|
||||||
|
|
||||||
|
case clockFill = "clock-fill"
|
||||||
|
|
||||||
|
case file
|
||||||
|
|
||||||
|
case globe
|
||||||
|
|
||||||
|
case location
|
||||||
|
|
||||||
|
case poster
|
||||||
|
|
||||||
|
case video
|
||||||
|
|
||||||
// MARK: Statistics
|
// MARK: Statistics
|
||||||
|
|
||||||
case statisticsTime = "time"
|
case statisticsTime = "time"
|
||||||
@ -44,16 +60,23 @@ enum PageIcon: String, CaseIterable {
|
|||||||
case .statisticsElevationDown: return StatisticsElevationDownIcon.self
|
case .statisticsElevationDown: return StatisticsElevationDownIcon.self
|
||||||
case .statisticsDistance: return StatisticsDistanceIcon.self
|
case .statisticsDistance: return StatisticsDistanceIcon.self
|
||||||
case .statisticsEnergy: return StatisticsEnergyIcon.self
|
case .statisticsEnergy: return StatisticsEnergyIcon.self
|
||||||
case .buttonDownload: return ButtonDownloadIcon.self
|
case .buttonDownload: return Icon.ArrowDown.self
|
||||||
case .buttonExternalLink: return ButtonExternalIcon.self
|
case .buttonExternalLink: return Icon.ArrowRight.self
|
||||||
case .buttonGitLink: return ButtonGitIcon.self
|
case .buttonGitLink: return Icon.Git.self
|
||||||
case .buttonPlay: return ButtonPlayIcon.self
|
case .buttonPlay: return Icon.Play.self
|
||||||
case .audioPlayerPlaylist: return AudioPlayerPlaylistIcon.self
|
case .audioPlayerPlaylist: return AudioPlayerPlaylistIcon.self
|
||||||
case .audioPlayerClose: return AudioPlayerCloseIcon.self
|
case .audioPlayerClose: return AudioPlayerCloseIcon.self
|
||||||
case .audioPlayerPlay: return AudioPlayerPlayIcon.self
|
case .audioPlayerPlay: return AudioPlayerPlayIcon.self
|
||||||
case .audioPlayerPause: return AudioPlayerPauseIcon.self
|
case .audioPlayerPause: return AudioPlayerPauseIcon.self
|
||||||
case .audioPlayerPrevious: return AudioPlayerPreviousIcon.self
|
case .audioPlayerPrevious: return AudioPlayerPreviousIcon.self
|
||||||
case .audioPlayerNext: return AudioPlayerNextIcon.self
|
case .audioPlayerNext: return AudioPlayerNextIcon.self
|
||||||
|
case .calendar: return Icon.Calendar.self
|
||||||
|
case .clockFill: return Icon.ClockFill.self
|
||||||
|
case .file: return Icon.File.self
|
||||||
|
case .globe: return Icon.Globe.self
|
||||||
|
case .location: return Icon.Location.self
|
||||||
|
case .poster: return Icon.Poster.self
|
||||||
|
case .video: return Icon.Video.self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
struct StatisticsTimeIcon: ContentIcon {
|
struct StatisticsTimeIcon: ContentIcon {
|
||||||
|
|
||||||
static let name = "icon-time"
|
static let name = "icon-clock"
|
||||||
|
|
||||||
static let content = """
|
static let content = """
|
||||||
<svg id="icon-time" width="16" height="16" viewBox="0 0 16 16"><path fill="currentColor" d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .3.4l3.5 2a.5.5 0 0 0 .4-.8L8 8.7V3.5z"/><path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/></svg>
|
<svg id="icon-clock" width="16" height="16" viewBox="0 0 16 16"><path fill="currentColor" d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .3.4l3.5 2a.5.5 0 0 0 .4-.8L8 8.7V3.5z"/><path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/></svg>
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ struct ContentPage: HtmlProducer {
|
|||||||
|
|
||||||
private let title: String
|
private let title: String
|
||||||
|
|
||||||
|
private let showTitle: Bool
|
||||||
|
|
||||||
private let tags: [FeedEntryData.Tag]
|
private let tags: [FeedEntryData.Tag]
|
||||||
|
|
||||||
private let navigationBarLinks: [NavigationBar.Link]
|
private let navigationBarLinks: [NavigationBar.Link]
|
||||||
@ -24,10 +26,21 @@ struct ContentPage: HtmlProducer {
|
|||||||
|
|
||||||
private let icons: Set<PageIcon>
|
private let icons: Set<PageIcon>
|
||||||
|
|
||||||
init(language: ContentLanguage, dateString: String, title: String, tags: [FeedEntryData.Tag], linkTitle: String, description: String, navigationBarLinks: [NavigationBar.Link], pageContent: String, headers: Set<HeaderElement>, footers: [String], icons: Set<PageIcon>) {
|
init(language: ContentLanguage,
|
||||||
|
dateString: String,
|
||||||
|
title: String,
|
||||||
|
showTitle: Bool,
|
||||||
|
tags: [FeedEntryData.Tag],
|
||||||
|
linkTitle: String,
|
||||||
|
description: String,
|
||||||
|
navigationBarLinks: [NavigationBar.Link],
|
||||||
|
pageContent: String,
|
||||||
|
headers: Set<HeaderElement>,
|
||||||
|
footers: [String], icons: Set<PageIcon>) {
|
||||||
self.language = language
|
self.language = language
|
||||||
self.dateString = dateString
|
self.dateString = dateString
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.showTitle = showTitle
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.linkTitle = linkTitle
|
self.linkTitle = linkTitle
|
||||||
self.description = description
|
self.description = description
|
||||||
@ -47,9 +60,11 @@ struct ContentPage: HtmlProducer {
|
|||||||
|
|
||||||
result += "<main><article>"
|
result += "<main><article>"
|
||||||
result += "<div style=\"height: 70px;\"></div>"
|
result += "<div style=\"height: 70px;\"></div>"
|
||||||
|
if showTitle {
|
||||||
result += "<h3>\(dateString)</h3>"
|
result += "<h3>\(dateString)</h3>"
|
||||||
result += "<h1>\(title)</h1>"
|
result += "<h1>\(title)</h1>"
|
||||||
result += TagList(tags: tags).content
|
result += TagList(tags: tags).content
|
||||||
|
}
|
||||||
result += symbols
|
result += symbols
|
||||||
result += pageContent
|
result += pageContent
|
||||||
result += "</article></main>"
|
result += "</article></main>"
|
||||||
@ -59,6 +74,9 @@ struct ContentPage: HtmlProducer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var symbols: String {
|
private var symbols: String {
|
||||||
|
guard !icons.isEmpty else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
var result = "<div style='display:none'>"
|
var result = "<div style='display:none'>"
|
||||||
for icon in icons {
|
for icon in icons {
|
||||||
result += icon.icon.content
|
result += icon.icon.content
|
||||||
|
@ -47,6 +47,8 @@ struct LocalizedPageFile {
|
|||||||
let lastModifiedDate: Date?
|
let lastModifiedDate: Date?
|
||||||
|
|
||||||
let originalURL: String?
|
let originalURL: String?
|
||||||
|
|
||||||
|
let hideTitle: Bool?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalizedPageFile: Codable {
|
extension LocalizedPageFile: Codable {
|
||||||
|
@ -13,18 +13,22 @@ struct PathSettingsFile {
|
|||||||
|
|
||||||
let tagsOutputFolderPath: String
|
let tagsOutputFolderPath: String
|
||||||
|
|
||||||
|
let audioOutputFolderPath: String
|
||||||
|
|
||||||
init(assetsOutputFolderPath: String,
|
init(assetsOutputFolderPath: String,
|
||||||
pagesOutputFolderPath: String,
|
pagesOutputFolderPath: String,
|
||||||
imagesOutputFolderPath: String,
|
imagesOutputFolderPath: String,
|
||||||
filesOutputFolderPath: String,
|
filesOutputFolderPath: String,
|
||||||
videosOutputFolderPath: String,
|
videosOutputFolderPath: String,
|
||||||
tagsOutputFolderPath: String) {
|
tagsOutputFolderPath: String,
|
||||||
|
audioOutputFolderPath: String) {
|
||||||
self.assetsOutputFolderPath = assetsOutputFolderPath
|
self.assetsOutputFolderPath = assetsOutputFolderPath
|
||||||
self.pagesOutputFolderPath = pagesOutputFolderPath
|
self.pagesOutputFolderPath = pagesOutputFolderPath
|
||||||
self.imagesOutputFolderPath = imagesOutputFolderPath
|
self.imagesOutputFolderPath = imagesOutputFolderPath
|
||||||
self.filesOutputFolderPath = filesOutputFolderPath
|
self.filesOutputFolderPath = filesOutputFolderPath
|
||||||
self.videosOutputFolderPath = videosOutputFolderPath
|
self.videosOutputFolderPath = videosOutputFolderPath
|
||||||
self.tagsOutputFolderPath = tagsOutputFolderPath
|
self.tagsOutputFolderPath = tagsOutputFolderPath
|
||||||
|
self.audioOutputFolderPath = audioOutputFolderPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +45,8 @@ extension PathSettingsFile {
|
|||||||
imagesOutputFolderPath: "image",
|
imagesOutputFolderPath: "image",
|
||||||
filesOutputFolderPath: "file",
|
filesOutputFolderPath: "file",
|
||||||
videosOutputFolderPath: "video",
|
videosOutputFolderPath: "video",
|
||||||
tagsOutputFolderPath: "tag")
|
tagsOutputFolderPath: "tag",
|
||||||
|
audioOutputFolderPath: "audio")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,12 @@ struct LocalizedPageDetailView: View {
|
|||||||
update: { page.urlString = $0 })
|
update: { page.urlString = $0 })
|
||||||
.disabled(isExternalPage)
|
.disabled(isExternalPage)
|
||||||
|
|
||||||
|
BoolPropertyView(
|
||||||
|
title: "Hide automatic title",
|
||||||
|
value: $page.hideTitle,
|
||||||
|
footer: "Prevent the date and title from being printed on the page")
|
||||||
|
.disabled(isExternalPage)
|
||||||
|
|
||||||
OptionalStringPropertyView(
|
OptionalStringPropertyView(
|
||||||
title: "Preview Title",
|
title: "Preview Title",
|
||||||
text: $page.linkPreviewTitle,
|
text: $page.linkPreviewTitle,
|
||||||
|
@ -15,12 +15,6 @@ struct GenerationContentView: View {
|
|||||||
self._selectedSection = selected
|
self._selectedSection = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
@State
|
|
||||||
private var isGeneratingWebsite = false
|
|
||||||
|
|
||||||
@State
|
|
||||||
private var generatorText: String = ""
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
switch selectedSection {
|
switch selectedSection {
|
||||||
case .folders, .navigationBar, .postFeed, .tagOverview:
|
case .folders, .navigationBar, .postFeed, .tagOverview:
|
||||||
@ -41,21 +35,24 @@ struct GenerationContentView: View {
|
|||||||
.padding(.bottom, 30)
|
.padding(.bottom, 30)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Button(action: generateFullWebsite) {
|
Button {
|
||||||
Text("Generate")
|
if content.isGeneratingWebsite {
|
||||||
|
content.endCurrentGeneration()
|
||||||
|
} else {
|
||||||
|
generateFullWebsite()
|
||||||
}
|
}
|
||||||
Text(generatorText)
|
} label: {
|
||||||
Spacer()
|
Text(content.isGeneratingWebsite ? "Cancel" : "Generate")
|
||||||
if isGeneratingWebsite {
|
}
|
||||||
|
if content.isGeneratingWebsite {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.progressViewStyle(.circular)
|
.progressViewStyle(.circular)
|
||||||
.frame(height: 25)
|
.frame(height: 25)
|
||||||
}
|
}
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
.disabled(isGeneratingWebsite)
|
|
||||||
Text(content.generationStatus)
|
Text(content.generationStatus)
|
||||||
.font(.subheadline)
|
.padding(.vertical, 5)
|
||||||
.padding()
|
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Text("\(content.results.imagesToGenerate.count) images")
|
Text("\(content.results.imagesToGenerate.count) images")
|
||||||
Text("\(content.results.externalLinks.count) external links")
|
Text("\(content.results.externalLinks.count) external links")
|
||||||
|
@ -54,6 +54,11 @@ struct PathSettingsView: View {
|
|||||||
text: $content.settings.paths.videosOutputFolderPath,
|
text: $content.settings.paths.videosOutputFolderPath,
|
||||||
footer: "The path in the output folder where the generated videos are stored")
|
footer: "The path in the output folder where the generated videos are stored")
|
||||||
|
|
||||||
|
StringPropertyView(
|
||||||
|
title: "Audio output folder",
|
||||||
|
text: $content.settings.paths.audioOutputFolderPath,
|
||||||
|
footer: "The path in the output folder where the audio files are stored")
|
||||||
|
|
||||||
StringPropertyView(
|
StringPropertyView(
|
||||||
title: "Assets output folder",
|
title: "Assets output folder",
|
||||||
text: $content.settings.paths.assetsOutputFolderPath,
|
text: $content.settings.paths.assetsOutputFolderPath,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user