generated from tpl/obsidian-sample-plugin
			Put actual books on bookshelf view
This commit is contained in:
		
							parent
							
								
									658713fbec
								
							
						
					
					
						commit
						b9f146f922
					
				| 
						 | 
					@ -10,7 +10,7 @@ import { EditorCheckCommand } from "./Command";
 | 
				
			||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
					import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
				
			||||||
import { RatingModal } from "@ui/modals";
 | 
					import { RatingModal } from "@ui/modals";
 | 
				
			||||||
import type { ReadingLog } from "@utils/ReadingLog";
 | 
					import type { ReadingLog } from "@utils/ReadingLog";
 | 
				
			||||||
import { READ_STATE } from "@src/const";
 | 
					import { STATUS_READ } from "@src/const";
 | 
				
			||||||
import { mkdirRecursive, dirname } from "@utils/fs";
 | 
					import { mkdirRecursive, dirname } from "@utils/fs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class LogReadingFinishedCommand extends EditorCheckCommand {
 | 
					export class LogReadingFinishedCommand extends EditorCheckCommand {
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,7 @@ export class LogReadingFinishedCommand extends EditorCheckCommand {
 | 
				
			||||||
		const endDate = moment().format("YYYY-MM-DD");
 | 
							const endDate = moment().format("YYYY-MM-DD");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
							this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
				
			||||||
			frontMatter[this.settings.statusProperty] = READ_STATE;
 | 
								frontMatter[this.settings.statusProperty] = STATUS_READ;
 | 
				
			||||||
			frontMatter[this.settings.endDateProperty] = endDate;
 | 
								frontMatter[this.settings.endDateProperty] = endDate;
 | 
				
			||||||
			frontMatter[this.settings.ratingProperty] = ratings.rating;
 | 
								frontMatter[this.settings.ratingProperty] = ratings.rating;
 | 
				
			||||||
			if (this.settings.spiceProperty !== "") {
 | 
								if (this.settings.spiceProperty !== "") {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import {
 | 
				
			||||||
} from "obsidian";
 | 
					} from "obsidian";
 | 
				
			||||||
import { EditorCheckCommand } from "./Command";
 | 
					import { EditorCheckCommand } from "./Command";
 | 
				
			||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
					import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
				
			||||||
import { IN_PROGRESS_STATE } from "@src/const";
 | 
					import { STATUS_IN_PROGRESS } from "@src/const";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class LogReadingStartedCommand extends EditorCheckCommand {
 | 
					export class LogReadingStartedCommand extends EditorCheckCommand {
 | 
				
			||||||
	constructor(
 | 
						constructor(
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ export class LogReadingStartedCommand extends EditorCheckCommand {
 | 
				
			||||||
		const startDate = moment().format("YYYY-MM-DD");
 | 
							const startDate = moment().format("YYYY-MM-DD");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
							this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
				
			||||||
			frontMatter[this.settings.statusProperty] = IN_PROGRESS_STATE;
 | 
								frontMatter[this.settings.statusProperty] = STATUS_IN_PROGRESS;
 | 
				
			||||||
			frontMatter[this.settings.startDateProperty] = startDate;
 | 
								frontMatter[this.settings.startDateProperty] = startDate;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import {
 | 
				
			||||||
import { EditorCheckCommand } from "./Command";
 | 
					import { EditorCheckCommand } from "./Command";
 | 
				
			||||||
import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
					import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
				
			||||||
import type { ReadingLog } from "@utils/ReadingLog";
 | 
					import type { ReadingLog } from "@utils/ReadingLog";
 | 
				
			||||||
import { TO_BE_READ_STATE } from "@src/const";
 | 
					import { STATUS_TO_BE_READ } from "@src/const";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ResetReadingStatusCommand extends EditorCheckCommand {
 | 
					export class ResetReadingStatusCommand extends EditorCheckCommand {
 | 
				
			||||||
	constructor(
 | 
						constructor(
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ export class ResetReadingStatusCommand extends EditorCheckCommand {
 | 
				
			||||||
		const file = ctx.file!;
 | 
							const file = ctx.file!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
							this.app.fileManager.processFrontMatter(file, (frontMatter) => {
 | 
				
			||||||
			frontMatter[this.settings.statusProperty] = TO_BE_READ_STATE;
 | 
								frontMatter[this.settings.statusProperty] = STATUS_TO_BE_READ;
 | 
				
			||||||
			frontMatter[this.settings.startDateProperty] = "";
 | 
								frontMatter[this.settings.startDateProperty] = "";
 | 
				
			||||||
			frontMatter[this.settings.endDateProperty] = "";
 | 
								frontMatter[this.settings.endDateProperty] = "";
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
export const TO_BE_READ_STATE = "To Be Read";
 | 
					export const STATUS_TO_BE_READ = "To Be Read";
 | 
				
			||||||
export const IN_PROGRESS_STATE = "Currently Reading";
 | 
					export const STATUS_IN_PROGRESS = "Currently Reading";
 | 
				
			||||||
export const READ_STATE = "Read";
 | 
					export const STATUS_READ = "Read";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
 | 
					export const CONTENT_TYPE_EXTENSIONS: Record<string, string> = {
 | 
				
			||||||
	"image/jpeg": "jpg",
 | 
						"image/jpeg": "jpg",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								src/types.ts
								
								
								
								
							
							
						
						
									
										12
									
								
								src/types.ts
								
								
								
								
							| 
						 | 
					@ -1,4 +1,8 @@
 | 
				
			||||||
import type { IN_PROGRESS_STATE, READ_STATE, TO_BE_READ_STATE } from "./const";
 | 
					import type {
 | 
				
			||||||
 | 
						STATUS_IN_PROGRESS,
 | 
				
			||||||
 | 
						STATUS_READ,
 | 
				
			||||||
 | 
						STATUS_TO_BE_READ,
 | 
				
			||||||
 | 
					} from "./const";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Author {
 | 
					export interface Author {
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
| 
						 | 
					@ -24,8 +28,8 @@ export interface Book {
 | 
				
			||||||
	isbn13: string;
 | 
						isbn13: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ToBeReadState = typeof TO_BE_READ_STATE;
 | 
					export type ToBeReadState = typeof STATUS_TO_BE_READ;
 | 
				
			||||||
export type InProgressState = typeof IN_PROGRESS_STATE;
 | 
					export type InProgressState = typeof STATUS_IN_PROGRESS;
 | 
				
			||||||
export type ReadState = typeof READ_STATE;
 | 
					export type ReadState = typeof STATUS_READ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ReadingState = ToBeReadState | InProgressState | ReadState;
 | 
					export type ReadingState = ToBeReadState | InProgressState | ReadState;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import { registerCodeBlockRenderer } from ".";
 | 
				
			||||||
import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
 | 
					import { SvelteCodeBlockRenderer } from "./SvelteCodeBlockRenderer";
 | 
				
			||||||
import BookshelfCodeBlockView from "./BookshelfCodeBlockView.svelte";
 | 
					import BookshelfCodeBlockView from "./BookshelfCodeBlockView.svelte";
 | 
				
			||||||
import type BookTrackerPlugin from "@src/main";
 | 
					import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
 | 
					import z from "zod/v4";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function registerBookshelfCodeBlockProcessor(
 | 
					export function registerBookshelfCodeBlockProcessor(
 | 
				
			||||||
	plugin: BookTrackerPlugin
 | 
						plugin: BookTrackerPlugin
 | 
				
			||||||
| 
						 | 
					@ -13,6 +14,12 @@ export function registerBookshelfCodeBlockProcessor(
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BookshelfSettingsSchema = z.object({
 | 
				
			||||||
 | 
						titleProperty: z.string(),
 | 
				
			||||||
 | 
						subtitleProperty: z.optional(z.string()),
 | 
				
			||||||
 | 
						authorsProperty: z.string(),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class BookshelfCodeBlockRenderer extends SvelteCodeBlockRenderer<
 | 
					export class BookshelfCodeBlockRenderer extends SvelteCodeBlockRenderer<
 | 
				
			||||||
	typeof BookshelfCodeBlockView
 | 
						typeof BookshelfCodeBlockView
 | 
				
			||||||
> {
 | 
					> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import { READ_STATE } from "@src/const";
 | 
						import { STATUS_TO_BE_READ } from "@src/const";
 | 
				
			||||||
	import type BookTrackerPlugin from "@src/main";
 | 
						import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
	import type { ReadingState } from "@src/types";
 | 
						import type { ReadingState } from "@src/types";
 | 
				
			||||||
	import Book from "@ui/components/bookshelf/Book.svelte";
 | 
						import Book from "@ui/components/bookshelf/Book.svelte";
 | 
				
			||||||
| 
						 | 
					@ -9,26 +9,42 @@
 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		createMetadata,
 | 
							createMetadata,
 | 
				
			||||||
		setMetadataContext,
 | 
							setMetadataContext,
 | 
				
			||||||
 | 
							type FileMetadata,
 | 
				
			||||||
	} from "@ui/stores/metadata.svelte";
 | 
						} from "@ui/stores/metadata.svelte";
 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		createSettings,
 | 
							createSettings,
 | 
				
			||||||
		setSettingsContext,
 | 
							setSettingsContext,
 | 
				
			||||||
	} from "@ui/stores/settings.svelte";
 | 
						} from "@ui/stores/settings.svelte";
 | 
				
			||||||
	import { COLOR_NAMES } from "@utils/color";
 | 
						import { COLOR_NAMES, type ColorName } from "@utils/color";
 | 
				
			||||||
	import { randomElement } from "@utils/rand";
 | 
						import { randomElement, randomFloat, randomInt } from "@utils/rand";
 | 
				
			||||||
	import { onDestroy } from "svelte";
 | 
						import { onDestroy } from "svelte";
 | 
				
			||||||
 | 
						import { BookshelfSettingsSchema } from "./BookshelfCodeBlock";
 | 
				
			||||||
 | 
						import { parseYaml, TFile } from "obsidian";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		plugin: BookTrackerPlugin;
 | 
							plugin: BookTrackerPlugin;
 | 
				
			||||||
		source: string;
 | 
							source: string;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { plugin }: Props = $props();
 | 
						interface BookData {
 | 
				
			||||||
 | 
							title: string;
 | 
				
			||||||
 | 
							subtitle?: string;
 | 
				
			||||||
 | 
							author: string;
 | 
				
			||||||
 | 
							width: number;
 | 
				
			||||||
 | 
							color: ColorName;
 | 
				
			||||||
 | 
							design: (typeof designs)[number];
 | 
				
			||||||
 | 
							orientation: undefined | "tilted" | "on-display";
 | 
				
			||||||
 | 
							file: TFile;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const { plugin, source }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const settings = $derived(BookshelfSettingsSchema.parse(parseYaml(source)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const settingsStore = createSettings(plugin);
 | 
						const settingsStore = createSettings(plugin);
 | 
				
			||||||
	setSettingsContext(settingsStore);
 | 
						setSettingsContext(settingsStore);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let stateFilter: ReadingState = $state(READ_STATE);
 | 
						let stateFilter: ReadingState = $state(STATUS_TO_BE_READ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const metadataStore = createMetadata(plugin, stateFilter);
 | 
						const metadataStore = createMetadata(plugin, stateFilter);
 | 
				
			||||||
	setMetadataContext(metadataStore);
 | 
						setMetadataContext(metadataStore);
 | 
				
			||||||
| 
						 | 
					@ -40,155 +56,113 @@
 | 
				
			||||||
		"split-bands",
 | 
							"split-bands",
 | 
				
			||||||
	] as const;
 | 
						] as const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function randomDesign() {
 | 
						const randomDesign = () => randomElement(designs);
 | 
				
			||||||
		return randomElement(designs);
 | 
						const randomColor = () => randomElement(COLOR_NAMES);
 | 
				
			||||||
 | 
						function randomOrientation() {
 | 
				
			||||||
 | 
							const n = randomFloat();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (n < 0.55) {
 | 
				
			||||||
 | 
								return undefined;
 | 
				
			||||||
 | 
							} else if (n < 0.8) {
 | 
				
			||||||
 | 
								return "tilted";
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return "on-display";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const randomStackChance = () => randomFloat() > 0.9;
 | 
				
			||||||
 | 
						function randomStackSize() {
 | 
				
			||||||
 | 
							const n = randomFloat();
 | 
				
			||||||
 | 
							if (n < 0.2) {
 | 
				
			||||||
 | 
								return 5;
 | 
				
			||||||
 | 
							} else if (n < 0.5) {
 | 
				
			||||||
 | 
								return 4;
 | 
				
			||||||
 | 
							} else if (n < 0.8) {
 | 
				
			||||||
 | 
								return 3;
 | 
				
			||||||
 | 
							} else if (n < 0.98) {
 | 
				
			||||||
 | 
								return 2;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						const calculateWidth = (pageCount: number) =>
 | 
				
			||||||
 | 
							Math.max(10, Math.min(1000, 200 + pageCount * 10));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function getBookData(metadata: FileMetadata): BookData {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								title: metadata.frontmatter[settings.titleProperty],
 | 
				
			||||||
 | 
								subtitle: settings.subtitleProperty
 | 
				
			||||||
 | 
									? metadata.frontmatter[settings.subtitleProperty]
 | 
				
			||||||
 | 
									: undefined,
 | 
				
			||||||
 | 
								author: metadata.frontmatter[settings.authorsProperty].join(", "),
 | 
				
			||||||
 | 
								width: calculateWidth(
 | 
				
			||||||
 | 
									metadata.frontmatter[settingsStore.settings.pageCountProperty],
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
								color: randomColor(),
 | 
				
			||||||
 | 
								design: randomDesign(),
 | 
				
			||||||
 | 
								orientation: randomOrientation(),
 | 
				
			||||||
 | 
								file: metadata.file,
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function randomColor() {
 | 
						const books = $derived.by(() => {
 | 
				
			||||||
		return randomElement(COLOR_NAMES);
 | 
							let books: (BookData | BookData[])[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (let i = 0; i < metadataStore.metadata.length; i++) {
 | 
				
			||||||
 | 
								if (randomStackChance()) {
 | 
				
			||||||
 | 
									const booksRemaining = metadataStore.metadata.length - i;
 | 
				
			||||||
 | 
									const stackSize = randomInt(
 | 
				
			||||||
 | 
										1,
 | 
				
			||||||
 | 
										Math.min(booksRemaining, randomStackSize()),
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									books.push(
 | 
				
			||||||
 | 
										metadataStore.metadata
 | 
				
			||||||
 | 
											.slice(i, i + stackSize)
 | 
				
			||||||
 | 
											.map(getBookData),
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									i += stackSize - 1;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									books.push(getBookData(metadataStore.metadata[i]));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return books;
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onDestroy(() => metadataStore.destroy());
 | 
						onDestroy(() => metadataStore.destroy());
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<Bookshelf>
 | 
					<Bookshelf>
 | 
				
			||||||
	<Book
 | 
						{#each books as book}
 | 
				
			||||||
		title="Hello World"
 | 
							{#if Array.isArray(book)}
 | 
				
			||||||
		width={1120}
 | 
								<BookStack totalChildren={book.length}>
 | 
				
			||||||
		color={randomColor()}
 | 
									{#each book as bookData}
 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="White Space"
 | 
					 | 
				
			||||||
		width={700}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="The Art of Computer Programming Vol 1"
 | 
					 | 
				
			||||||
		width={1156}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="Cascading Style Sheets"
 | 
					 | 
				
			||||||
		subtitle="Guide to Design"
 | 
					 | 
				
			||||||
		width={560}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
		orientation="tilted"
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="HTML5"
 | 
					 | 
				
			||||||
		subtitle="Welcome to the Web"
 | 
					 | 
				
			||||||
		width={1350}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<BookStack totalChildren={2}>
 | 
					 | 
				
			||||||
					<BookStackElement
 | 
										<BookStackElement
 | 
				
			||||||
			title="Coding for Dummies"
 | 
											title={bookData.title}
 | 
				
			||||||
			subtitle="JS tutorial"
 | 
											subtitle={bookData.subtitle}
 | 
				
			||||||
			color={randomColor()}
 | 
											color={bookData.color}
 | 
				
			||||||
			design={randomDesign()}
 | 
											design={bookData.design}
 | 
				
			||||||
		/>
 | 
											onClick={() =>
 | 
				
			||||||
		<BookStackElement
 | 
												plugin.app.workspace.openLinkText(
 | 
				
			||||||
			title="Coding for Dummies"
 | 
													bookData.file.path,
 | 
				
			||||||
			subtitle="C# tutorial"
 | 
													"",
 | 
				
			||||||
			color={randomColor()}
 | 
													true,
 | 
				
			||||||
			design={randomDesign()}
 | 
												)}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
 | 
									{/each}
 | 
				
			||||||
			</BookStack>
 | 
								</BookStack>
 | 
				
			||||||
 | 
							{:else}
 | 
				
			||||||
			<Book
 | 
								<Book
 | 
				
			||||||
		title="CoffeeScript"
 | 
									title={book.title}
 | 
				
			||||||
		subtitle="The JS Alternative"
 | 
									subtitle={book.subtitle}
 | 
				
			||||||
		author="The Dev Guy"
 | 
									author={book.author}
 | 
				
			||||||
		color={randomColor()}
 | 
									width={book.width}
 | 
				
			||||||
		design={randomDesign()}
 | 
									color={book.color}
 | 
				
			||||||
		orientation="on-display"
 | 
									design={book.design}
 | 
				
			||||||
 | 
									orientation={book.orientation}
 | 
				
			||||||
 | 
									onClick={() =>
 | 
				
			||||||
 | 
										plugin.app.workspace.openLinkText(book.file.path, "", true)}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
	<Book
 | 
							{/if}
 | 
				
			||||||
		title="Cheat Sheet"
 | 
						{/each}
 | 
				
			||||||
		subtitle="Guide to Design"
 | 
					 | 
				
			||||||
		width={870}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="Psychology of Colors"
 | 
					 | 
				
			||||||
		width={540}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="TypeScript"
 | 
					 | 
				
			||||||
		subtitle="Intro JS to type checking"
 | 
					 | 
				
			||||||
		width={1130}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="Testing"
 | 
					 | 
				
			||||||
		width={10}
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="JavaScript"
 | 
					 | 
				
			||||||
		subtitle="The Definitive Guide"
 | 
					 | 
				
			||||||
		author="David Flanagan"
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
		orientation="on-display"
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="Pragmatic Programmer"
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book title="White Space" color={randomColor()} design={randomDesign()} />
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="W3 Schools"
 | 
					 | 
				
			||||||
		subtitle="The best around"
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
		orientation="tilted"
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="UI/UX"
 | 
					 | 
				
			||||||
		subtitle="Guide to Mobile Development"
 | 
					 | 
				
			||||||
		author="John Doe"
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
		orientation="on-display"
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book
 | 
					 | 
				
			||||||
		title="Clean Code"
 | 
					 | 
				
			||||||
		color={randomColor()}
 | 
					 | 
				
			||||||
		design={randomDesign()}
 | 
					 | 
				
			||||||
		orientation="tilted"
 | 
					 | 
				
			||||||
	/>
 | 
					 | 
				
			||||||
	<Book title="Docs for Devs" color={randomColor()} design={randomDesign()} />
 | 
					 | 
				
			||||||
	<BookStack totalChildren={4}>
 | 
					 | 
				
			||||||
		<BookStackElement
 | 
					 | 
				
			||||||
			title="The Art of Computer Programming Vol 1"
 | 
					 | 
				
			||||||
			color={randomColor()}
 | 
					 | 
				
			||||||
			design={randomDesign()}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
		<BookStackElement
 | 
					 | 
				
			||||||
			title="The Art of Computer Programming Vol 2"
 | 
					 | 
				
			||||||
			color={randomColor()}
 | 
					 | 
				
			||||||
			design={randomDesign()}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
		<BookStackElement
 | 
					 | 
				
			||||||
			title="The Art of Computer Programming Vol 3"
 | 
					 | 
				
			||||||
			color={randomColor()}
 | 
					 | 
				
			||||||
			design={randomDesign()}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
		<BookStackElement
 | 
					 | 
				
			||||||
			title="The Art of Computer Programming Vol 4a"
 | 
					 | 
				
			||||||
			color={randomColor()}
 | 
					 | 
				
			||||||
			design={randomDesign()}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
	</BookStack>
 | 
					 | 
				
			||||||
</Bookshelf>
 | 
					</Bookshelf>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
	import { createReadingLog } from "@ui/stores/reading-log.svelte";
 | 
						import { createReadingLog } from "@ui/stores/reading-log.svelte";
 | 
				
			||||||
	import { ALL_TIME } from "@ui/stores/date-filter.svelte";
 | 
						import { ALL_TIME } from "@ui/stores/date-filter.svelte";
 | 
				
			||||||
	import { onDestroy } from "svelte";
 | 
						import { onDestroy } from "svelte";
 | 
				
			||||||
 | 
						import { getLinkpath } from "obsidian";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		plugin: BookTrackerPlugin;
 | 
							plugin: BookTrackerPlugin;
 | 
				
			||||||
| 
						 | 
					@ -14,9 +15,7 @@
 | 
				
			||||||
	const { plugin }: Props = $props();
 | 
						const { plugin }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function bookUri(book: string) {
 | 
						function bookUri(book: string) {
 | 
				
			||||||
		const v = encodeURIComponent(plugin.app.vault.getName());
 | 
							return getLinkpath(book + ".md");
 | 
				
			||||||
		const f = encodeURIComponent(book + ".md");
 | 
					 | 
				
			||||||
		return `obsidian://open?vault=${v}&file=${f}`;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const store = createReadingLog(plugin.readingLog);
 | 
						const store = createReadingLog(plugin.readingLog);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@
 | 
				
			||||||
		orientation?: "tilted" | "on-display";
 | 
							orientation?: "tilted" | "on-display";
 | 
				
			||||||
		height?: number;
 | 
							height?: number;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let {
 | 
						let {
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,7 @@
 | 
				
			||||||
		orientation,
 | 
							orientation,
 | 
				
			||||||
		height,
 | 
							height,
 | 
				
			||||||
		width,
 | 
							width,
 | 
				
			||||||
 | 
							onClick,
 | 
				
			||||||
	}: BookProps = $props();
 | 
						}: BookProps = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function widthCheck(input: number | undefined) {
 | 
						function widthCheck(input: number | undefined) {
 | 
				
			||||||
| 
						 | 
					@ -69,10 +71,11 @@
 | 
				
			||||||
				{design}
 | 
									{design}
 | 
				
			||||||
				{height}
 | 
									{height}
 | 
				
			||||||
				{width}
 | 
									{width}
 | 
				
			||||||
 | 
									{onClick}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
		</BookTiltedDefault>
 | 
							</BookTiltedDefault>
 | 
				
			||||||
	{:else if orientation === "on-display"}
 | 
						{:else if orientation === "on-display"}
 | 
				
			||||||
		<BookOnDisplay color={color.hex}>
 | 
							<BookOnDisplay color={color.hex} {onClick}>
 | 
				
			||||||
			<div
 | 
								<div
 | 
				
			||||||
				class="book-display-crease"
 | 
									class="book-display-crease"
 | 
				
			||||||
				style:--book-color={color.hex}
 | 
									style:--book-color={color.hex}
 | 
				
			||||||
| 
						 | 
					@ -84,19 +87,19 @@
 | 
				
			||||||
		</BookOnDisplay>
 | 
							</BookOnDisplay>
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
{:else if design === "split-bands"}
 | 
					{:else if design === "split-bands"}
 | 
				
			||||||
	<BookSplitBands color={color.hex} width={verifiedWidth}>
 | 
						<BookSplitBands color={color.hex} width={verifiedWidth} {onClick}>
 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
							<BookText {title} {subtitle} />
 | 
				
			||||||
	</BookSplitBands>
 | 
						</BookSplitBands>
 | 
				
			||||||
{:else if design === "dual-top-bands"}
 | 
					{:else if design === "dual-top-bands"}
 | 
				
			||||||
	<BookDualTopBands color={color.hex} width={verifiedWidth}>
 | 
						<BookDualTopBands color={color.hex} width={verifiedWidth} {onClick}>
 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
							<BookText {title} {subtitle} />
 | 
				
			||||||
	</BookDualTopBands>
 | 
						</BookDualTopBands>
 | 
				
			||||||
{:else if design === "colored-spine"}
 | 
					{:else if design === "colored-spine"}
 | 
				
			||||||
	<BookColoredSpine color={color.hex} width={verifiedWidth}>
 | 
						<BookColoredSpine color={color.hex} width={verifiedWidth} {onClick}>
 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
							<BookText {title} {subtitle} />
 | 
				
			||||||
	</BookColoredSpine>
 | 
						</BookColoredSpine>
 | 
				
			||||||
{:else}
 | 
					{:else}
 | 
				
			||||||
	<BookDefault color={color.hex} width={verifiedWidth}>
 | 
						<BookDefault color={color.hex} width={verifiedWidth} {onClick}>
 | 
				
			||||||
		<BookText {title} {subtitle} />
 | 
							<BookText {title} {subtitle} />
 | 
				
			||||||
	</BookDefault>
 | 
						</BookDefault>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
		subtitle?: string;
 | 
							subtitle?: string;
 | 
				
			||||||
		color?: ColorName | string;
 | 
							color?: ColorName | string;
 | 
				
			||||||
		design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
 | 
							design?: "default" | "split-bands" | "dual-top-bands" | "colored-spine";
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let {
 | 
						let {
 | 
				
			||||||
| 
						 | 
					@ -18,6 +19,7 @@
 | 
				
			||||||
		subtitle,
 | 
							subtitle,
 | 
				
			||||||
		color: colorRaw = "green",
 | 
							color: colorRaw = "green",
 | 
				
			||||||
		design,
 | 
							design,
 | 
				
			||||||
 | 
							onClick,
 | 
				
			||||||
	}: Props = $props();
 | 
						}: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const color = $derived(
 | 
						const color = $derived(
 | 
				
			||||||
| 
						 | 
					@ -29,19 +31,19 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<li class="bookshelf__bookstack-elem">
 | 
					<li class="bookshelf__bookstack-elem">
 | 
				
			||||||
	{#if design === "split-bands"}
 | 
						{#if design === "split-bands"}
 | 
				
			||||||
		<BookStackSplitBands color={color.hex}>
 | 
							<BookStackSplitBands color={color.hex} {onClick}>
 | 
				
			||||||
			<BookText {title} {subtitle} />
 | 
								<BookText {title} {subtitle} />
 | 
				
			||||||
		</BookStackSplitBands>
 | 
							</BookStackSplitBands>
 | 
				
			||||||
	{:else if design === "dual-top-bands"}
 | 
						{:else if design === "dual-top-bands"}
 | 
				
			||||||
		<BookStackDualTopBands color={color.hex}>
 | 
							<BookStackDualTopBands color={color.hex} {onClick}>
 | 
				
			||||||
			<BookText {title} {subtitle} />
 | 
								<BookText {title} {subtitle} />
 | 
				
			||||||
		</BookStackDualTopBands>
 | 
							</BookStackDualTopBands>
 | 
				
			||||||
	{:else if design === "colored-spine"}
 | 
						{:else if design === "colored-spine"}
 | 
				
			||||||
		<BookStackColoredSpine color={color.hex}>
 | 
							<BookStackColoredSpine color={color.hex} {onClick}>
 | 
				
			||||||
			<BookText {title} {subtitle} />
 | 
								<BookText {title} {subtitle} />
 | 
				
			||||||
		</BookStackColoredSpine>
 | 
							</BookStackColoredSpine>
 | 
				
			||||||
	{:else}
 | 
						{:else}
 | 
				
			||||||
		<BookStackDefault color={color.hex}>
 | 
							<BookStackDefault color={color.hex} {onClick}>
 | 
				
			||||||
			<BookText {title} {subtitle} />
 | 
								<BookText {title} {subtitle} />
 | 
				
			||||||
		</BookStackDefault>
 | 
							</BookStackDefault>
 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ $bookEdge: 2px;
 | 
				
			||||||
		display: inline-flex;
 | 
							display: inline-flex;
 | 
				
			||||||
		flex-flow: column nowrap;
 | 
							flex-flow: column nowrap;
 | 
				
			||||||
		list-style: none;
 | 
							list-style: none;
 | 
				
			||||||
 | 
							float: left;
 | 
				
			||||||
		margin: 0;
 | 
							margin: 0;
 | 
				
			||||||
		padding: 0;
 | 
							padding: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,10 @@
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green", width = 40 }: Props = $props();
 | 
						let { children, color = "green", width = 40, onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
	const borderRightColor = $derived(chroma(color).mix("black", 0.04));
 | 
						const borderRightColor = $derived(chroma(color).mix("black", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,10 @@
 | 
				
			||||||
	style:--book-width={width + "px"}
 | 
						style:--book-width={width + "px"}
 | 
				
			||||||
	style:width={width + "px"}
 | 
						style:width={width + "px"}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,10 @@
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green", width = 40 }: Props = $props();
 | 
						let { children, color = "green", width = 40, onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -25,6 +26,10 @@
 | 
				
			||||||
	style:--book-width={width + "px"}
 | 
						style:--book-width={width + "px"}
 | 
				
			||||||
	style:width={width + "px"}
 | 
						style:width={width + "px"}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,10 @@
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green", width = 40 }: Props = $props();
 | 
						let { children, color = "green", width = 40, onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,10 @@
 | 
				
			||||||
	style:--book-width={width + "px"}
 | 
						style:--book-width={width + "px"}
 | 
				
			||||||
	style:width={width + "px"}
 | 
						style:width={width + "px"}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,14 +4,19 @@
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green" }: Props = $props();
 | 
						let { children, color = "green", onClick }: Props = $props();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
	class="bookshelf__book-wrapper bookshelf__book-onDisplay"
 | 
						class="bookshelf__book-wrapper bookshelf__book-onDisplay"
 | 
				
			||||||
	style:--book-color={color}
 | 
						style:--book-color={color}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,9 +7,10 @@
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
		width?: number;
 | 
							width?: number;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green", width = 40 }: Props = $props();
 | 
						let { children, color = "green", width = 40, onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,10 @@
 | 
				
			||||||
	style:--book-width={width + "px"}
 | 
						style:--book-width={width + "px"}
 | 
				
			||||||
	style:width={width + "px"}
 | 
						style:width={width + "px"}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green" }: Props = $props();
 | 
						let { children, color = "green", onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
	const borderRightColor = $derived(chroma(color).mix("black", 0.04));
 | 
						const borderRightColor = $derived(chroma(color).mix("black", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -22,6 +23,10 @@
 | 
				
			||||||
	style:--book-border-right-color={borderRightColor.css()}
 | 
						style:--book-border-right-color={borderRightColor.css()}
 | 
				
			||||||
	style:--book-background-color={backgroundColor.css()}
 | 
						style:--book-background-color={backgroundColor.css()}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green" }: Props = $props();
 | 
						let { children, color = "green", onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -22,6 +23,10 @@
 | 
				
			||||||
	style:--book-border-left-color={borderLeftColor.css()}
 | 
						style:--book-border-left-color={borderLeftColor.css()}
 | 
				
			||||||
	style:--book-border-right-color={borderRightColor.css()}
 | 
						style:--book-border-right-color={borderRightColor.css()}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green" }: Props = $props();
 | 
						let { children, color = "green", onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -24,6 +25,10 @@
 | 
				
			||||||
	style:--book-border-right-color={borderRightColor.css()}
 | 
						style:--book-border-right-color={borderRightColor.css()}
 | 
				
			||||||
	style:--book-band-color={bandColor.css()}
 | 
						style:--book-band-color={bandColor.css()}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@
 | 
				
			||||||
	interface Props {
 | 
						interface Props {
 | 
				
			||||||
		children?: Snippet;
 | 
							children?: Snippet;
 | 
				
			||||||
		color?: string;
 | 
							color?: string;
 | 
				
			||||||
 | 
							onClick?: () => void;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let { children, color = "green" }: Props = $props();
 | 
						let { children, color = "green", onClick }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const backgroundColor = $derived(chroma(color));
 | 
						const backgroundColor = $derived(chroma(color));
 | 
				
			||||||
	const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
						const borderLeftColor = $derived(chroma(color).mix("white", 0.04));
 | 
				
			||||||
| 
						 | 
					@ -24,6 +25,10 @@
 | 
				
			||||||
	style:--book-border-right-color={borderRightColor.css()}
 | 
						style:--book-border-right-color={borderRightColor.css()}
 | 
				
			||||||
	style:--book-band-color={bandColor.css()}
 | 
						style:--book-band-color={bandColor.css()}
 | 
				
			||||||
	style:color={textColor}
 | 
						style:color={textColor}
 | 
				
			||||||
 | 
						onclick={onClick}
 | 
				
			||||||
 | 
						onkeydown={(ev) => ev.key === "Enter" && onClick?.()}
 | 
				
			||||||
 | 
						role="link"
 | 
				
			||||||
 | 
						tabindex="0"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	{@render children?.()}
 | 
						{@render children?.()}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
import { READ_STATE } from "@src/const";
 | 
					import { STATUS_READ } from "@src/const";
 | 
				
			||||||
import type { CachedMetadata, TFile } from "obsidian";
 | 
					import type { CachedMetadata, TFile } from "obsidian";
 | 
				
			||||||
import { getContext, setContext } from "svelte";
 | 
					import { getContext, setContext } from "svelte";
 | 
				
			||||||
import { getSettingsContext } from "./settings.svelte";
 | 
					import { getSettingsContext } from "./settings.svelte";
 | 
				
			||||||
import type BookTrackerPlugin from "@src/main";
 | 
					import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
 | 
					import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
 | 
				
			||||||
import type { ReadingState } from "@src/types";
 | 
					import type { ReadingState } from "@src/types";
 | 
				
			||||||
 | 
					import type { BookTrackerPluginSettings } from "@ui/settings";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type FileMetadata = {
 | 
					export type FileMetadata = {
 | 
				
			||||||
	file: TFile;
 | 
						file: TFile;
 | 
				
			||||||
| 
						 | 
					@ -16,34 +17,45 @@ export type FileProperty = {
 | 
				
			||||||
	value: any;
 | 
						value: any;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface MetadataStore extends DateFilterStore {
 | 
					export interface MetadataStore extends DateFilterStore {
 | 
				
			||||||
	get metadata(): FileMetadata[];
 | 
						get metadata(): FileMetadata[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	destroy(): void;
 | 
						destroy(): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function createMetadata(
 | 
					
 | 
				
			||||||
 | 
					function getMetadata(
 | 
				
			||||||
	plugin: BookTrackerPlugin,
 | 
						plugin: BookTrackerPlugin,
 | 
				
			||||||
	state: ReadingState = READ_STATE
 | 
						settings: BookTrackerPluginSettings,
 | 
				
			||||||
): MetadataStore {
 | 
						state: ReadingState
 | 
				
			||||||
	const settingsStore = getSettingsContext();
 | 
					): FileMetadata[] {
 | 
				
			||||||
 | 
						const metadata: FileMetadata[] = [];
 | 
				
			||||||
	let metadata: FileMetadata[] = $state([]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	$effect(() => {
 | 
					 | 
				
			||||||
		const newMetadata: FileMetadata[] = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (const file of plugin.app.vault.getMarkdownFiles()) {
 | 
						for (const file of plugin.app.vault.getMarkdownFiles()) {
 | 
				
			||||||
		const frontmatter =
 | 
							const frontmatter =
 | 
				
			||||||
			plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
 | 
								plugin.app.metadataCache.getFileCache(file)?.frontmatter ?? {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (frontmatter[settingsStore.settings.statusProperty] !== state) {
 | 
							if (frontmatter[settings.statusProperty] !== state) {
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			newMetadata.push({ file, frontmatter });
 | 
							metadata.push({ file, frontmatter });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return metadata;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metadata = newMetadata;
 | 
					export function createMetadata(
 | 
				
			||||||
 | 
						plugin: BookTrackerPlugin,
 | 
				
			||||||
 | 
						statusFilter: ReadingState = STATUS_READ
 | 
				
			||||||
 | 
					): MetadataStore {
 | 
				
			||||||
 | 
						const settingsStore = getSettingsContext();
 | 
				
			||||||
 | 
						const initialMetadata = getMetadata(
 | 
				
			||||||
 | 
							plugin,
 | 
				
			||||||
 | 
							settingsStore.settings,
 | 
				
			||||||
 | 
							statusFilter
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						let metadata: FileMetadata[] = $state(initialMetadata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						$effect(() => {
 | 
				
			||||||
 | 
							metadata = getMetadata(plugin, settingsStore.settings, statusFilter);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function onChanged(file: TFile, _data: string, cache: CachedMetadata) {
 | 
						function onChanged(file: TFile, _data: string, cache: CachedMetadata) {
 | 
				
			||||||
| 
						 | 
					@ -57,7 +69,6 @@ export function createMetadata(
 | 
				
			||||||
			return f;
 | 
								return f;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	plugin.registerEvent(plugin.app.metadataCache.on("changed", onChanged));
 | 
						plugin.registerEvent(plugin.app.metadataCache.on("changed", onChanged));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function onDeleted(file: TFile) {
 | 
						function onDeleted(file: TFile) {
 | 
				
			||||||
| 
						 | 
					@ -69,13 +80,17 @@ export function createMetadata(
 | 
				
			||||||
		() => metadata,
 | 
							() => metadata,
 | 
				
			||||||
		(f) => {
 | 
							(f) => {
 | 
				
			||||||
			// @ts-expect-error Moment is provided by Obsidian
 | 
								// @ts-expect-error Moment is provided by Obsidian
 | 
				
			||||||
			return moment(f.frontmatter[settings.endDateProperty]);
 | 
								return moment(
 | 
				
			||||||
 | 
									f.frontmatter[settingsStore.settings.endDateProperty]
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return {
 | 
						return {
 | 
				
			||||||
		get metadata() {
 | 
							get metadata() {
 | 
				
			||||||
			return dateFilter.filteredData;
 | 
								return statusFilter === STATUS_READ
 | 
				
			||||||
 | 
									? dateFilter.filteredData
 | 
				
			||||||
 | 
									: metadata;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		get filterYear() {
 | 
							get filterYear() {
 | 
				
			||||||
			return dateFilter.filterYear;
 | 
								return dateFilter.filterYear;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import type BookTrackerPlugin from "@src/main";
 | 
				
			||||||
import { type BookTrackerSettings, DEFAULT_SETTINGS } from "../settings/types";
 | 
					import { type BookTrackerSettings, DEFAULT_SETTINGS } from "../settings/types";
 | 
				
			||||||
import { getContext, setContext } from "svelte";
 | 
					import { getContext, setContext } from "svelte";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface SettingsStore {
 | 
					export interface SettingsStore {
 | 
				
			||||||
	settings: BookTrackerSettings;
 | 
						settings: BookTrackerSettings;
 | 
				
			||||||
	load(): Promise<void>;
 | 
						load(): Promise<void>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,10 @@
 | 
				
			||||||
 | 
					export function randomFloat(min?: number, max?: number) {
 | 
				
			||||||
 | 
						const minVal = min === undefined && max === undefined ? 0 : min ?? 0;
 | 
				
			||||||
 | 
						const maxVal = max === undefined ? (min === undefined ? 1 : min) : max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Math.random() * (maxVal - minVal) + minVal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function randomInt(min: number, max: number) {
 | 
					export function randomInt(min: number, max: number) {
 | 
				
			||||||
	return Math.floor(Math.random() * (max - min + 1)) + min;
 | 
						return Math.floor(Math.random() * (max - min + 1)) + min;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue