diff --git a/manifest.json b/manifest.json index b2ec452..1ed4cb0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-explorer", "name": "Vault Explorer", - "version": "1.36.3", + "version": "1.37.0", "minAppVersion": "1.4.13", "description": "Explore your vault in visual format", "author": "DecafDev", diff --git a/package.json b/package.json index 9d88622..6353ec5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-vault-explorer", - "version": "1.36.3", + "version": "1.37.0", "description": "Explore your vault in visual format", "main": "main.js", "scripts": { diff --git a/src/constants.ts b/src/constants.ts index e56f2bb..c2d0fcc 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,7 +9,7 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = { properties: { favorite: "", url: "", - imageUrl: "", + image: "", createdDate: "", modifiedDate: "", custom1: "", @@ -45,8 +45,25 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = { }, grid: { isEnabled: true, + coverImageSources: [ + { + type: "image-property", + isEnabled: true, + }, + { + type: "url-property", + isEnabled: true, + }, + { + type: "frontmatter", + isEnabled: true, + }, + { + type: "body", + isEnabled: true, + }, + ], loadSocialMediaImage: true, - coverImageSource: "frontmatter-and-body", }, list: { isEnabled: true, @@ -74,7 +91,6 @@ export const DEFAULT_SETTINGS: VaultExplorerPluginSettings = { titleWrapping: "normal", enableClockUpdates: true, enableFileIcons: false, - enableScrollButtons: true, filterGroupsWidth: "300px", shouldWrapFilterGroups: false, pageSize: 25, diff --git a/src/migrations/index.ts b/src/migrations/index.ts index 1bbee7b..dcb08fb 100644 --- a/src/migrations/index.ts +++ b/src/migrations/index.ts @@ -26,6 +26,7 @@ import Migrate_1_29_0 from "./migrate_1_29_0"; import Migrate_1_30_0 from "./migrate_1_30_5"; import Migrate_1_31_0 from "./migrate_1_31_0"; import Migrate_1_33_0 from "./migrate_1_33_0"; +import Migrate_1_37_0 from "./migrate_1_37_0"; const migrations: TMigration[] = [ { @@ -153,6 +154,11 @@ const migrations: TMigration[] = [ to: "1.33.0", migrate: Migrate_1_33_0, }, + { + from: "1.36.3", + to: "1.37.0", + migrate: Migrate_1_37_0, + }, ]; export const preformMigrations = ( diff --git a/src/migrations/migrate_1_31_0.ts b/src/migrations/migrate_1_31_0.ts index 9a0a9aa..01e1f9f 100644 --- a/src/migrations/migrate_1_31_0.ts +++ b/src/migrations/migrate_1_31_0.ts @@ -1,11 +1,11 @@ -import { VaultExplorerPluginSettings } from "src/types"; import MigrationInterface from "./migration_interface"; import { VaultExplorerPluginSettings_1_30_5 } from "src/types/types-1.30.5"; +import { VaultExplorerPluginSettings_1_32_2 } from "src/types/types-1.32.2"; export default class Migrate_1_31_0 implements MigrationInterface { migrate(data: Record) { const typedData = data as unknown as VaultExplorerPluginSettings_1_30_5; - const newData: VaultExplorerPluginSettings = { + const newData: VaultExplorerPluginSettings_1_32_2 = { ...typedData, shouldWrapFilterGroups: typedData.filterGroupsWrapping === "wrap", }; diff --git a/src/migrations/migrate_1_33_0.ts b/src/migrations/migrate_1_33_0.ts index 5c2c38d..9d26f04 100644 --- a/src/migrations/migrate_1_33_0.ts +++ b/src/migrations/migrate_1_33_0.ts @@ -1,11 +1,11 @@ -import { VaultExplorerPluginSettings } from "src/types"; import MigrationInterface from "./migration_interface"; import { VaultExplorerPluginSettings_1_32_2 } from "src/types/types-1.32.2"; +import { VaultExplorerPluginSettings_1_36_3 } from "src/types/types-1.36.3"; export default class Migrate_1_33_0 implements MigrationInterface { migrate(data: Record) { const typedData = data as unknown as VaultExplorerPluginSettings_1_32_2; - const newData: VaultExplorerPluginSettings = { + const newData: VaultExplorerPluginSettings_1_36_3 = { ...typedData, }; diff --git a/src/migrations/migrate_1_37_0.ts b/src/migrations/migrate_1_37_0.ts new file mode 100644 index 0000000..a576aad --- /dev/null +++ b/src/migrations/migrate_1_37_0.ts @@ -0,0 +1,45 @@ +import { VaultExplorerPluginSettings } from "src/types"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_36_3 } from "src/types/types-1.36.3"; + +export default class Migrate_1_37_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = data as unknown as VaultExplorerPluginSettings_1_36_3; + const newData: VaultExplorerPluginSettings = { + ...typedData, + views: { + ...typedData.views, + grid: { + ...typedData.views.grid, + coverImageSources: [ + { + type: "image-property", + isEnabled: true, + }, + { + type: "url-property", + isEnabled: true, + }, + { + type: "frontmatter", + isEnabled: true, + }, + { + type: "body", + isEnabled: true, + }, + ], + }, + }, + properties: { + ...typedData.properties, + image: typedData.properties.imageUrl, + }, + }; + + delete (newData as any).enableScrollButtons; + delete (newData as any).views.grid.coverImageSource; + delete (newData as any).properties.imageUrl; + return newData as unknown as Record; + } +} diff --git a/src/obsidian/vault-explorer-settings-tab.ts b/src/obsidian/vault-explorer-settings-tab.ts index 8eebab4..d635984 100644 --- a/src/obsidian/vault-explorer-settings-tab.ts +++ b/src/obsidian/vault-explorer-settings-tab.ts @@ -14,9 +14,10 @@ import { } from "src/logger/constants"; import Logger from "js-logger"; import { stringToLogLevel } from "src/logger"; -import { CollapseStyle, CoverImageSource, TExplorerView } from "src/types"; +import { CollapseStyle, TExplorerView } from "src/types"; import EventManager from "src/event/event-manager"; import LicenseKeyApp from "../svelte/license-key-app/index.svelte"; +import ImageSourceApp from "../svelte/image-source-app/index.svelte"; import { PluginEvent } from "src/event/types"; import "./styles.css"; @@ -24,12 +25,15 @@ import { clearSocialMediaImageCache } from "src/svelte/app/services/social-media export default class VaultExplorerSettingsTab extends PluginSettingTab { plugin: VaultExplorerPlugin; - component: LicenseKeyApp | null; + + licenseKeyApp: LicenseKeyApp | null; + imageSourceApp: ImageSourceApp | null; constructor(app: App, plugin: VaultExplorerPlugin) { super(app, plugin); this.plugin = plugin; - this.component = null; + this.licenseKeyApp = null; + this.imageSourceApp = null; } display(): void { @@ -97,22 +101,6 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { }) ); - // TODO remove? - // new Setting(containerEl) - // .setName("Scroll buttons") - // .setDesc("Display scroll buttons for scrollable content.") - // .addToggle((toggle) => - // toggle - // .setValue(this.plugin.settings.enableScrollButtons) - // .onChange(async (value) => { - // this.plugin.settings.enableScrollButtons = value; - // await this.plugin.saveSettings(); - // EventManager.getInstance().emit( - // "scroll-buttons-setting-change" - // ); - // }) - // ); - new Setting(containerEl) .setName("Page size") .setDesc("Number of items to display per page.") @@ -309,44 +297,9 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { new Setting(containerEl).setName("Grid view").setHeading(); - new Setting(containerEl) - .setName("Automatic cover image detection") - .setDesc("Set where cover images are automatically loaded from.") - .addDropdown((cb) => - cb - .addOptions({ - "frontmatter-and-body": "Frontmatter and body", - "frontmatter-only": "Frontmatter only", - off: "Off", - }) - .setValue(this.plugin.settings.views.grid.coverImageSource) - .onChange(async (value) => { - this.plugin.settings.views.grid.coverImageSource = - value as CoverImageSource; - await this.plugin.saveSettings(); - EventManager.getInstance().emit( - PluginEvent.COVER_IMAGE_SOURCE_SETTING_CHANGE - ); - }) - ); - - new Setting(containerEl) - .setName("Preferred cover image property") - .setDesc( - "If set, the value of the selected property will be preferred for the cover image" - ) - .addDropdown((dropdown) => - dropdown - .addOptions(getDropdownOptionsForProperties(textProperties)) - .setValue(this.plugin.settings.properties.imageUrl) - .onChange(async (value) => { - this.plugin.settings.properties.imageUrl = value; - await this.plugin.saveSettings(); - EventManager.getInstance().emit( - PluginEvent.PROPERTY_SETTING_CHANGE - ); - }) - ); + this.imageSourceApp = new ImageSourceApp({ + target: containerEl, + }); new Setting(containerEl) .setName("Load social media image") @@ -498,6 +451,24 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { }) ); + new Setting(containerEl) + .setName("Image property") + .setDesc( + "Property used to store an image. This must be a text property." + ) + .addDropdown((dropdown) => + dropdown + .addOptions(getDropdownOptionsForProperties(textProperties)) + .setValue(this.plugin.settings.properties.image) + .onChange(async (value) => { + this.plugin.settings.properties.image = value; + await this.plugin.saveSettings(); + EventManager.getInstance().emit( + PluginEvent.PROPERTY_SETTING_CHANGE + ); + }) + ); + new Setting(containerEl) .setName("URL property") .setDesc( @@ -645,7 +616,7 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { new Setting(containerEl).setName("Premium").setHeading(); - this.component = new LicenseKeyApp({ + this.licenseKeyApp = new LicenseKeyApp({ target: containerEl, }); @@ -697,7 +668,7 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { ); new Setting(containerEl) - .setName("Clear cache data") + .setName("Social media image cache") .addButton((button) => button .setClass("mod-destructive") @@ -709,7 +680,8 @@ export default class VaultExplorerSettingsTab extends PluginSettingTab { } onClose() { - this.component?.$destroy(); + this.licenseKeyApp?.$destroy(); + this.imageSourceApp?.$destroy(); } private updateViewOrder(view: TExplorerView, value: boolean) { diff --git a/src/svelte/app/components/grid-card.svelte b/src/svelte/app/components/grid-card.svelte index df6e35b..89c5e42 100644 --- a/src/svelte/app/components/grid-card.svelte +++ b/src/svelte/app/components/grid-card.svelte @@ -5,7 +5,7 @@ import store from "../../shared/services/store"; import Wrap from "src/svelte/shared/components/wrap.svelte"; import Stack from "src/svelte/shared/components/stack.svelte"; - import { createEventDispatcher, onMount } from "svelte"; + import { afterUpdate, createEventDispatcher, onMount } from "svelte"; import { WordBreak } from "src/types"; import { HOVER_LINK_SOURCE_ID } from "src/constants"; import EventManager from "src/event/event-manager"; @@ -57,24 +57,17 @@ let renderKey = 0; onMount(() => { - async function updateImgSrc() { - if (imageUrl !== null) { - const entry = await getSocialMediaImageEntry(imageUrl); - if (entry) { - const isExpired = - await isSocialMediaImageEntryExpired(entry); - if (!isExpired) { - imgSrc = entry.socialMediaImageUrl; - return; - } - } - imgSrc = imageUrl; - } - } - updateImgSrc(); }); + afterUpdate(() => { + if (imageUrl === null) { + imgSrc = null; + } else { + updateImgSrc(); + } + }); + onMount(() => { function handleLoadSocialMediaImageChange() { const newValue = plugin.settings.views.grid.loadSocialMediaImage; @@ -128,6 +121,30 @@ }; }); + async function updateImgSrc() { + if (imageUrl !== null) { + const entry = await getSocialMediaImageEntry(imageUrl); + if (entry) { + const isExpired = await isSocialMediaImageEntryExpired(entry); + if (!isExpired) { + const socialUrl = entry.socialMediaImageUrl; + + //The url is null when the social media image does not exist + //This will always happen for sites like Twitter (x.com) + //To avoid fetching the same non-existent image, we will set imgSrc to null + if (socialUrl === null) { + imgSrc = null; + } else { + imgSrc = socialUrl; + } + return; + } + } + imgSrc = imageUrl; + } + console.log("Image", imageUrl, imgSrc); + } + function handleUrlClick(e: Event) { e.stopPropagation(); } @@ -181,15 +198,12 @@ if (socialUrl) { putSocialMediaImageUrl(imgSrc, socialUrl); target.src = socialUrl; - return; } else { - isError = true; + putSocialMediaImageUrl(imgSrc, null); } } } - let isError = false; - function handleImageLoad() { isCoverImageLoaded = true; } @@ -219,9 +233,7 @@ @@ -336,11 +348,6 @@ border-radius: var(--radius-m); } - /* - .vault-explorer-grid-card:hover { - background-color: var(--background-modifier-hover); - } */ - .vault-explorer-grid-card:focus-visible { box-shadow: 0 0 0 3px var(--background-modifier-border-focus); } diff --git a/src/svelte/app/index.svelte b/src/svelte/app/index.svelte index 1d87300..2afc7d0 100644 --- a/src/svelte/app/index.svelte +++ b/src/svelte/app/index.svelte @@ -10,18 +10,11 @@ import { Notice, TFile } from "obsidian"; import { TCustomFilter, - TDashboardView, TFavoritesFilter, TSearchFilter, TSortFilter, TTimestampFilter, TExplorerView, - TListView, - TGridView, - TRecommendedView, - TRelatedView, - TTableView, - TFeedView, } from "src/types"; import store from "../shared/services/store"; import VaultExplorerPlugin from "src/main"; @@ -104,6 +97,7 @@ let frontmatterCacheTime: number = Date.now(); let propertySettingsTime: number = Date.now(); + let coverImageSourcesTime: number = Date.now(); let loadedFiles: LoadedFile[] = []; let timeValuesUpdateInterval: NodeJS.Timer | null = null; @@ -112,43 +106,6 @@ let contentCache: FileContentCache = new Map(); let randomSortCache: RandomFileSortCache = new Map(); - let dashboardView: TDashboardView = { - isEnabled: false, - }; - - let listView: TListView = { - isEnabled: false, - showTags: true, - }; - - let gridView: TGridView = { - loadSocialMediaImage: false, - isEnabled: false, - coverImageSource: "frontmatter-and-body", - }; - - //TODO use just isEnabled - let feedView: TFeedView = { - isEnabled: false, - removeH1: true, - lineClampSmall: 2, - lineClampMedium: 3, - lineClampLarge: 5, - collapseStyle: "no-new-lines", - }; - - let tableView: TTableView = { - isEnabled: false, - }; - - let recommendedView: TRecommendedView = { - isEnabled: false, - }; - - let relatedView: TRelatedView = { - isEnabled: false, - }; - let viewOrder: TExplorerView[] = []; // ============================================ @@ -181,13 +138,6 @@ timestampFilter = settings.filters.timestamp; currentView = settings.currentView; customFilter = settings.filters.custom; - dashboardView = settings.views.dashboard; - listView = settings.views.list; - gridView = settings.views.grid; - feedView = settings.views.feed; - tableView = settings.views.table; - recommendedView = settings.views.recommended; - relatedView = settings.views.related; viewOrder = settings.viewOrder; if (settings.enableClockUpdates) { @@ -501,8 +451,7 @@ message: "called", }); - //TODO update? - updateFrontmatterCacheTime(); + coverImageSourcesTime = Date.now(); } EventManager.getInstance().on( @@ -567,13 +516,6 @@ plugin.settings.filters.sort = sortFilter; plugin.settings.filters.timestamp = timestampFilter; plugin.settings.filters.favorites = favoritesFilter; - plugin.settings.views.dashboard = dashboardView; - plugin.settings.views.list = listView; - plugin.settings.views.grid = gridView; - plugin.settings.views.feed = feedView; - plugin.settings.views.table = tableView; - plugin.settings.views.recommended = recommendedView; - plugin.settings.views.related = relatedView; plugin.settings.currentView = currentView; plugin.settings.filters.custom = customFilter; plugin.settings.viewOrder = viewOrder; @@ -799,7 +741,7 @@ } let formatted: FileRenderData[] = []; - $: if (propertySettingsTime) { + $: if (propertySettingsTime || coverImageSourcesTime) { formatted = filteredCustom.map((loadedFile) => { const { id, file } = loadedFile; const frontmatter = @@ -882,13 +824,6 @@ favoritesFilter, currentView, customFilter, - dashboardView, - listView, - gridView, - feedView, - tableView, - recommendedView, - relatedView, viewOrder, saveSettings(); diff --git a/src/svelte/app/services/render-data.ts b/src/svelte/app/services/render-data.ts index 9daf0aa..301e8e1 100644 --- a/src/svelte/app/services/render-data.ts +++ b/src/svelte/app/services/render-data.ts @@ -14,17 +14,16 @@ import { import { isImageExtension } from "./utils/image-utils"; import { removeFrontmatter } from "./utils/content-utils"; import { getURIForWikiLinkTarget } from "./utils/wiki-link-utils"; -import { - isExternalEmbed, - isInternalEmbed, - isUrl, - isWikiLink, -} from "./link-utils/link-validators"; +import { isUrl, isWikiLink } from "./link-utils/link-validators"; import { getExternalEmbedTarget, getInternalEmbedTarget, getWikiLinkTarget, } from "./link-utils/link-target-getters"; +import { + getFirstExternalEmbed, + getFirstInternalEmbed, +} from "./link-utils/link-getters"; /** * Formats the file's data for rendering @@ -56,12 +55,12 @@ export const formatFileDataForRender = ({ }): FileRenderData => { const { name, basename, extension, path } = file; - const { coverImageSource } = settings.views.grid; + const { coverImageSources } = settings.views.grid; const { createdDate: createdDateProp, modifiedDate: modifiedDateProp, url: urlProp, - imageUrl: imageUrlProp, + image: imageProp, favorite: favoriteProp, custom1: custom1Prop, custom2: custom2Prop, @@ -149,58 +148,62 @@ export const formatFileDataForRender = ({ imageUrl = app.vault.getResourcePath(file); } - //Handle image property - if (imageUrl === null && coverImageSource !== "off") { - const loadedUrl = loadPropertyValue( - fileFrontmatter, - imageUrlProp, - PropertyType.TEXT - ); - - if (loadedUrl !== null) { - if (isInternalEmbed(loadedUrl)) { - const target = getInternalEmbedTarget(loadedUrl); - if (target) { - const uri = getURIForWikiLinkTarget(app, target, path); - if (uri) { - imageUrl = uri; + for (const imageSource of coverImageSources) { + const { type, isEnabled } = imageSource; + if (isEnabled) { + switch (type) { + case "image-property": { + const loadedUrl = handleImagePropertyCoverSource( + app, + fileFrontmatter, + path, + imageProp + ); + if (loadedUrl !== null) { + imageUrl = loadedUrl; } + break; } - } else if (isWikiLink(loadedUrl)) { - const target = getWikiLinkTarget(loadedUrl); - if (target) { - const uri = getURIForWikiLinkTarget(app, target, path); - if (uri) { - imageUrl = uri; + case "url-property": { + const loadedUrl = handleUrlPropertyCoverSource( + fileFrontmatter, + urlProp + ); + if (loadedUrl !== null) { + imageUrl = loadedUrl; } + break; } - } else if (isExternalEmbed(loadedUrl)) { - const target = getExternalEmbedTarget(loadedUrl); - if (target) { - imageUrl = target; + case "frontmatter": { + const loadedUrl = handleFrontmatterCoverSource( + app, + fileFrontmatter, + path + ); + if (loadedUrl !== null) { + imageUrl = loadedUrl; + } + break; } - } else if (isUrl(loadedUrl)) { - imageUrl = loadedUrl; + case "body": { + const loadedUrl = handleBodyCoverSource( + app, + path, + fileContent + ); + if (loadedUrl !== null) { + imageUrl = loadedUrl; + } + break; + } + default: + throw new Error( + `Unhandled cover image source type: ${type}` + ); + } + if (imageUrl !== null) { + break; // Exit the loop if a non-null URL is found } - } - } - - //Handle image in frontmatter - if (imageUrl === null && coverImageSource !== "off") { - const textProperties: FileTextProperties = loadTextProperties( - app, - fileFrontmatter - ); - - for (const property of textProperties) { - //TODO implement - } - } - - if (imageUrl === null && coverImageSource === "frontmatter-and-body") { - if (fileContent !== null) { - const body = removeFrontmatter(fileContent); - //TODO implement } } @@ -224,3 +227,105 @@ export const formatFileDataForRender = ({ custom3, }; }; + +const handleImagePropertyCoverSource = ( + app: App, + fileFrontmatter: FrontMatterCache | undefined, + filePath: string, + imageProperty: string +) => { + const propertyValue = loadPropertyValue( + fileFrontmatter, + imageProperty, + PropertyType.TEXT + ); + return getImageUrlFromProperty(app, filePath, propertyValue); +}; + +const handleUrlPropertyCoverSource = ( + fileFrontmatter: FrontMatterCache | undefined, + urlProperty: string +) => { + const propertyValue = loadPropertyValue( + fileFrontmatter, + urlProperty, + PropertyType.TEXT + ); + if (propertyValue !== null) { + if (isUrl(propertyValue)) { + return propertyValue; + } + } + return null; +}; + +const handleFrontmatterCoverSource = ( + app: App, + fileFrontmatter: FrontMatterCache | undefined, + filePath: string +) => { + const textProperties: FileTextProperties = loadTextProperties( + app, + fileFrontmatter + ); + + for (const property of textProperties) { + const { value } = property; + const imageUrl = getImageUrlFromProperty(app, filePath, value); + if (imageUrl) { + return imageUrl; + } + } + return null; +}; + +const handleBodyCoverSource = ( + app: App, + filePath: string, + fileContent: string | null +) => { + if (fileContent === null) return null; + + const body = removeFrontmatter(fileContent); + const firstInternalLink = getFirstInternalEmbed(body); + if (firstInternalLink) { + const target = getInternalEmbedTarget(firstInternalLink); + if (target) { + const uri = getURIForWikiLinkTarget(app, target, filePath); + if (uri) { + return uri; + } + } + } + + const firstExternalLink = getFirstExternalEmbed(body); + if (firstExternalLink) { + const target = getExternalEmbedTarget(firstExternalLink); + if (target) { + return target; + } + } + + return null; +}; + +const getImageUrlFromProperty = ( + app: App, + filePath: string, + propertyValue: string | null +) => { + if (propertyValue === null) return null; + + if (isWikiLink(propertyValue)) { + const target = getWikiLinkTarget(propertyValue); + if (target) { + const uri = getURIForWikiLinkTarget(app, target, filePath); + if (uri) { + return uri; + } + } + } else if (isUrl(propertyValue)) { + return propertyValue; + } + return null; +}; diff --git a/src/svelte/app/services/social-media-image-cache.ts b/src/svelte/app/services/social-media-image-cache.ts index 8a769f6..c122694 100644 --- a/src/svelte/app/services/social-media-image-cache.ts +++ b/src/svelte/app/services/social-media-image-cache.ts @@ -9,7 +9,7 @@ const ENTRY_EXPIRATION_TIME = ONE_WEEK_MILLIS; interface SocialMediaImageEntry { url: string; - socialMediaImageUrl: string; + socialMediaImageUrl: string | null; // null if no social media image found timestamp: number; } @@ -42,7 +42,7 @@ export const getSocialMediaImageEntry = async (url: string) => { */ export const putSocialMediaImageUrl = async ( url: string, - socialMediaImageUrl: string + socialMediaImageUrl: string | null ) => { const db = await openDatabase(); db.put(STORE_NAME, { diff --git a/src/svelte/image-source-app/display-name.ts b/src/svelte/image-source-app/display-name.ts new file mode 100644 index 0000000..cfdaa88 --- /dev/null +++ b/src/svelte/image-source-app/display-name.ts @@ -0,0 +1,16 @@ +import { CoverImageSourceType } from "src/types"; + +export const getDisplayNameForImageSource = (type: CoverImageSourceType) => { + switch (type) { + case "image-property": + return "Image Property"; + case "url-property": + return "URL"; + case "frontmatter": + return "Frontmatter"; + case "body": + return "Body"; + default: + return ""; + } +}; diff --git a/src/svelte/image-source-app/index.svelte b/src/svelte/image-source-app/index.svelte new file mode 100644 index 0000000..34d2626 --- /dev/null +++ b/src/svelte/image-source-app/index.svelte @@ -0,0 +1,129 @@ + + +
+
+
Image source
+
+
+ {#each coverImageSources as source} +
+ handleSourceDragStart(e, source.type)} + on:dragover={handleSourceDragOver} + on:drop={(e) => handleSourceDrop(e, source.type)} + class="vault-explorer-image-source-setting-row" + on:click={() => handleSourceClick(source.type)} + on:keydown={(e) => { + if (e.key === "Enter" || e.key === " ") { + handleSourceClick(source.type); + } + }} + > +
+ +
+ {getDisplayNameForImageSource(source.type)} +
+
+ +
+ {/each} +
+
+
+
+ + diff --git a/src/svelte/license-key-app/index.svelte b/src/svelte/license-key-app/index.svelte index 31f43b7..51d1692 100644 --- a/src/svelte/license-key-app/index.svelte +++ b/src/svelte/license-key-app/index.svelte @@ -117,9 +117,6 @@