mirror of
https://github.com/decaf-dev/obsidian-vault-explorer.git
synced 2026-07-22 10:10:31 +00:00
Finish cover image system (#284)
* chore: bump version * refactor: remove enableScrollButtons * feat: handle render cases * feat: remove links not used in properties * test: update test vault files for cover image testing * refactor: remove unused imports * fix: don't show broken link when social media image isn't loaded * feat: add image source setting app * refactor: remove unused properties * feat: make cover image source options draggable * fix: prevent url's that don't have a social image from loading everytime the grid mounts * fix: remove extra padding * refactor: rename to social media image cache
This commit is contained in:
parent
e0f1d64e81
commit
c3051dcf51
31 changed files with 829 additions and 259 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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<string, unknown>) {
|
||||
const typedData = data as unknown as VaultExplorerPluginSettings_1_30_5;
|
||||
const newData: VaultExplorerPluginSettings = {
|
||||
const newData: VaultExplorerPluginSettings_1_32_2 = {
|
||||
...typedData,
|
||||
shouldWrapFilterGroups: typedData.filterGroupsWrapping === "wrap",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<string, unknown>) {
|
||||
const typedData = data as unknown as VaultExplorerPluginSettings_1_32_2;
|
||||
const newData: VaultExplorerPluginSettings = {
|
||||
const newData: VaultExplorerPluginSettings_1_36_3 = {
|
||||
...typedData,
|
||||
};
|
||||
|
||||
|
|
|
|||
45
src/migrations/migrate_1_37_0.ts
Normal file
45
src/migrations/migrate_1_37_0.ts
Normal file
|
|
@ -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<string, unknown>) {
|
||||
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<string, unknown>;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
<img
|
||||
class="vault-explorer-grid-card__image"
|
||||
src={imgSrc}
|
||||
style="display: {isCoverImageLoaded || isError
|
||||
? 'block'
|
||||
: 'none'};"
|
||||
style="display: {isCoverImageLoaded ? 'block' : 'none'};"
|
||||
on:load={handleImageLoad}
|
||||
on:error={handleImageError}
|
||||
/>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string>(
|
||||
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<string>(
|
||||
fileFrontmatter,
|
||||
imageProperty,
|
||||
PropertyType.TEXT
|
||||
);
|
||||
return getImageUrlFromProperty(app, filePath, propertyValue);
|
||||
};
|
||||
|
||||
const handleUrlPropertyCoverSource = (
|
||||
fileFrontmatter: FrontMatterCache | undefined,
|
||||
urlProperty: string
|
||||
) => {
|
||||
const propertyValue = loadPropertyValue<string>(
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
16
src/svelte/image-source-app/display-name.ts
Normal file
16
src/svelte/image-source-app/display-name.ts
Normal file
|
|
@ -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 "";
|
||||
}
|
||||
};
|
||||
129
src/svelte/image-source-app/index.svelte
Normal file
129
src/svelte/image-source-app/index.svelte
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<script lang="ts">
|
||||
import store from "../shared/services/store";
|
||||
import VaultExplorerPlugin from "src/main";
|
||||
import { CoverImageSource, CoverImageSourceType } from "src/types";
|
||||
import { getDisplayNameForImageSource } from "./display-name";
|
||||
import Icon from "../shared/components/icon.svelte";
|
||||
import { PluginEvent } from "src/event/types";
|
||||
import EventManager from "src/event/event-manager";
|
||||
|
||||
let plugin: VaultExplorerPlugin | null = null;
|
||||
let coverImageSources: CoverImageSource[] = [];
|
||||
|
||||
store.plugin.subscribe((p) => {
|
||||
plugin = p;
|
||||
coverImageSources = p.settings.views.grid.coverImageSources;
|
||||
});
|
||||
|
||||
function handleSourceClick(type: CoverImageSourceType) {
|
||||
const updatedSources = coverImageSources.map((source) => {
|
||||
if (source.type === type) {
|
||||
return {
|
||||
...source,
|
||||
isEnabled: !source.isEnabled,
|
||||
};
|
||||
}
|
||||
return source;
|
||||
});
|
||||
coverImageSources = updatedSources;
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
if (!plugin) return;
|
||||
|
||||
plugin.settings.views.grid.coverImageSources = coverImageSources;
|
||||
await plugin.saveSettings();
|
||||
EventManager.getInstance().emit(
|
||||
PluginEvent.COVER_IMAGE_SOURCE_SETTING_CHANGE,
|
||||
);
|
||||
}
|
||||
|
||||
function handleSourceDragOver(e: Event) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function handleSourceDragStart(e: Event, type: CoverImageSourceType) {
|
||||
(e as any).dataTransfer.setData("text", type);
|
||||
(e as any).dataTransfer.effectAllowed = "move";
|
||||
}
|
||||
|
||||
function handleSourceDrop(e: Event, id: string) {
|
||||
const dragId = (e as any).dataTransfer.getData("text");
|
||||
(e as any).dataTransfer.dropEffect = "move";
|
||||
|
||||
const draggedIndex = coverImageSources.findIndex(
|
||||
(source) => source.type === dragId,
|
||||
);
|
||||
const dragged = coverImageSources.find(
|
||||
(source) => source.type === dragId,
|
||||
);
|
||||
|
||||
const droppedIndex = coverImageSources.findIndex(
|
||||
(source) => source.type === id,
|
||||
);
|
||||
const dropped = coverImageSources.find((source) => source.type === id);
|
||||
|
||||
if (!dragged || !dropped || draggedIndex === -1 || droppedIndex === -1)
|
||||
return;
|
||||
|
||||
let newCoverImageSources = [...coverImageSources];
|
||||
newCoverImageSources[draggedIndex] = dropped;
|
||||
newCoverImageSources[droppedIndex] = dragged;
|
||||
coverImageSources = newCoverImageSources;
|
||||
}
|
||||
|
||||
$: coverImageSources, saveSettings();
|
||||
</script>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-item-info">
|
||||
<div class="setting-item-name">Image source</div>
|
||||
<div class="setting-item-description">
|
||||
<div class="vault-explorer-image-source-setting-container">
|
||||
{#each coverImageSources as source}
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
draggable="true"
|
||||
on:dragstart={(e) =>
|
||||
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);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="vault-explorer-image-source-setting-title">
|
||||
<Icon iconId={source.isEnabled ? "check" : "x"} />
|
||||
<div>
|
||||
{getDisplayNameForImageSource(source.type)}
|
||||
</div>
|
||||
</div>
|
||||
<Icon iconId="grip-horizontal" />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.vault-explorer-image-source-setting-title {
|
||||
display: flex;
|
||||
column-gap: 8px;
|
||||
}
|
||||
|
||||
.vault-explorer-image-source-setting-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 8px;
|
||||
}
|
||||
|
||||
.vault-explorer-image-source-setting-row:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -117,9 +117,6 @@
|
|||
<style>
|
||||
.vault-explorer-setting-message {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-smallest);
|
||||
padding-top: var(--size-4-1);
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.vault-explorer-setting-message--success {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
|
|||
typeof typedObj["properties"] === "function") &&
|
||||
typeof typedObj["properties"]["favorite"] === "string" &&
|
||||
typeof typedObj["properties"]["url"] === "string" &&
|
||||
typeof typedObj["properties"]["imageUrl"] === "string" &&
|
||||
typeof typedObj["properties"]["image"] === "string" &&
|
||||
typeof typedObj["properties"]["createdDate"] === "string" &&
|
||||
typeof typedObj["properties"]["modifiedDate"] === "string" &&
|
||||
typeof typedObj["properties"]["custom1"] === "string" &&
|
||||
|
|
@ -595,9 +595,17 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
|
|||
typeof typedObj["views"]["grid"] === "object" ||
|
||||
typeof typedObj["views"]["grid"] === "function") &&
|
||||
typeof typedObj["views"]["grid"]["isEnabled"] === "boolean" &&
|
||||
(typedObj["views"]["grid"]["coverImageSource"] === "frontmatter-only" ||
|
||||
typedObj["views"]["grid"]["coverImageSource"] === "frontmatter-and-body" ||
|
||||
typedObj["views"]["grid"]["coverImageSource"] === "off") &&
|
||||
Array.isArray(typedObj["views"]["grid"]["coverImageSources"]) &&
|
||||
typedObj["views"]["grid"]["coverImageSources"].every((e: any) =>
|
||||
(e !== null &&
|
||||
typeof e === "object" ||
|
||||
typeof e === "function") &&
|
||||
(e["type"] === "image-property" ||
|
||||
e["type"] === "url-property" ||
|
||||
e["type"] === "frontmatter" ||
|
||||
e["type"] === "body") &&
|
||||
typeof e["isEnabled"] === "boolean"
|
||||
) &&
|
||||
typeof typedObj["views"]["grid"]["loadSocialMediaImage"] === "boolean" &&
|
||||
(typedObj["views"]["list"] !== null &&
|
||||
typeof typedObj["views"]["list"] === "object" ||
|
||||
|
|
@ -638,7 +646,6 @@ export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplore
|
|||
typedObj["currentView"] === TExplorerView.TABLE ||
|
||||
typedObj["currentView"] === TExplorerView.RECOMMENDED ||
|
||||
typedObj["currentView"] === TExplorerView.RELATED) &&
|
||||
typeof typedObj["enableScrollButtons"] === "boolean" &&
|
||||
typeof typedObj["pageSize"] === "number" &&
|
||||
typeof typedObj["filterGroupsWidth"] === "string" &&
|
||||
typeof typedObj["shouldWrapFilterGroups"] === "boolean" &&
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ export interface VaultExplorerPluginSettings {
|
|||
properties: {
|
||||
favorite: string;
|
||||
url: string;
|
||||
imageUrl: string;
|
||||
image: string;
|
||||
createdDate: string;
|
||||
modifiedDate: string;
|
||||
custom1: string;
|
||||
|
|
@ -30,7 +30,6 @@ export interface VaultExplorerPluginSettings {
|
|||
enableClockUpdates: boolean;
|
||||
enableFileIcons: boolean;
|
||||
currentView: TExplorerView | null;
|
||||
enableScrollButtons: boolean;
|
||||
pageSize: number;
|
||||
filterGroupsWidth: string;
|
||||
shouldWrapFilterGroups: boolean;
|
||||
|
|
@ -53,14 +52,20 @@ export interface TListView extends BaseView {
|
|||
}
|
||||
|
||||
export interface TGridView extends BaseView {
|
||||
coverImageSource: CoverImageSource;
|
||||
coverImageSources: CoverImageSource[];
|
||||
loadSocialMediaImage: boolean;
|
||||
}
|
||||
|
||||
export type CoverImageSource =
|
||||
| "frontmatter-only"
|
||||
| "frontmatter-and-body"
|
||||
| "off";
|
||||
export interface CoverImageSource {
|
||||
type: CoverImageSourceType;
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
export type CoverImageSourceType =
|
||||
| "image-property"
|
||||
| "url-property"
|
||||
| "frontmatter"
|
||||
| "body";
|
||||
|
||||
export interface TDashboardView extends BaseView {}
|
||||
|
||||
|
|
|
|||
318
src/types/types-1.36.3.ts
Normal file
318
src/types/types-1.36.3.ts
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
export interface VaultExplorerPluginSettings_1_36_3 {
|
||||
properties: {
|
||||
favorite: string;
|
||||
url: string;
|
||||
imageUrl: string;
|
||||
createdDate: string;
|
||||
modifiedDate: string;
|
||||
custom1: string;
|
||||
custom2: string;
|
||||
custom3: string;
|
||||
};
|
||||
filters: {
|
||||
search: TSearchFilter;
|
||||
favorites: TFavoritesFilter;
|
||||
sort: TSortFilter;
|
||||
timestamp: TTimestampFilter;
|
||||
custom: TCustomFilter;
|
||||
};
|
||||
views: {
|
||||
dashboard: TDashboardView;
|
||||
grid: TGridView;
|
||||
list: TListView;
|
||||
table: TTableView;
|
||||
feed: TFeedView;
|
||||
recommended: TRecommendedView;
|
||||
related: TRelatedView;
|
||||
};
|
||||
titleWrapping: WordBreak;
|
||||
enableClockUpdates: boolean;
|
||||
enableFileIcons: boolean;
|
||||
currentView: TExplorerView | null;
|
||||
enableScrollButtons: boolean;
|
||||
pageSize: number;
|
||||
filterGroupsWidth: string;
|
||||
shouldWrapFilterGroups: boolean;
|
||||
viewOrder: TExplorerView[];
|
||||
configDir: string;
|
||||
pluginVersion: string | null;
|
||||
logLevel: string;
|
||||
}
|
||||
|
||||
type FileInteractionStyle = "title" | "content";
|
||||
|
||||
interface BaseView {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
interface TTableView extends BaseView {}
|
||||
|
||||
interface TListView extends BaseView {
|
||||
showTags: boolean;
|
||||
}
|
||||
|
||||
interface TGridView extends BaseView {
|
||||
coverImageSource: CoverImageSource;
|
||||
loadSocialMediaImage: boolean;
|
||||
}
|
||||
|
||||
type CoverImageSource = "frontmatter-only" | "frontmatter-and-body" | "off";
|
||||
|
||||
interface TDashboardView extends BaseView {}
|
||||
|
||||
type CollapseStyle = "no-new-lines" | "no-extra-new-lines";
|
||||
|
||||
interface TFeedView extends BaseView {
|
||||
collapseStyle: CollapseStyle;
|
||||
removeH1: boolean;
|
||||
lineClampSmall: number;
|
||||
lineClampMedium: number;
|
||||
lineClampLarge: number;
|
||||
}
|
||||
|
||||
interface TRecommendedView extends BaseView {}
|
||||
|
||||
interface TRelatedView extends BaseView {}
|
||||
|
||||
interface BaseFilter {
|
||||
isEnabled: boolean;
|
||||
}
|
||||
|
||||
interface TSearchFilter extends BaseFilter {
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface TFavoritesFilter extends BaseFilter {
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
interface TSortFilter extends BaseFilter {
|
||||
value: SortFilterOption;
|
||||
}
|
||||
|
||||
interface TTimestampFilter extends BaseFilter {
|
||||
value: TimestampFilterOption;
|
||||
}
|
||||
|
||||
interface TCustomFilter extends BaseFilter {
|
||||
selectedGroupId: string;
|
||||
groups: TFilterGroup[];
|
||||
}
|
||||
|
||||
type TimestampFilterOption =
|
||||
| "created-today"
|
||||
| "modified-today"
|
||||
| "created-this-week"
|
||||
| "modified-this-week"
|
||||
| "created-2-weeks"
|
||||
| "modified-2-weeks"
|
||||
| "all";
|
||||
|
||||
type SortFilterOption =
|
||||
| "file-name-asc"
|
||||
| "file-name-desc"
|
||||
| "modified-asc"
|
||||
| "modified-desc"
|
||||
| "random";
|
||||
|
||||
type WordBreak = "normal" | "break-word";
|
||||
|
||||
enum TExplorerView {
|
||||
DASHBOARD = "dashboard",
|
||||
GRID = "grid",
|
||||
LIST = "list",
|
||||
FEED = "feed",
|
||||
TABLE = "table",
|
||||
RECOMMENDED = "recommended",
|
||||
RELATED = "related",
|
||||
}
|
||||
|
||||
type FilterOperator = "and" | "or";
|
||||
|
||||
enum TextFilterCondition {
|
||||
IS = "is",
|
||||
IS_NOT = "is-not",
|
||||
CONTAINS = "contains",
|
||||
DOES_NOT_CONTAIN = "does-not-contain",
|
||||
STARTS_WITH = "starts-with",
|
||||
ENDS_WITH = "ends-with",
|
||||
EXISTS = "exists",
|
||||
DOES_NOT_EXIST = "does-not-exist",
|
||||
}
|
||||
|
||||
enum ListFilterCondition {
|
||||
CONTAINS = "contains",
|
||||
DOES_NOT_CONTAIN = "does-not-contain",
|
||||
EXISTS = "exists",
|
||||
DOES_NOT_EXIST = "does-not-exist",
|
||||
}
|
||||
|
||||
enum NumberFilterCondition {
|
||||
IS_EQUAL = "is-equal",
|
||||
IS_NOT_EQUAL = "is-not-equal",
|
||||
IS_GREATER = "is-greater",
|
||||
IS_LESS = "is-less",
|
||||
IS_GREATER_OR_EQUAL = "is-greater-or-equal",
|
||||
IS_LESS_OR_EQUAL = "is-less-or-equal",
|
||||
EXISTS = "exists",
|
||||
DOES_NOT_EXIST = "does-not-exist",
|
||||
}
|
||||
|
||||
enum CheckboxFilterCondition {
|
||||
IS = "is",
|
||||
IS_NOT = "is-not",
|
||||
EXISTS = "exists",
|
||||
DOES_NOT_EXIST = "does-not-exist",
|
||||
}
|
||||
|
||||
enum DateFilterCondition {
|
||||
IS = "is",
|
||||
IS_BEFORE = "is-before",
|
||||
IS_AFTER = "is-after",
|
||||
IS_ON_OR_BEFORE = "is-on-or-before",
|
||||
IS_ON_OR_AFTER = "is-on-or-after",
|
||||
EXISTS = "exists",
|
||||
DOES_NOT_EXIST = "does-not-exist",
|
||||
}
|
||||
|
||||
enum ContentFilterCondition {
|
||||
CONTAINS = "contains",
|
||||
DOES_NOT_CONTAIN = "does-not-contain",
|
||||
IS_EMPTY = "is-empty",
|
||||
IS_NOT_EMPTY = "is-not-empty",
|
||||
}
|
||||
|
||||
enum FolderFilterCondition {
|
||||
IS = "is",
|
||||
IS_NOT = "is-not",
|
||||
}
|
||||
|
||||
enum FileNameFilterCondition {
|
||||
IS = "is",
|
||||
IS_NOT = "is-not",
|
||||
CONTAINS = "contains",
|
||||
DOES_NOT_CONTAIN = "does-not-contain",
|
||||
STARTS_WITH = "starts-with",
|
||||
ENDS_WITH = "ends-with",
|
||||
}
|
||||
|
||||
type FilterCondition =
|
||||
| TextFilterCondition
|
||||
| NumberFilterCondition
|
||||
| DateFilterCondition
|
||||
| CheckboxFilterCondition
|
||||
| ListFilterCondition
|
||||
| ContentFilterCondition
|
||||
| FolderFilterCondition
|
||||
| FileNameFilterCondition;
|
||||
|
||||
//This matches the Obsidian property types
|
||||
enum PropertyType {
|
||||
TEXT = "text",
|
||||
NUMBER = "number",
|
||||
LIST = "list",
|
||||
CHECKBOX = "checkbox",
|
||||
DATE = "date",
|
||||
DATETIME = "datetime",
|
||||
}
|
||||
|
||||
enum FilterRuleType {
|
||||
PROPERTY = "property",
|
||||
FOLDER = "folder",
|
||||
FILE_NAME = "file-name",
|
||||
CONTENT = "content",
|
||||
}
|
||||
|
||||
enum DatePropertyFilterValue {
|
||||
TODAY = "today",
|
||||
TOMORROW = "tomorrow",
|
||||
YESTERDAY = "yesterday",
|
||||
ONE_WEEK_FROM_NOW = "one-week-from-now",
|
||||
ONE_WEEK_AGO = "one-week-ago",
|
||||
ONE_MONTH_FROM_NOW = "one-month-from-now",
|
||||
ONE_MONTH_AGO = "one-month-ago",
|
||||
CUSTOM = "custom",
|
||||
}
|
||||
|
||||
interface BaseFilterRule {
|
||||
id: string;
|
||||
operator: FilterOperator;
|
||||
type: FilterRuleType;
|
||||
condition: FilterCondition;
|
||||
isEnabled: boolean;
|
||||
value: string;
|
||||
matchWhenPropertyDNE: boolean;
|
||||
}
|
||||
|
||||
interface TextPropertyFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.PROPERTY;
|
||||
propertyType: PropertyType.TEXT;
|
||||
propertyName: string;
|
||||
condition: TextFilterCondition;
|
||||
}
|
||||
|
||||
interface NumberPropertyFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.PROPERTY;
|
||||
propertyType: PropertyType.NUMBER;
|
||||
propertyName: string;
|
||||
condition: NumberFilterCondition;
|
||||
}
|
||||
|
||||
interface ListPropertyFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.PROPERTY;
|
||||
propertyType: PropertyType.LIST;
|
||||
propertyName: string;
|
||||
condition: ListFilterCondition;
|
||||
}
|
||||
|
||||
interface CheckboxPropertyFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.PROPERTY;
|
||||
propertyType: PropertyType.CHECKBOX;
|
||||
propertyName: string;
|
||||
condition: CheckboxFilterCondition;
|
||||
}
|
||||
|
||||
interface DatePropertyFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.PROPERTY;
|
||||
propertyType: PropertyType.DATE | PropertyType.DATETIME;
|
||||
propertyName: string;
|
||||
condition: DateFilterCondition;
|
||||
valueData: string;
|
||||
}
|
||||
|
||||
interface FolderFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.FOLDER;
|
||||
condition: FolderFilterCondition;
|
||||
includeSubfolders: boolean;
|
||||
}
|
||||
|
||||
interface FileNameFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.FILE_NAME;
|
||||
condition: FileNameFilterCondition;
|
||||
}
|
||||
|
||||
interface ContentFilterRule extends BaseFilterRule {
|
||||
type: FilterRuleType.CONTENT;
|
||||
condition: ContentFilterCondition;
|
||||
}
|
||||
|
||||
type TFilterRule =
|
||||
| PropertyFilterRule
|
||||
| FolderFilterRule
|
||||
| FileNameFilterRule
|
||||
| ContentFilterRule;
|
||||
|
||||
type PropertyFilterRule =
|
||||
| TextPropertyFilterRule
|
||||
| NumberPropertyFilterRule
|
||||
| ListPropertyFilterRule
|
||||
| CheckboxPropertyFilterRule
|
||||
| DatePropertyFilterRule;
|
||||
|
||||
interface TFilterGroup {
|
||||
id: string;
|
||||
name: string;
|
||||
rules: TFilterRule[];
|
||||
isEnabled: boolean;
|
||||
isSticky: boolean;
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"creationDate": "creation",
|
||||
"modifiedDate": "modification",
|
||||
"createdDate": "creation",
|
||||
"imageUrl": "image"
|
||||
"image": "image"
|
||||
},
|
||||
"filters": {
|
||||
"search": {
|
||||
|
|
@ -51,7 +51,24 @@
|
|||
"grid": {
|
||||
"isEnabled": true,
|
||||
"loadSocialMediaImage": true,
|
||||
"coverImageSource": "frontmatter-and-body"
|
||||
"coverImageSources": [
|
||||
{
|
||||
"type": "image-property",
|
||||
"isEnabled": true
|
||||
},
|
||||
{
|
||||
"type": "url-property",
|
||||
"isEnabled": true
|
||||
},
|
||||
{
|
||||
"type": "frontmatter",
|
||||
"isEnabled": true
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"isEnabled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"isEnabled": true,
|
||||
|
|
@ -82,7 +99,6 @@
|
|||
"titleWrapping": "normal",
|
||||
"enableClockUpdates": true,
|
||||
"enableFileIcons": true,
|
||||
"enableScrollButtons": true,
|
||||
"filterGroupsWidth": "759px",
|
||||
"shouldWrapFilterGroups": false,
|
||||
"pageSize": 25,
|
||||
|
|
@ -92,6 +108,6 @@
|
|||
"feed"
|
||||
],
|
||||
"configDir": ".vaultexplorer",
|
||||
"pluginVersion": "1.36.2",
|
||||
"pluginVersion": "1.37.0",
|
||||
"logLevel": "trace"
|
||||
}
|
||||
|
|
@ -1,2 +1 @@
|
|||

|
||||
|
||||

|
||||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
banner: "[[pexels-tomfisk-1653823.jpg]]"
|
||||
image:
|
||||
---
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
banner: "[[pexels-tomfisk-1653823.jpg]]"
|
||||
url: https://vaultexplorer.com
|
||||
---
|
||||
3
test-vault/Frontmatter with embed image.md
Normal file
3
test-vault/Frontmatter with embed image.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
image: "![[pexels-tomfisk-1653823.jpg]]"
|
||||
---
|
||||
3
test-vault/Frontmatter with image (url).md
Normal file
3
test-vault/Frontmatter with image (url).md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
image: https://vaultexplorer.com
|
||||
---
|
||||
3
test-vault/Frontmatter with image (wiki link).md
Normal file
3
test-vault/Frontmatter with image (wiki link).md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
image: "[[pexels-tomfisk-1653823.jpg]]"
|
||||
---
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
image: "[[pexels-kelly-1179532-23732393.jpg]]"
|
||||
favorite: false
|
||||
---
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
url: https://vaultexplorer.com
|
||||
banner: "[[pexels-tomfisk-1653823.jpg]]"
|
||||
---
|
||||
3
test-vault/Frontmatter with url.md
Normal file
3
test-vault/Frontmatter with url.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
url: https://vaultexplorer.com
|
||||
---
|
||||
|
|
@ -126,5 +126,6 @@
|
|||
"1.36.0": "1.4.13",
|
||||
"1.36.1": "1.4.13",
|
||||
"1.36.2": "1.4.13",
|
||||
"1.36.3": "1.4.13"
|
||||
"1.36.3": "1.4.13",
|
||||
"1.37.0": "1.4.13"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue