diff --git a/manifest.json b/manifest.json index bda7c47..c4b7499 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-book-tracker", "name": "Book Tracker", - "version": "1.4.1", + "version": "1.4.2", "minAppVersion": "0.15.0", "description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.", "author": "FiFiTiDo", diff --git a/package.json b/package.json index d4a2a8a..ead8785 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-book-tracker", - "version": "1.4.1", + "version": "1.4.2", "description": "Simplifies tracking your reading progress and managing your book collection in Obsidian.", "main": "main.js", "scripts": { diff --git a/src/main.ts b/src/main.ts index 2bf2085..74724af 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,7 @@ import { DEFAULT_SETTINGS, BookTrackerSettingTab, } from "@ui/settings"; -import { Templater } from "@utils/Templater"; +import { safeString, Templater } from "@utils/Templater"; import { CONTENT_TYPE_EXTENSIONS } from "./const"; import { Storage } from "@utils/Storage"; import { ReadingLog } from "@utils/ReadingLog"; @@ -34,6 +34,7 @@ import { registerReadingCalendarCodeBlockProcessor } from "@ui/code-blocks/Readi import { registerAToZChallengeCodeBlockProcessor } from "@ui/code-blocks/AToZChallengeCodeBlock"; import moment from "@external/moment"; import { compressImage } from "@utils/image"; +import { titleSortValue } from "@utils/text"; export default class BookTrackerPlugin extends Plugin { public settings: BookTrackerPluginSettings; @@ -112,14 +113,14 @@ export default class BookTrackerPlugin extends Plugin { ): Promise { const response = await requestUrl(coverImageUrl); const contentType = response.headers["content-type"]; - const extension = CONTENT_TYPE_EXTENSIONS[contentType || ""] || ""; - if (extension === "") { - throw new Error("Unsupported content type: " + contentType); - } + + const urlExtension = coverImageUrl.split(".").pop(); + const extension = + CONTENT_TYPE_EXTENSIONS[contentType || ""] ?? urlExtension ?? "jpg"; let filePath = this.settings.coverFolder + "/"; if (this.settings.groupCoversByFirstLetter) { - let groupName = fileName.charAt(0).toUpperCase(); + let groupName = titleSortValue(fileName).charAt(0).toUpperCase(); if (!/^[A-Z]$/.test(groupName)) { groupName = "#"; } @@ -162,19 +163,43 @@ export default class BookTrackerPlugin extends Plugin { async createEntry(book: Book): Promise { const fileName = this.templater .renderTemplate(this.settings.fileNameFormat, { - title: book.title, - authors: book.authors.map((a) => a.name).join(", "), + title: safeString(book.title), + authors: safeString(book.authors.map((a) => a.name).join(", ")), }) .replace(/[/:*?<>|""]/g, ""); - const data: Record = { book }; + // Wrap strings as safe strings to avoid Handlebars escaping + const data: Record = { + book: { + title: safeString(book.title), + subtitle: safeString(book.subtitle), + description: book.description, + authors: book.authors.map((a) => ({ + ...a, + name: safeString(a.name), + })), + series: book.series + ? { + ...book.series, + title: safeString(book.series.title), + } + : null, + publisher: safeString(book.publisher), + publishedAt: book.publishedAt, + genres: book.genres.map((g) => safeString(g)), + coverImageUrl: safeString(book.coverImageUrl), + pageCount: book.pageCount, + isbn: safeString(book.isbn), + isbn13: safeString(book.isbn13), + }, + }; if (this.settings.downloadCovers && book.coverImageUrl) { const coverImageFile = await this.downloadCoverImage( book.coverImageUrl, fileName ); - data.coverImagePath = coverImageFile.path; + data.coverImagePath = safeString(coverImageFile.path); } const renderedContent = await this.templater.renderTemplateFile( @@ -225,7 +250,15 @@ export default class BookTrackerPlugin extends Plugin { const getDate = (key: string) => { const value = fm?.[key]; - if (typeof value === "string" || value instanceof Date) { + if (typeof value === "string") { + if (value.includes(" ")) { + return moment(value, "YYYY-MM-DD HH:mm:ss"); + } else if (value.includes("T")) { + return moment(value, "YYYY-MM-DDTHH:mm:ss"); + } else { + return moment(value, "YYYY-MM-DD"); + } + } else if (value instanceof Date) { return moment(value); } return null; diff --git a/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte b/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte index 6080dfd..36272e9 100644 --- a/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte +++ b/src/ui/code-blocks/AToZChallengeCodeBlockView.svelte @@ -13,6 +13,7 @@ import DateFilter from "@ui/components/DateFilter.svelte"; import BookCover from "@ui/components/BookCover.svelte"; import { setAppContext } from "@ui/stores/app"; + import { titleSortValue } from "@utils/text"; const { plugin }: SvelteCodeBlockProps = $props(); setAppContext(plugin.app); @@ -26,25 +27,15 @@ disableMonthFilter: true, }); - const getSortValue = (value: string) => { - if (value.startsWith("A ")) { - return value.slice(2) + ", A"; - } else if (value.startsWith("An ")) { - return value.slice(3) + ", An"; - } else if (value.startsWith("The ")) { - return value.slice(4) + ", The"; - } else { - return value; - } - }; - const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); const metadata = $derived( metadataStore.metadata.reduce( (acc, meta) => { const title = meta.book.title; - const firstLetter = getSortValue(title).charAt(0).toUpperCase(); + const firstLetter = titleSortValue(title) + .charAt(0) + .toUpperCase(); if (!firstLetter.match(/[A-Z]/)) { return acc; diff --git a/src/ui/components/BookCover.svelte b/src/ui/components/BookCover.svelte index 5fb25e1..6643cfe 100644 --- a/src/ui/components/BookCover.svelte +++ b/src/ui/components/BookCover.svelte @@ -87,16 +87,17 @@ } .book-cover { - width: var(--book-cover-width); - height: var( - --book-cover-height, - calc(var(--book-cover-width) / (var(--book-cover-aspect-ratio))) - ); + &, + & img { + width: var(--book-cover-width); + height: var( + --book-cover-height, + calc(var(--book-cover-width) / (var(--book-cover-aspect-ratio))) + ); + } img { border-radius: var(--radius-l); - height: 100%; - width: 100%; object-fit: cover; object-position: center; } diff --git a/src/ui/components/DetailsView.svelte b/src/ui/components/DetailsView.svelte index 5a7bca1..90808d0 100644 --- a/src/ui/components/DetailsView.svelte +++ b/src/ui/components/DetailsView.svelte @@ -7,6 +7,7 @@ import OpenFileLink from "./OpenFileLink.svelte"; import BookCover from "./BookCover.svelte"; import { getReadingLogContext } from "@ui/stores/reading-log.svelte"; + import { Platform } from "obsidian"; interface Props { settings: ShelfSettings; @@ -21,7 +22,10 @@
{#each metadataStore.metadata as meta}
- +

diff --git a/src/utils/Templater.ts b/src/utils/Templater.ts index 132033f..5d58f34 100644 --- a/src/utils/Templater.ts +++ b/src/utils/Templater.ts @@ -25,6 +25,10 @@ Handlebars.registerHelper("indent", (text: string, indent = " ") => { ); }); +export function safeString(str: string) { + return new Handlebars.SafeString(str); +} + export class Templater { public constructor(private readonly app: App) {} diff --git a/src/utils/image.ts b/src/utils/image.ts index e3bb6a2..61bf1d0 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -47,8 +47,6 @@ export async function compressImage( } } - console.log(width, height); - canvas.width = width!; canvas.height = height!; diff --git a/src/utils/text.ts b/src/utils/text.ts new file mode 100644 index 0000000..0aa890c --- /dev/null +++ b/src/utils/text.ts @@ -0,0 +1,11 @@ +export function titleSortValue(title: string): string { + if (title.toLowerCase().startsWith("a ")) { + return title.slice(2) + ", A"; + } else if (title.toLowerCase().startsWith("an ")) { + return title.slice(3) + ", An"; + } else if (title.toLowerCase().startsWith("the ")) { + return title.slice(4) + ", The"; + } else { + return title; + } +} diff --git a/versions.json b/versions.json index dbd02df..b8e2af4 100644 --- a/versions.json +++ b/versions.json @@ -6,5 +6,6 @@ "1.3.1": "0.15.0", "1.3.2": "0.15.0", "1.4.0": "0.15.0", - "1.4.1": "0.15.0" + "1.4.1": "0.15.0", + "1.4.2": "0.15.0" } \ No newline at end of file