Add navigation settings, fix page generation

This commit is contained in:
Christoph Hagen
2025-01-02 11:56:51 +01:00
parent 922ba4ebe7
commit 4d4275e072
43 changed files with 921 additions and 581 deletions

View File

@ -0,0 +1,24 @@
struct BoxCommandProcessor: CommandProcessor {
let commandType: ShorthandMarkdownKey = .box
let results: PageGenerationResults
init(content: Content, results: PageGenerationResults) {
self.results = results
}
/**
Format: `![box](<title>;<body>)`
*/
func process(_ arguments: [String], markdown: Substring) -> String {
guard arguments.count > 1 else {
results.invalid(command: .box, markdown)
return ""
}
let title = arguments[0]
let text = arguments.dropFirst().joined(separator: ";")
return ContentBox(title: title, text: text).content
}
}

View File

@ -3,7 +3,6 @@ struct IconCommandProcessor: CommandProcessor {
let commandType: ShorthandMarkdownKey = .icons
let results: PageGenerationResults
init(content: Content, results: PageGenerationResults) {

View File

@ -13,7 +13,7 @@ struct InlineLinkProcessor {
let language: ContentLanguage
func handleLink(html: String, markdown: Substring) -> String {
func process(html: String, markdown: Substring) -> String {
let url = markdown.between("(", and: ")")
if url.hasPrefix(pageLinkMarker) {
return handleInlinePageLink(url: url, html: html, markdown: markdown)
@ -56,7 +56,7 @@ struct InlineLinkProcessor {
return markdown.between("[", and: "]")
}
results.linked(to: tag)
let tagPath = content.absoluteUrlToTag(tag, language: language)
let tagPath = tag.absoluteUrl(in: language)
return html.replacingOccurrences(of: textToChange, with: tagPath)
}

View File

@ -0,0 +1,25 @@
import Splash
struct PageCodeProcessor {
private let codeHighlightFooter = "<script>hljs.highlightAll();</script>"
private let swift = SyntaxHighlighter(format: HTMLOutputFormat())
let results: PageGenerationResults
init(results: PageGenerationResults) {
self.results = results
}
func process(_ html: String, markdown: Substring) -> String {
guard markdown.starts(with: "```swift") else {
results.require(header: .codeHightlighting)
results.require(footer: codeHighlightFooter)
return html // Just use normal code highlighting
}
// Highlight swift code using Splash
let code = markdown.between("```swift", and: "```").trimmed
return "<pre><code>" + swift.highlight(code) + "</pre></code>"
}
}

View File

@ -0,0 +1,153 @@
import SwiftSoup
/**
Handles both inline HTML and the external HTML command
*/
struct PageHtmlProcessor: CommandProcessor {
let commandType: ShorthandMarkdownKey = .includedHtml
let results: PageGenerationResults
let content: Content
init(content: Content, results: PageGenerationResults) {
self.content = content
self.results = results
}
/**
Handle the HTML command
Format: `![html](<fileId>)`
*/
func process(_ arguments: [String], markdown: Substring) -> String {
guard arguments.count == 1 else {
results.invalid(command: .includedHtml, markdown)
return ""
}
let fileId = arguments[0]
guard let file = content.file(fileId) else {
results.missing(file: fileId, source: "External HTML command")
return ""
}
let content = file.textContent()
checkResources(in: content)
return content
}
/**
Handle inline HTML
*/
func process(_ html: String, markdown: Substring) -> String {
checkResources(in: html)
return html
}
private func checkResources(in html: String) {
let document: Document
do {
document = try SwiftSoup.parse(html)
} catch {
results.warning("Failed to parse inline HTML: \(error)")
return
}
checkImages(in: document)
checkLinks(in: document)
checkSourceSets(in: document)
}
private func checkImages(in document: Document) {
let srcAttributes: [String]
do {
let imgElements = try document.select("img")
srcAttributes = try imgElements.array()
.compactMap { try $0.attr("src") }
.filter { !$0.trimmed.isEmpty }
} catch {
results.warning("Failed to check 'src' attributes of <img> elements in inline HTML: \(error)")
return
}
for src in srcAttributes {
results.warning("Found image in html: \(src)")
}
}
private func checkLinks(in document: Document) {
let hrefs: [String]
do {
let linkElements = try document.select("a")
hrefs = try linkElements.array()
.compactMap { try $0.attr("href").trimmed }
.filter { !$0.isEmpty }
} catch {
results.warning("Failed to check 'href' attributes of <a> elements in inline HTML: \(error)")
return
}
for url in hrefs {
if url.hasPrefix("http://") || url.hasPrefix("https://") {
results.externalLink(to: url)
} else {
results.warning("Relative link in HTML: \(url)")
}
}
}
private func checkSourceSets(in document: Document) {
let sources: [Element]
do {
sources = try document.select("source").array()
} catch {
results.warning("Failed to find <source> elements in inline HTML: \(error)")
return
}
}
private func checkSourceSetAttributes(sources: [Element]) {
let srcSets: [String]
do {
srcSets = try sources
.compactMap { try $0.attr("srcset") }
.filter { !$0.trimmed.isEmpty }
} catch {
results.warning("Failed to check 'srcset' attributes of <source> elements in inline HTML: \(error)")
return
}
for src in srcSets {
results.warning("Found source set in html: \(src)")
}
}
private func checkSourceAttributes(sources: [Element]) {
let srcAttributes: [String]
do {
srcAttributes = try sources
.compactMap { try $0.attr("src") }
.filter { !$0.trimmed.isEmpty }
} catch {
results.warning("Failed to check 'src' attributes of <source> elements in inline HTML: \(error)")
return
}
for src in srcAttributes {
guard content.isValidIdForFile(src) else {
results.warning("Found source in html: \(src)")
continue
}
guard let file = content.file(src) else {
results.warning("Found source in html: \(src)")
continue
}
#warning("Either find files by their full path, or replace file id with full path")
results.require(file: file)
}
}
}