Generate tag overview, add file action

This commit is contained in:
Christoph Hagen
2025-01-05 09:21:21 +01:00
parent 0dca633805
commit 01baf560ee
27 changed files with 501 additions and 137 deletions

View File

@ -11,6 +11,7 @@ extension Content {
self.copyRequiredFiles()
self.generateRequiredImages()
self.results.recalculate()
self.status("Generation completed")
}
}
@ -61,9 +62,6 @@ extension Content {
}
private func generateRequiredImages() {
let imageGenerator = ImageGenerator(
storage: storage,
settings: settings)
let images = results.imagesToGenerate.sorted()
let count = images.count
@ -255,7 +253,7 @@ extension Content {
guard shouldGenerateWebsite else { return }
let results = results.makeResults(for: .tagOverview, in: language)
let generator = TagOverviewGenerator(content: self, language: language, results: results)
generator.generatePage(tags: tags, overview: tagOverview)
generator.generatePages(tags: tags, overview: tagOverview)
}
}

View File

@ -10,6 +10,7 @@ extension Content {
subtitle: tag.subtitle,
description: tag.description,
thumbnail: tag.thumbnail.map { images[$0] },
linkPreviewTitle: tag.linkPreviewTitle,
originalUrl: tag.originalURL)
}

View File

@ -129,6 +129,7 @@ private extension LocalizedTag {
name: name,
subtitle: subtitle,
description: description,
linkPreviewTitle: linkPreviewTitle,
thumbnail: linkPreviewImage?.id,
originalURL: originalUrl)
}

View File

@ -7,7 +7,7 @@ extension Content {
private static let disallowedCharactersInFileIds = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-.")).inverted
func isNewIdForTag(_ id: String) -> Bool {
!tags.contains { $0.id == id }
tagOverview?.id != id && !tags.contains { $0.id == id }
}
func isNewIdForPage(_ id: String) -> Bool {

View File

@ -37,6 +37,8 @@ final class Content: ObservableObject {
@Published
private(set) var shouldGenerateWebsite = false
let imageGenerator: ImageGenerator
init(settings: Settings,
posts: [Post],
pages: [Page],
@ -53,6 +55,9 @@ final class Content: ObservableObject {
let storage = Storage()
self.storage = storage
self.imageGenerator = ImageGenerator(
storage: storage,
settings: settings)
}
init() {
@ -67,6 +72,9 @@ final class Content: ObservableObject {
let storage = Storage()
self.storage = storage
self.imageGenerator = ImageGenerator(
storage: storage,
settings: settings)
}
private func clear() {
@ -112,4 +120,9 @@ final class Content: ObservableObject {
print("Failed to reload content: \(error)")
}
}
func remove(_ file: FileResource) {
files.remove(file)
#warning("Remove file from required files, thumbnails, post images, etc.")
}
}

View File

@ -14,8 +14,13 @@ final class FileResource: Item {
@Published
var english: String
/// The dimensions of the image
@Published
var size: CGSize = .zero
var imageDimensions: CGSize? = nil
/// The size of the file in bytes
@Published
var fileSize: Int? = nil
init(content: Content, id: String, isExternallyStored: Bool, en: String, de: String) {
self.type = FileType(fileExtension: id.fileExtension)
@ -49,10 +54,13 @@ final class FileResource: Item {
// MARK: Images
var aspectRatio: CGFloat {
guard size.height > 0 else {
guard let imageDimensions else {
return 0
}
return size.width / size.height
guard imageDimensions.height > 0 else {
return 0
}
return imageDimensions.width / imageDimensions.height
}
var imageToDisplay: Image {
@ -60,18 +68,58 @@ final class FileResource: Item {
print("Failed to load data for image \(id)")
return failureImage
}
if fileSize == nil {
DispatchQueue.main.async {
self.fileSize = imageData.count
}
}
guard let loadedImage = NSImage(data: imageData) else {
print("Failed to create image \(id)")
return failureImage
}
if self.size == .zero && loadedImage.size != .zero {
if loadedImage.size != imageDimensions {
DispatchQueue.main.async {
self.size = loadedImage.size
self.imageDimensions = loadedImage.size
}
}
return .init(nsImage: loadedImage)
}
func determineImageDimensions() {
let size = getImageDimensions()
DispatchQueue.main.async {
self.imageDimensions = size
}
}
private func getImageDimensions() -> CGSize? {
guard type.isImage else {
return nil
}
guard let imageData = content.storage.fileData(for: id) else {
return nil
}
guard let loadedImage = NSImage(data: imageData) else {
return nil
}
return loadedImage.size
}
func determineFileSize() {
DispatchQueue.global(qos: .userInitiated).async {
let size = self.content.storage.size(of: self.id)
DispatchQueue.main.async {
self.fileSize = size
}
}
}
func removeGeneratedImages() {
content.imageGenerator.removeVersions(of: id)
content.storage.deleteInOutputFolder(path: outputImageFolder)
}
private var failureImage: Image {
Image(systemSymbol: .exclamationmarkTriangle)
}
@ -105,6 +153,13 @@ final class FileResource: Item {
// MARK: Paths
func removeFileFromOutputFolder() {
content.storage.deleteInOutputFolder(path: absoluteUrl)
if type.isImage {
removeGeneratedImages()
}
}
/**
Get the url path to a file in the output folder.
The result is an absolute path from the output folder for use in HTML.

View File

@ -4,6 +4,10 @@ class Item: ObservableObject, Identifiable {
unowned let content: Content
/// A dummy property to force views to update when properties change
@Published
private var changeToggle = false
@Published
var id: String
@ -12,6 +16,10 @@ class Item: ObservableObject, Identifiable {
self.id = id
}
func didChange() {
changeToggle.toggle()
}
func makeCleanAbsolutePath(_ path: String) -> String {
"/" + makeCleanRelativePath(path)
}

View File

@ -21,6 +21,9 @@ final class LocalizedTag: ObservableObject {
@Published
var linkPreviewImage: FileResource?
@Published
var linkPreviewTitle: String?
/// The original url in the previous site layout
let originalUrl: String?
@ -30,6 +33,7 @@ final class LocalizedTag: ObservableObject {
subtitle: String? = nil,
description: String? = nil,
thumbnail: FileResource? = nil,
linkPreviewTitle: String? = nil,
originalUrl: String? = nil) {
self.content = content
self.urlComponent = urlComponent
@ -37,6 +41,7 @@ final class LocalizedTag: ObservableObject {
self.subtitle = subtitle
self.description = description
self.linkPreviewImage = thumbnail
self.linkPreviewTitle = linkPreviewTitle
self.originalUrl = originalUrl
}
@ -48,10 +53,6 @@ final class LocalizedTag: ObservableObject {
extension LocalizedTag: LinkPreviewItem {
var linkPreviewTitle: String? {
self.name
}
var linkPreviewDescription: String? {
description
}

View File

@ -26,12 +26,19 @@ final class Tag: Item {
super.init(content: content, id: id)
}
var linkName: String {
id.lowercased().replacingOccurrences(of: " ", with: "-")
func isValid(id: String) -> Bool {
content.isValidIdForTagOrPageOrPost(id) &&
content.isNewIdForTag(id)
}
var url: String {
"/tags/\(linkName).html"
@discardableResult
func update(id newId: String) -> Bool {
guard content.storage.move(tag: id, to: newId) else {
print("Failed to move files of tag \(id)")
return false
}
id = newId
return true
}
// MARK: Paths