Add links to headlines

This commit is contained in:
Christoph Hagen 2025-02-23 10:42:43 +01:00
parent d4c0da0a32
commit cdcbe21c0d
4 changed files with 58 additions and 13 deletions

View File

@ -189,6 +189,7 @@
E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F422C4294F60047CD0C /* FeedEntry.swift */; }; E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F422C4294F60047CD0C /* FeedEntry.swift */; };
E2B85F452C429ED60047CD0C /* ImageGallery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F442C429ED60047CD0C /* ImageGallery.swift */; }; E2B85F452C429ED60047CD0C /* ImageGallery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F442C429ED60047CD0C /* ImageGallery.swift */; };
E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */; }; E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */; };
E2BF1BC62D6B16FF003089F1 /* HeadlineLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */; };
E2DD04742C276F31003BFF1F /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DD04732C276F31003BFF1F /* MainView.swift */; }; E2DD04742C276F31003BFF1F /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DD04732C276F31003BFF1F /* MainView.swift */; };
E2DD047A2C276F32003BFF1F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD04792C276F32003BFF1F /* Assets.xcassets */; }; E2DD047A2C276F32003BFF1F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD04792C276F32003BFF1F /* Assets.xcassets */; };
E2DD047E2C276F32003BFF1F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */; }; E2DD047E2C276F32003BFF1F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */; };
@ -457,6 +458,7 @@
E2B85F422C4294F60047CD0C /* FeedEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedEntry.swift; sourceTree = "<group>"; }; E2B85F422C4294F60047CD0C /* FeedEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedEntry.swift; sourceTree = "<group>"; };
E2B85F442C429ED60047CD0C /* ImageGallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGallery.swift; sourceTree = "<group>"; }; E2B85F442C429ED60047CD0C /* ImageGallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGallery.swift; sourceTree = "<group>"; };
E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extension.swift"; sourceTree = "<group>"; }; E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extension.swift"; sourceTree = "<group>"; };
E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineLink.swift; sourceTree = "<group>"; };
E2DD04702C276F31003BFF1F /* CHDataManagement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHDataManagement.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2DD04702C276F31003BFF1F /* CHDataManagement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHDataManagement.app; sourceTree = BUILT_PRODUCTS_DIR; };
E2DD04732C276F31003BFF1F /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; E2DD04732C276F31003BFF1F /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
E2DD04792C276F32003BFF1F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; E2DD04792C276F32003BFF1F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@ -694,6 +696,7 @@
E29D311E2D0320D90051B7F4 /* ContentElements */ = { E29D311E2D0320D90051B7F4 /* ContentElements */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */,
E2B4821F2D67074C005C309D /* WallpaperSlider.swift */, E2B4821F2D67074C005C309D /* WallpaperSlider.swift */,
E29D31C12D0DBED70051B7F4 /* AudioPlayer */, E29D31C12D0DBED70051B7F4 /* AudioPlayer */,
E29D318A2D0B07E60051B7F4 /* ContentBox.swift */, E29D318A2D0B07E60051B7F4 /* ContentBox.swift */,
@ -1285,6 +1288,7 @@
E229904C2D10BE5D009F8D77 /* InitialSetupView.swift in Sources */, E229904C2D10BE5D009F8D77 /* InitialSetupView.swift in Sources */,
E218502B2CF790B30090B18B /* PostContentView.swift in Sources */, E218502B2CF790B30090B18B /* PostContentView.swift in Sources */,
E29D317D2D086AB00051B7F4 /* Int+Random.swift in Sources */, E29D317D2D086AB00051B7F4 /* Int+Random.swift in Sources */,
E2BF1BC62D6B16FF003089F1 /* HeadlineLink.swift in Sources */,
E25DA56F2D00F9A100AEF16D /* PostFeedSettingsView.swift in Sources */, E25DA56F2D00F9A100AEF16D /* PostFeedSettingsView.swift in Sources */,
E2521E042D51796000C56662 /* StorageErrorView.swift in Sources */, E2521E042D51796000C56662 /* StorageErrorView.swift in Sources */,
E29D313B2D04464A0051B7F4 /* LocalizedTagDetailView.swift in Sources */, E29D313B2D04464A0051B7F4 /* LocalizedTagDetailView.swift in Sources */,

View File

@ -23,7 +23,6 @@ struct PhoneScreensBlock: OrderedKeyBlockProcessor {
} }
func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String { func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String {
print("Processing Phone Screens Block")
guard let frameId = arguments.first(where: {$0.key == .frame })?.value else { guard let frameId = arguments.first(where: {$0.key == .frame })?.value else {
invalid(markdown) invalid(markdown)
return "" return ""

View File

@ -19,20 +19,20 @@ struct MarkdownHeadlineProcessor: MarkdownProcessor {
/** /**
Modify headlines by extracting an id from the headline and adding it into the html element 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 The id is created by lowercasing the string, removing all special characters, and replacing spaces with scores
*/ */
func process(html: String, markdown: Substring) -> String { func process(html: String, markdown: Substring) -> String {
let id = markdown let parts2 = markdown.components(separatedBy: "#")
.last(after: "#") // Determine the type of heading, e.g. 2 for <h2>
.trimmed let headlineParts = parts2.drop { $0.isEmpty }
.filter { $0.isNumber || $0.isLetter || $0 == " " } let headlineType = parts2.count - headlineParts.count
.lowercased()
.components(separatedBy: " ") guard headlineType > 1 && headlineType < 4 else {
.filter { $0 != "" } return html
.joined(separator: "-") }
let parts = html.components(separatedBy: ">")
return parts[0] + " id=\"\(id)\">" + parts.dropFirst().joined(separator: ">") let title = headlineParts.joined(separator: "#")
let headline = HeadlineLink(level: headlineType, title: title)
return headline.content
} }
} }

View File

@ -0,0 +1,42 @@
struct HeadlineLink: HtmlProducer {
/// The type of headline, e.g. `2` for `<h2>`
let level: Int
let title: String
var id: String {
title.validHtmlId()
}
func populate(_ result: inout String) {
result += "<h\(level) id='\(id)'>"
result += "<a href='#\(id)'>\(title)</a>"
result += "</h\(level)>"
}
}
private extension String {
static let characterReplacements: [Character: String] = [
"ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss",
"Ä": "Ae", "Ö": "Oe", "Ü": "Ue",
" ": "-", "/": "-", "&": "and"
]
func validHtmlId() -> String {
var result = self.lowercased()
for (char, replacement) in String.characterReplacements {
result = result.replacingOccurrences(of: String(char), with: replacement)
}
result = result.filter { $0.isLetter || $0.isNumber || $0 == "-" }
return result
.components(separatedBy: "-")
.filter { $0 != "" }
.joined(separator: "-")
}
}