Improve settings, sidebars

This commit is contained in:
Christoph Hagen
2024-12-04 22:54:05 +01:00
parent b3cc4a57db
commit c3309197c0
36 changed files with 968 additions and 426 deletions

View File

@ -49,9 +49,9 @@ final class ImageGenerator {
}
}
func runJobs() -> Bool {
func runJobs(callback: (String) -> Void) -> Bool {
for job in jobs {
print("Generating image \(job.version)")
callback("Generating image \(job.version)")
guard generate(job: job) else {
return false
}
@ -72,6 +72,10 @@ final class ImageGenerator {
func generateVersion(for image: String, type: ImageType, maximumWidth: CGFloat, maximumHeight: CGFloat) -> String {
let version = versionFileName(image: image, type: type, width: maximumWidth, height: maximumHeight)
let fullPath = "/" + relativeImageOutputPath + "/" + version
if exists(version) {
hasNowGenerated(version: version, for: image)
return fullPath
}
if hasPreviouslyGenerated(version: version, for: image), exists(version) {
// Don't add job again
return fullPath
@ -121,6 +125,7 @@ final class ImageGenerator {
print("Missing image \(inputPath.path())")
return false
}
let data: Data
do {
data = try Data(contentsOf: inputPath)
@ -163,8 +168,16 @@ final class ImageGenerator {
return false
}
let result = inOutputImagesFolder { folder in
let url = folder.appendingPathComponent(job.version)
if job.type == .avif {
let out = url.path()
let input = out.replacingOccurrences(of: ".avif", with: ".jpg")
print("avifenc -q 70 \(input) \(out)")
return true
}
do {
try data.write(to: url)
return true
@ -198,19 +211,20 @@ final class ImageGenerator {
private func create(image: NSBitmapImageRep, type: ImageType, quality: CGFloat) -> Data? {
switch type {
case .jpg:
return image.representation(using: .jpeg, properties: [.compressionFactor: NSNumber(value: quality)])
return image.representation(using: .jpeg, properties: [.compressionFactor: NSNumber(value: 0.6)])
case .png:
return image.representation(using: .png, properties: [.compressionFactor: NSNumber(value: quality)])
return image.representation(using: .png, properties: [.compressionFactor: NSNumber(value: 0.6)])
case .avif:
return createAvif(image: image, quality: quality)
return createAvif(image: image, quality: 0.7)
case .webp:
return createWebp(image: image, quality: quality)
return createWebp(image: image, quality: 0.8)
case .gif:
return image.representation(using: .gif, properties: [.compressionFactor: NSNumber(value: quality)])
}
}
private func createAvif(image: NSBitmapImageRep, quality: CGFloat) -> Data? {
return Data()
let newImage = NSImage(size: image.size)
newImage.addRepresentation(image)
return SDImageAVIFCoder.shared.encodedData(with: newImage, format: .AVIF, options: [.encodeCompressionQuality: quality])

View File

@ -0,0 +1,14 @@
struct LocalizedPostSettingsFile {
/// The page title for the post feed
let feedTitle: String
/// The page description for the post feed
let feedDescription: String
/// The path to the feed in the final website, appended with the page number
let feedUrlPrefix: String
}
extension LocalizedPostSettingsFile: Codable { }

View File

@ -0,0 +1,12 @@
struct LocalizedSettingsFile {
let navigationBarIconDescription: String
let posts: LocalizedPostSettingsFile
}
extension LocalizedSettingsFile: Codable {
}

View File

@ -0,0 +1,12 @@
struct NavigationBarSettingsFile {
/// The path to the main icon in the navigation bar
let navigationIconPath: String
/// The tags to show in the navigation bar
let navigationTags: [String]
}
extension NavigationBarSettingsFile: Codable { }

View File

@ -0,0 +1,12 @@
import Foundation
struct PostSettingsFile {
/// The number of posts to show in a single page of the news feed
let postsPerPage: Int
/// The maximum width of the main content
let contentWidth: CGFloat
}
extension PostSettingsFile: Codable { }

View File

@ -0,0 +1,17 @@
import Foundation
struct SettingsFile {
/// The file path to the output directory
let outputDirectoryPath: String
let navigationBar: NavigationBarSettingsFile
let posts: PostSettingsFile
let german: LocalizedSettingsFile
let english: LocalizedSettingsFile
}
extension SettingsFile: Codable { }

View File

@ -1,28 +0,0 @@
import Foundation
struct WebsiteDataFile {
let navigationTags: [String]
let german: LocalizedWebsiteDataFile
let english: LocalizedWebsiteDataFile
}
extension WebsiteDataFile: Codable {
}
struct LocalizedWebsiteDataFile {
let title: String
let description: String
let iconDescription: String
}
extension LocalizedWebsiteDataFile: Codable {
}

View File

@ -236,17 +236,17 @@ final class Storage {
// MARK: Website data
private var websiteDataUrl: URL {
baseFolder.appending(path: "website-data.json", directoryHint: .notDirectory)
private var settingsDataUrl: URL {
baseFolder.appending(path: "settings.json", directoryHint: .notDirectory)
}
func loadWebsiteData() throws -> WebsiteDataFile {
try read(at: websiteDataUrl)
func loadSettings() throws -> SettingsFile {
try read(at: settingsDataUrl)
}
@discardableResult
func save(websiteData: WebsiteDataFile) -> Bool {
write(websiteData, type: "Website Data", id: "-", to: websiteDataUrl)
func save(settings: SettingsFile) -> Bool {
write(settings, type: "Settings", id: "-", to: settingsDataUrl)
}
// MARK: Image generation data

View File

@ -1,71 +1,61 @@
import Foundation
struct WebsiteGeneratorConfiguration {
let language: ContentLanguage
let outputDirectory: URL
let postsPerPage: Int
let postFeedTitle: String
let postFeedDescription: String
let postFeedUrlPrefix: String
let navigationIconPath: String
let mainContentMaximumWidth: CGFloat
}
final class WebsiteGenerator {
let language: ContentLanguage
let outputDirectory: URL
let localizedSettings: LocalizedSettings
let postsPerPage: Int
private var outputDirectory: URL {
URL(filePath: content.settings.outputDirectoryPath)
}
let postFeedTitle: String
private var postsPerPage: Int {
content.settings.posts.postsPerPage
}
let postFeedDescription: String
private var postFeedTitle: String {
localizedSettings.posts.title
}
let postFeedUrlPrefix: String
private var postFeedDescription: String {
localizedSettings.posts.description
}
let navigationIconPath: String
private var postFeedUrlPrefix: String {
localizedSettings.posts.feedUrlPrefix
}
let mainContentMaximumWidth: CGFloat
private var navigationIconPath: String {
content.settings.navigationBar.iconPath
}
private var mainContentMaximumWidth: CGFloat {
content.settings.posts.contentWidth
}
private let content: Content
private let imageGenerator: ImageGenerator
init(content: Content, configuration: WebsiteGeneratorConfiguration) {
self.language = configuration.language
self.outputDirectory = configuration.outputDirectory
self.postsPerPage = configuration.postsPerPage
self.postFeedTitle = configuration.postFeedTitle
self.postFeedDescription = configuration.postFeedDescription
self.postFeedUrlPrefix = configuration.postFeedUrlPrefix
self.navigationIconPath = configuration.navigationIconPath
self.mainContentMaximumWidth = configuration.mainContentMaximumWidth
init(content: Content, language: ContentLanguage) {
self.language = language
self.content = content
self.localizedSettings = content.settings.localized(in: language)
self.imageGenerator = ImageGenerator(
storage: content.storage,
inputImageFolder: content.storage.filesFolder,
relativeImageOutputPath: "images")
}
func generateWebsite() -> Bool {
func generateWebsite(callback: (String) -> Void) -> Bool {
guard imageGenerator.prepareForGeneration() else {
return false
}
guard createPostFeedPages() else {
return false
}
guard imageGenerator.runJobs() else {
guard imageGenerator.runJobs(callback: callback) else {
return false
}
return imageGenerator.save()
@ -77,7 +67,9 @@ final class WebsiteGenerator {
return true
}
let navBarData = createNavigationBarData()
let navBarData = createNavigationBarData(
settings: content.settings.navigationBar,
iconDescription: localizedSettings.navigationBarIconDescription)
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
for pageIndex in 1...numberOfPages {
@ -91,15 +83,14 @@ final class WebsiteGenerator {
return true
}
private func createNavigationBarData() -> NavigationBarData {
let data = content.websiteData.localized(in: language)
let navigationItems: [NavigationBarLink] = content.websiteData.navigationTags.map {
private func createNavigationBarData(settings: NavigationBarSettings, iconDescription: String) -> NavigationBarData {
let navigationItems: [NavigationBarLink] = settings.tags.map {
let localized = $0.localized(in: language)
return .init(text: localized.name, url: localized.urlComponent)
}
return NavigationBarData(
navigationIconPath: navigationIconPath,
iconDescription: data.iconDescription,
iconDescription: iconDescription,
navigationItems: navigationItems)
}