mirror of
https://github.com/mntno/obsidian-come-down.git
synced 2026-07-22 05:43:15 +00:00
1.1.1-rc.1
This commit is contained in:
parent
fa5323f538
commit
a424f3e48c
27 changed files with 2460 additions and 721 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -13,13 +13,14 @@ data.json
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Hot Reload plugin
|
|
||||||
.hotreload
|
|
||||||
|
|
||||||
# This plugin
|
# This plugin
|
||||||
|
|
||||||
## The compiled main.js file.
|
## Files are copied here on `pnpm run build`.
|
||||||
main.js
|
/dist/
|
||||||
|
|
||||||
## Created by plugin.
|
## Sym-linked to the plugin's folder in the dev/test vault. Files are copied here `pnpm run dev`.
|
||||||
|
## Git stores symlinks as a special file containing the target path string, not as a directory. So the gitignore rule needs to match a file, not a directory pattern.
|
||||||
|
dev-vault
|
||||||
|
|
||||||
|
main.js
|
||||||
cache.json
|
cache.json
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,6 @@
|
||||||
//
|
//
|
||||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||||
{}
|
{
|
||||||
|
"project_name": "Come Down"
|
||||||
|
}
|
||||||
|
|
|
||||||
21
README.md
21
README.md
|
|
@ -27,7 +27,7 @@ Having the cache located inside the plugin’s folder serves several purposes:
|
||||||
- It allows the cache to be automatically deleted if the plugin is uninstalled.
|
- It allows the cache to be automatically deleted if the plugin is uninstalled.
|
||||||
- A .gitignore file can be placed in the cache folder to exclude it from Git. Had it been in a visible folder, users might mistakenly think that files they add there would be committed.
|
- A .gitignore file can be placed in the cache folder to exclude it from Git. Had it been in a visible folder, users might mistakenly think that files they add there would be committed.
|
||||||
|
|
||||||
### Embedded Images Are Already Cached
|
### Embedded Images are Already Cached
|
||||||
|
|
||||||
Obsidian is built on Electron, which embeds the Chromium web browser. Therefore, it has built-in caching just as any web browser. But this cache exists outside your vault.
|
Obsidian is built on Electron, which embeds the Chromium web browser. Therefore, it has built-in caching just as any web browser. But this cache exists outside your vault.
|
||||||
|
|
||||||
|
|
@ -35,19 +35,12 @@ For example, if you copied your vault's root folder to a USB memory and opened i
|
||||||
|
|
||||||
### Disabling
|
### Disabling
|
||||||
|
|
||||||
If the plugin is disabled, everything will work as it did before the plugin was enabled — the embedded images will load by means of the underlying browser. Upon enabling it again, the plugin's already cached items will be used, bypassing the browser.
|
If the plugin is disabled, everything will work as it did before the plugin was enabled — the embedded images will load by means of the underlying browser. Upon enabling it again, the plugin's already cached items will be used, bypassing the browser.
|
||||||
|
|
||||||
Note that if an embedded image is removed from a note while the plugin is disabled, its potential cached file will not be removed until that specific note is opened again with the plugin enabled. If you want to disable the plugin you may delete the cache first from settings.
|
Note that if an embedded image is removed from a note while the plugin is disabled, its potential cached file will not be removed until that specific note is opened again with the plugin enabled. If you want to disable the plugin, you may want to delete the cache from the settings first.
|
||||||
|
|
||||||
|
### Syncing
|
||||||
|
|
||||||
|
It is best to avoid caching on several devices without synchronizing in between, as this can lead to synchronization conflicts and inconsistencies. For example, you may see the same image download again on another device.
|
||||||
|
|
||||||
[^1]: This excludes [Obsidian Sync](https://obsidian.md/sync).
|
[^1]: This excludes [Obsidian Sync](https://obsidian.md/sync).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import esbuild from "esbuild";
|
|
||||||
import process from "process";
|
|
||||||
import builtins from "builtin-modules";
|
import builtins from "builtin-modules";
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import process from "process";
|
||||||
|
|
||||||
const banner =
|
const banner =
|
||||||
`/*
|
`/*
|
||||||
|
|
@ -10,6 +12,10 @@ if you want to view the source, please visit the github repository of this plugi
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const prod = (process.argv[2] === "production");
|
const prod = (process.argv[2] === "production");
|
||||||
|
const outdir = prod ? "dist" : "dev-vault";
|
||||||
|
|
||||||
|
fs.copyFileSync("manifest.json", path.join(outdir, "manifest.json"));
|
||||||
|
fs.copyFileSync("styles.css", path.join(outdir, "styles.css"));
|
||||||
|
|
||||||
const context = await esbuild.context({
|
const context = await esbuild.context({
|
||||||
banner: {
|
banner: {
|
||||||
|
|
@ -33,15 +39,16 @@ const context = await esbuild.context({
|
||||||
"@lezer/lr",
|
"@lezer/lr",
|
||||||
...builtins],
|
...builtins],
|
||||||
format: "cjs",
|
format: "cjs",
|
||||||
target: "es2022",
|
// Runtime of min supported Obsidian version 1.8.0, see manifest.json
|
||||||
|
target: "es2024",
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
sourcemap: prod ? false : "inline",
|
sourcemap: prod ? false : "inline",
|
||||||
treeShaking: true,
|
treeShaking: true,
|
||||||
outfile: "main.js",
|
outdir: outdir,
|
||||||
minify: prod,
|
minify: prod,
|
||||||
define: {
|
define: {
|
||||||
"process.env.NODE_ENV": prod ? '"production"' : '"development"',
|
"process.env.NODE_ENV": prod ? '"production"' : '"development"',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (prod) {
|
if (prod) {
|
||||||
|
|
|
||||||
78
eslint.config.js
Normal file
78
eslint.config.js
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// https://typescript-eslint.io/packages/typescript-eslint#usage
|
||||||
|
import eslint from "@eslint/js";
|
||||||
|
import { defineConfig } from 'eslint/config';
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import globals from "globals";
|
||||||
|
|
||||||
|
//import obsidianmd from "eslint-plugin-obsidianmd";
|
||||||
|
|
||||||
|
// If using Svelte
|
||||||
|
// import sveltePlugin from "eslint-plugin-svelte";
|
||||||
|
// import svelteParser from "svelte-eslint-parser";
|
||||||
|
|
||||||
|
export default defineConfig(
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"**/build/**",
|
||||||
|
"**/dist/**",
|
||||||
|
"./main.js",
|
||||||
|
"./src/**/*js",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
eslint.configs.recommended,
|
||||||
|
// https://typescript-eslint.io/users/configs#recommended-configurations
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
//...obsidianmd.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
|
plugins: {
|
||||||
|
// https://typescript-eslint.io/packages/typescript-eslint#manual-usage
|
||||||
|
"@typescript-eslint": tseslint.plugin,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser: tseslint.parser,
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
sourceType: "module",
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
|
||||||
|
// Instead of adding `obsidian-typings` package. Add whatever is used here instead.
|
||||||
|
//createDiv: "readonly",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// You should always have "no-unused-vars": "off" alongside @typescript-eslint/no-unused-vars,
|
||||||
|
// https://typescript-eslint.io/rules/no-unused-vars/
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", {
|
||||||
|
"args": "all",
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"caughtErrors": "all",
|
||||||
|
"caughtErrorsIgnorePattern": "^_",
|
||||||
|
"destructuredArrayIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"ignoreRestSiblings": true,
|
||||||
|
}],
|
||||||
|
|
||||||
|
//
|
||||||
|
"@typescript-eslint/ban-ts-comment": ["error", {
|
||||||
|
"ts-expect-error": false,
|
||||||
|
"ts-ignore": true,
|
||||||
|
"ts-nocheck": true,
|
||||||
|
"ts-check": true,
|
||||||
|
}],
|
||||||
|
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/no-unnecessary-condition": ["warn", {
|
||||||
|
// https://typescript-eslint.io/rules/no-unnecessary-condition/#only-allowed-literals
|
||||||
|
"allowConstantLoopConditions": "only-allowed-literals"
|
||||||
|
}],
|
||||||
|
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
|
|
||||||
import js from "@eslint/js";
|
|
||||||
import globals from "globals";
|
|
||||||
|
|
||||||
import typeScriptPlugin from "@typescript-eslint/eslint-plugin";
|
|
||||||
import typeScriptParser from "@typescript-eslint/parser";
|
|
||||||
|
|
||||||
// If using Svelte
|
|
||||||
// import sveltePlugin from "eslint-plugin-svelte";
|
|
||||||
// import svelteParser from "svelte-eslint-parser";
|
|
||||||
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
files: ["**/*.ts", "**/*.tsx"],
|
|
||||||
plugins: {
|
|
||||||
"@typescript-eslint": typeScriptPlugin,
|
|
||||||
},
|
|
||||||
languageOptions: {
|
|
||||||
parser: typeScriptParser,
|
|
||||||
parserOptions: {
|
|
||||||
project: './tsconfig.json',
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
//...globals.node
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
...js.configs.recommended.rules,
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"no-prototype-builtins": "off",
|
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
"**/*js",
|
|
||||||
"**/node_modules/**",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "come-down",
|
"id": "come-down",
|
||||||
"name": "Come Down",
|
"name": "Come Down",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1-rc.1",
|
||||||
"minAppVersion": "1.8.0",
|
"minAppVersion": "1.8.0",
|
||||||
"description": "Maintains a cache of your notes’ embedded external images.",
|
"description": "Maintains a cache of your notes’ embedded external images.",
|
||||||
"author": "mntno",
|
"author": "mntno",
|
||||||
|
|
|
||||||
29
package.json
29
package.json
|
|
@ -4,30 +4,31 @@
|
||||||
"description": "An Obsidian plugin for caching embedded external images.",
|
"description": "An Obsidian plugin for caching embedded external images.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint": "pnpm eslint .",
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
"build": "(pnpm eslint . || true) && tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "mntno",
|
"author": "mntno",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/language": "6.11.3",
|
"@codemirror/language": "6.12.3",
|
||||||
"@codemirror/state": "6.5.0",
|
"@codemirror/state": "6.5.0",
|
||||||
"@codemirror/view": "6.38.1",
|
"@codemirror/view": "6.38.6",
|
||||||
"@eslint/js": "9.37.0",
|
"@eslint/js": "^9.39.4",
|
||||||
"@lezer/common": "^1.2.3",
|
"@lezer/common": "^1.5.2",
|
||||||
"@types/node": "24.6.2",
|
"@types/node": "25.6.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.45.0",
|
"builtin-modules": "5.1.0",
|
||||||
"@typescript-eslint/parser": "8.45.0",
|
"esbuild": "^0.28.0",
|
||||||
"builtin-modules": "5.0.0",
|
"eslint": "^9.39.4",
|
||||||
"esbuild": "0.25.10",
|
"eslint-plugin-obsidianmd": "^0.1.9",
|
||||||
"globals": "16.4.0",
|
"globals": "17.5.0",
|
||||||
"image-size": "^2.0.2",
|
"image-size": "^2.0.2",
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
"tslib": "2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "5.9.3",
|
"typescript": "^6.0.2",
|
||||||
"typescript-eslint": "8.45.0",
|
"typescript-eslint": "^8.58.1",
|
||||||
"xxhash-wasm": "1.1.0"
|
"xxhash-wasm": "1.1.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
2680
pnpm-lock.yaml
2680
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
14
src/Env.ts
14
src/Env.ts
|
|
@ -54,11 +54,13 @@ export const Env = {
|
||||||
w: console.warn,
|
w: console.warn,
|
||||||
e: console.error,
|
e: console.error,
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
debug: DevContext.logCategory.DEBUGGING ? devLogger.info : noopLogger.info,
|
debug: DevContext.logCategory.DEBUGGING ? devLogger.info : noopLogger.info,
|
||||||
edit: DevContext.logCategory.EDIT_UPDATE_PASS ? devLogger.info : noopLogger.info,
|
edit: DevContext.logCategory.EDIT_UPDATE_PASS ? devLogger.info : noopLogger.info,
|
||||||
read: DevContext.logCategory.POST_PROCESS_PASS ? devLogger.info : noopLogger.info,
|
read: DevContext.logCategory.POST_PROCESS_PASS ? devLogger.info : noopLogger.info,
|
||||||
cm: DevContext.logCategory.CACHE_MANAGER ? devLogger.info : noopLogger.info,
|
cm: DevContext.logCategory.CACHE_MANAGER ? devLogger.info : noopLogger.info,
|
||||||
workaround: DevContext.logCategory.WORKAROUNDS ? devLogger.info : noopLogger.info,
|
workaround: DevContext.logCategory.WORKAROUNDS ? devLogger.info : noopLogger.info,
|
||||||
|
/* eslint-enable @typescript-eslint/no-unnecessary-condition */
|
||||||
},
|
},
|
||||||
|
|
||||||
perf: {
|
perf: {
|
||||||
|
|
@ -69,18 +71,22 @@ export const Env = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param appOrCallback - Either a callback function to execute after the cache is cleared, or an App instance to reload Obsidian.
|
||||||
|
*/
|
||||||
clearBrowserCache: (appOrCallback: (() => void) | App) => {
|
clearBrowserCache: (appOrCallback: (() => void) | App) => {
|
||||||
if (isDev && Platform.isDesktopApp) {
|
if (isDev && Platform.isDesktopApp) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
require('electron').remote.session.defaultSession.clearCache()
|
require('electron').remote.session.defaultSession.clearCache()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (appOrCallback instanceof App) {
|
if (appOrCallback instanceof App) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
app.commands.executeCommandById("app:reload");
|
appOrCallback.commands.executeCommandById("app:reload");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
appOrCallback();
|
appOrCallback();
|
||||||
})
|
})
|
||||||
.catch((error: any) => console.error('Error clearing cache:', error));
|
.catch((error: unknown) => console.error('Error clearing cache:', error));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -93,6 +99,10 @@ export const Env = {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
obj: {
|
||||||
|
is: (value: unknown): value is object => typeof value === "object" && value !== null, // `null` is an object
|
||||||
|
} as const,
|
||||||
|
|
||||||
str: {
|
str: {
|
||||||
EMPTY: "",
|
EMPTY: "",
|
||||||
SPACE: " ",
|
SPACE: " ",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { CacheManager } from "cache/CacheManager";
|
import { CacheManager } from "cache/CacheManager";
|
||||||
import { Env } from "Env";
|
import { Env } from "Env";
|
||||||
import { Platform, Plugin, PluginSettingTab, Setting } from "obsidian";
|
import { Plugin, PluginSettingTab, Setting } from "obsidian";
|
||||||
import { Notice } from "ui/Notice";
|
import { Notice } from "ui/Notice";
|
||||||
|
|
||||||
export interface PluginSettings {
|
export interface PluginSettings {
|
||||||
|
|
@ -29,7 +29,7 @@ type SettingsChanged = (settings: PluginSettings) => void;
|
||||||
export class SettingsManager {
|
export class SettingsManager {
|
||||||
public settings: PluginSettings;
|
public settings: PluginSettings;
|
||||||
public save: () => Promise<void>;
|
public save: () => Promise<void>;
|
||||||
public onChangedCallback: (name: string, value: any) => void | undefined;
|
public onChangedCallback: (name: string, value: unknown) => void | undefined;
|
||||||
|
|
||||||
static readonly DEFAULT_SETTINGS: PluginSettings = {
|
static readonly DEFAULT_SETTINGS: PluginSettings = {
|
||||||
noticeOnDownload: true,
|
noticeOnDownload: true,
|
||||||
|
|
@ -43,7 +43,7 @@ export class SettingsManager {
|
||||||
gitIgnoreCacheDir: "gitIgnoreCacheDir",
|
gitIgnoreCacheDir: "gitIgnoreCacheDir",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
constructor(settings: any, save: (settings: PluginSettings) => Promise<void>, onChangedCallback: (name: string, value: any) => void | undefined) {
|
constructor(settings: PluginSettings, save: (settings: PluginSettings) => Promise<void>, onChangedCallback: (name: string, value: unknown) => void | undefined) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.save = () => save(this.settings);
|
this.save = () => save(this.settings);
|
||||||
this.onChangedCallback = onChangedCallback;
|
this.onChangedCallback = onChangedCallback;
|
||||||
|
|
@ -87,7 +87,7 @@ export class SettingTab extends PluginSettingTab {
|
||||||
this.cacheManager.checkIfMetadataFileChangedExternally().then(() => this.render());
|
this.cacheManager.checkIfMetadataFileChangedExternally().then(() => this.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
public hide(): void {
|
override hide(): void {
|
||||||
Env.log.d("SettingTab:hide");
|
Env.log.d("SettingTab:hide");
|
||||||
if (this.isShown)
|
if (this.isShown)
|
||||||
this.settingsManager.unregisterOnChangedCallback(this.onChangedCallback);
|
this.settingsManager.unregisterOnChangedCallback(this.onChangedCallback);
|
||||||
|
|
@ -102,7 +102,7 @@ export class SettingTab extends PluginSettingTab {
|
||||||
|
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
|
|
||||||
let compactDownloadMessage: Setting | undefined;
|
let compactDownloadMessage: Setting | undefined = undefined;
|
||||||
const setCompactDownloadMessageVisibility = () => {
|
const setCompactDownloadMessageVisibility = () => {
|
||||||
if (settings.noticeOnDownload)
|
if (settings.noticeOnDownload)
|
||||||
compactDownloadMessage?.settingEl.show();
|
compactDownloadMessage?.settingEl.show();
|
||||||
|
|
@ -161,11 +161,7 @@ export class SettingTab extends PluginSettingTab {
|
||||||
|
|
||||||
button.buttonEl.remove();
|
button.buttonEl.remove();
|
||||||
|
|
||||||
if (Env.isDev && Platform.isDesktopApp) {
|
Env.clearBrowserCache(() => new Notice('Electron Cache cleared successfully. Restart vault.'));
|
||||||
require('electron').remote.session.defaultSession.clearCache()
|
|
||||||
.then(() => new Notice('Electron Cache cleared successfully. Restart vault.'))
|
|
||||||
.catch((error: any) => Env.log.e('Error clearing cache:', error));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -198,7 +194,7 @@ export class SettingTab extends PluginSettingTab {
|
||||||
settings.gitIgnoreCacheDir = value;
|
settings.gitIgnoreCacheDir = value;
|
||||||
await this.settingsManager.save();
|
await this.settingsManager.save();
|
||||||
refreshDisabled();
|
refreshDisabled();
|
||||||
this.settingsManager?.onChangedCallback(SettingsManager.SETTING_NAME.gitIgnoreCacheDir, value);
|
this.settingsManager.onChangedCallback(SettingsManager.SETTING_NAME.gitIgnoreCacheDir, value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/cache/CacheManager.ts
vendored
29
src/cache/CacheManager.ts
vendored
|
|
@ -84,7 +84,7 @@ export class CacheFetchError extends CacheError {
|
||||||
|
|
||||||
constructor(cacheKey: string, readonly error: Error | null, readonly info: { readonly sourceUrl: string, readonly statusCode?: number }) {
|
constructor(cacheKey: string, readonly error: Error | null, readonly info: { readonly sourceUrl: string, readonly statusCode?: number }) {
|
||||||
|
|
||||||
super(cacheKey, `Failed to fetch cache: ${cacheKey}. Status: ${info?.statusCode ?? "Unknown"}`, { cause: error });
|
super(cacheKey, `Failed to fetch cache: ${cacheKey}. Status: ${info.statusCode ?? "Unknown"}`, { cause: error });
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error.message && error.message.includes("net::ERR_NAME_NOT_RESOLVED")) {
|
if (error.message && error.message.includes("net::ERR_NAME_NOT_RESOLVED")) {
|
||||||
|
|
@ -100,7 +100,7 @@ export class CacheFetchError extends CacheError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.isRetryable = !this.info?.statusCode || (this.info?.statusCode >= 500 && this.info?.statusCode < 600);
|
this.isRetryable = !this.info.statusCode || (this.info.statusCode >= 500 && this.info.statusCode < 600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +136,7 @@ export class CacheManager {
|
||||||
private readonly metadataFilePath: string;
|
private readonly metadataFilePath: string;
|
||||||
|
|
||||||
private static hasher: XXHashAPI;
|
private static hasher: XXHashAPI;
|
||||||
|
private static isHasherInitialized = false;
|
||||||
|
|
||||||
private readonly filePathsToOmitWhenClearingCache: string[]
|
private readonly filePathsToOmitWhenClearingCache: string[]
|
||||||
|
|
||||||
|
|
@ -148,8 +149,10 @@ export class CacheManager {
|
||||||
|
|
||||||
public static async create(vault: Vault, cacheDir: string, metadataFilePath: string, filePathsToOmitWhenClearingCache: string[] = []) {
|
public static async create(vault: Vault, cacheDir: string, metadataFilePath: string, filePathsToOmitWhenClearingCache: string[] = []) {
|
||||||
const instance = new this(vault, cacheDir, metadataFilePath, filePathsToOmitWhenClearingCache);
|
const instance = new this(vault, cacheDir, metadataFilePath, filePathsToOmitWhenClearingCache);
|
||||||
if (!this.hasher)
|
if (!CacheManager.isHasherInitialized) {
|
||||||
this.hasher = await xxhash(); // Seems to be quick.
|
CacheManager.hasher = await xxhash(); // Seems to be quick.
|
||||||
|
CacheManager.isHasherInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
await instance.initCache(); // This can be called lazily if it turns out reading the json file takes too long time.
|
await instance.initCache(); // This can be called lazily if it turns out reading the json file takes too long time.
|
||||||
|
|
||||||
|
|
@ -172,8 +175,6 @@ export class CacheManager {
|
||||||
try {
|
try {
|
||||||
await this.loadMetadata();
|
await this.loadMetadata();
|
||||||
this.cacheInitiated = true;
|
this.cacheInitiated = true;
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
} finally {
|
} finally {
|
||||||
this.initPromise = null; // Reset when done.
|
this.initPromise = null; // Reset when done.
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +236,7 @@ export class CacheManager {
|
||||||
Env.log.cm(Env.dev.icon.CACHE_MANAGER, "\t", "Current retain count: ", this.retainCount());
|
Env.log.cm(Env.dev.icon.CACHE_MANAGER, "\t", "Current retain count: ", this.retainCount());
|
||||||
|
|
||||||
// If retainer/file does not exist / is not yet registered, it will be.
|
// If retainer/file does not exist / is not yet registered, it will be.
|
||||||
let retainer = this.metadataRoot.retainers[retainerPath];
|
const retainer = this.metadataRoot.retainers[retainerPath];
|
||||||
const cacheKeysCurrentlyReferenced = retainer ? [...retainer.ref] : [];
|
const cacheKeysCurrentlyReferenced = retainer ? [...retainer.ref] : [];
|
||||||
|
|
||||||
// Populate set of keys to retain.
|
// Populate set of keys to retain.
|
||||||
|
|
@ -340,7 +341,7 @@ export class CacheManager {
|
||||||
public renameRetainer(oldPath: string, path: string) {
|
public renameRetainer(oldPath: string, path: string) {
|
||||||
Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager.renameRetainer\n\t${oldPath}`);
|
Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager.renameRetainer\n\t${oldPath}`);
|
||||||
|
|
||||||
let retainer = this.metadataRoot.retainers[oldPath];
|
const retainer = this.metadataRoot.retainers[oldPath];
|
||||||
if (retainer !== undefined) {
|
if (retainer !== undefined) {
|
||||||
this.metadataRoot.retainers[path] = retainer;
|
this.metadataRoot.retainers[path] = retainer;
|
||||||
delete this.metadataRoot.retainers[oldPath];
|
delete this.metadataRoot.retainers[oldPath];
|
||||||
|
|
@ -354,7 +355,7 @@ export class CacheManager {
|
||||||
public async removeRetainer(path: string) {
|
public async removeRetainer(path: string) {
|
||||||
Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager.removeRetainer\n\t${path}`);
|
Env.log.cm(Env.dev.icon.CACHE_MANAGER, `CacheManager.removeRetainer\n\t${path}`);
|
||||||
|
|
||||||
let retainer = this.metadataRoot.retainers[path];
|
const retainer = this.metadataRoot.retainers[path];
|
||||||
if (retainer === undefined) {
|
if (retainer === undefined) {
|
||||||
// This can happen if a file which hasn't been open was deleted without opening it.
|
// This can happen if a file which hasn't been open was deleted without opening it.
|
||||||
Env.log.cm("\tFailed: No retainer found.");
|
Env.log.cm("\tFailed: No retainer found.");
|
||||||
|
|
@ -430,7 +431,7 @@ export class CacheManager {
|
||||||
// Increase count for each reference found.
|
// Increase count for each reference found.
|
||||||
for (const cacheKey of retainer.ref) {
|
for (const cacheKey of retainer.ref) {
|
||||||
// Note: make sure to `if (count !== undefined)` rather than `if(count)` otherwise `count` will not be treated as a `number` and addition will fail.
|
// Note: make sure to `if (count !== undefined)` rather than `if(count)` otherwise `count` will not be treated as a `number` and addition will fail.
|
||||||
let count = retainCounts[cacheKey];
|
const count = retainCounts[cacheKey];
|
||||||
Env.assert(count !== undefined, "Retainer is referencing a cache key that does not exist", cacheKey);
|
Env.assert(count !== undefined, "Retainer is referencing a cache key that does not exist", cacheKey);
|
||||||
if (count !== undefined)
|
if (count !== undefined)
|
||||||
retainCounts[cacheKey] = count + 1;
|
retainCounts[cacheKey] = count + 1;
|
||||||
|
|
@ -452,7 +453,7 @@ export class CacheManager {
|
||||||
* @returns `true` if the key is retained in {@link retainCounts}.
|
* @returns `true` if the key is retained in {@link retainCounts}.
|
||||||
*/
|
*/
|
||||||
private isRetained(retainCounts: Record<string, number>, key: string) {
|
private isRetained(retainCounts: Record<string, number>, key: string) {
|
||||||
let count = retainCounts[key];
|
const count = retainCounts[key];
|
||||||
return count === undefined || count === 0 ? false : true; // Since 1.0.6, retain counts of zero are actually not present in the dictionary anymore.
|
return count === undefined || count === 0 ? false : true; // Since 1.0.6, retain counts of zero are actually not present in the dictionary anymore.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +498,7 @@ export class CacheManager {
|
||||||
|
|
||||||
//
|
//
|
||||||
const retainers: CacheRetainer[] = Object.values(this.metadataRoot.retainers);
|
const retainers: CacheRetainer[] = Object.values(this.metadataRoot.retainers);
|
||||||
const numberOfRetainers = retainers.length;
|
//const numberOfRetainers = retainers.length;
|
||||||
|
|
||||||
// Here the Mardown file has been deleted but its still exists as a retainer.
|
// Here the Mardown file has been deleted but its still exists as a retainer.
|
||||||
const retainersWithoutActualFile = [];
|
const retainersWithoutActualFile = [];
|
||||||
|
|
@ -787,7 +788,7 @@ export class CacheManager {
|
||||||
* Aborts current download requests for the specified file.
|
* Aborts current download requests for the specified file.
|
||||||
* @todo
|
* @todo
|
||||||
*/
|
*/
|
||||||
async cancelOngoing(filePath: string) {
|
async cancelOngoing(_filePath: string) {
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -987,7 +988,7 @@ export class CacheManager {
|
||||||
return {
|
return {
|
||||||
w: sizeCalcResult.width,
|
w: sizeCalcResult.width,
|
||||||
h: sizeCalcResult.height,
|
h: sizeCalcResult.height,
|
||||||
t: sizeCalcResult?.type ?? "",
|
t: sizeCalcResult.type ?? Env.str.EMPTY,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof TypeError)
|
if (error instanceof TypeError)
|
||||||
|
|
|
||||||
21
src/main.ts
21
src/main.ts
|
|
@ -26,7 +26,7 @@ export default class ComeDownPlugin extends Plugin {
|
||||||
private settingsManager!: SettingsManager;
|
private settingsManager!: SettingsManager;
|
||||||
private cacheManager!: CacheManager;
|
private cacheManager!: CacheManager;
|
||||||
|
|
||||||
async onload() {
|
override async onload() {
|
||||||
Env.log.d("Plugin:onload");
|
Env.log.d("Plugin:onload");
|
||||||
|
|
||||||
Notice.setName(this.manifest.name);
|
Notice.setName(this.manifest.name);
|
||||||
|
|
@ -104,20 +104,19 @@ export default class ComeDownPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onunload() {
|
override async onunload() {
|
||||||
Env.log.d("Plugin:onunload");
|
Env.log.d("Plugin:onunload");
|
||||||
await this.cacheManager?.cancelAllOngoing();
|
await this.cacheManager.cancelAllOngoing();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onExternalSettingsChange() {
|
override onExternalSettingsChange() {
|
||||||
Env.log.d("Plugin:onExternalSettingsChange");
|
Env.log.d("Plugin:onExternalSettingsChange");
|
||||||
if (this.data) {
|
|
||||||
ComeDownPlugin.loadPluginData(this).then(data => {
|
ComeDownPlugin.loadPluginData(this).then(data => {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.settingsManager.onSettingsChangedExternally(this.data.settings);
|
this.settingsManager.onSettingsChangedExternally(this.data.settings);
|
||||||
setTimeout(() => this.cacheManager.checkIfMetadataFileChangedExternally(), 1000);
|
setTimeout(() => this.cacheManager.checkIfMetadataFileChangedExternally(), 1000);
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async loadPluginData(plugin: Plugin): Promise<PluginData> {
|
private static async loadPluginData(plugin: Plugin): Promise<PluginData> {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export class EditorViewPlugin {
|
||||||
Env.log.d("EditorViewPlugin:constructor");
|
Env.log.d("EditorViewPlugin:constructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
update(update: ViewUpdate) {
|
update(_update: ViewUpdate) {
|
||||||
//Env.log.d("EditorViewPlugin:update", this.getViewMetadata(update.view));
|
//Env.log.d("EditorViewPlugin:update", this.getViewMetadata(update.view));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,23 @@ import { Logger as BaseLogger } from "utils/Logger";
|
||||||
export class Logger extends BaseLogger {
|
export class Logger extends BaseLogger {
|
||||||
|
|
||||||
public beginMsg(...args: unknown[]) {
|
public beginMsg(...args: unknown[]) {
|
||||||
if (!Env.isDev) return "";
|
if (!Env.isDev)
|
||||||
const joinedArgs = args.length > 0 ? `(${args.join(" ")})` : "";
|
return Env.str.EMPTY;
|
||||||
return `${this.symbol} Begin pass. ${joinedArgs} ➡️🚪 ${this.idString}`;
|
|
||||||
|
return `${this.symbol} Begin pass. ${this.joinArgs(args)} ➡️🚪 ${this.idString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public endMsg(...args: unknown[]) {
|
public endMsg(...args: unknown[]) {
|
||||||
if (!Env.isDev) return "";
|
if (!Env.isDev)
|
||||||
const joinedArgs = args.length > 0 ? `(${args.join(" ")})` : "";
|
return Env.str.EMPTY;
|
||||||
return `${this.symbol} End pass. Finished. ${joinedArgs} ✅🚪➡️ ${this.idString}`;
|
|
||||||
|
return `${this.symbol} End pass. Finished. ${this.joinArgs(args)} ✅🚪➡️ ${this.idString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abortMsg(...args: unknown[]) {
|
public abortMsg(...args: unknown[]) {
|
||||||
if (!Env.isDev) return "";
|
if (!Env.isDev)
|
||||||
const joinedArgs = args.length > 0 ? `(${args.join(" ")})` : "";
|
return Env.str.EMPTY;
|
||||||
return `${this.symbol} End pass. Aborted. ${joinedArgs} ❌🚪➡️ ${this.idString}`;
|
|
||||||
|
return `${this.symbol} End pass. Aborted. ${this.joinArgs(args)} ❌🚪➡️ ${this.idString}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export class ProcessingContext {
|
||||||
|
|
||||||
this.vuCtx = underlyingCtx instanceof ViewUpdateContext ? underlyingCtx : null;
|
this.vuCtx = underlyingCtx instanceof ViewUpdateContext ? underlyingCtx : null;
|
||||||
this.ppCtx = underlyingCtx instanceof PostProcessorContext ? underlyingCtx : null;
|
this.ppCtx = underlyingCtx instanceof PostProcessorContext ? underlyingCtx : null;
|
||||||
Env.dev.assert(this.vuCtx || this.ppCtx);
|
Env.dev.assert(this.vuCtx !== null || this.ppCtx !== null);
|
||||||
|
|
||||||
this.viewContainerEl = viewContainerEl;
|
this.viewContainerEl = viewContainerEl;
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ export class ProcessingContext {
|
||||||
const contentEl = viewUpdate.view.contentDOM;
|
const contentEl = viewUpdate.view.contentDOM;
|
||||||
let viewContainerEl: HTMLElement | null = null;
|
let viewContainerEl: HTMLElement | null = null;
|
||||||
|
|
||||||
let view = ProcessingContext.tryGetViewInstance(app, contentEl);
|
const view = ProcessingContext.tryGetViewInstance(app, contentEl);
|
||||||
if (view)
|
if (view)
|
||||||
viewContainerEl = view.containerEl;
|
viewContainerEl = view.containerEl;
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ export class ProcessingContext {
|
||||||
if (viewContainerEl === null)
|
if (viewContainerEl === null)
|
||||||
viewContainerEl = Arr.firstOrNull(ObsAssistant.viewContainerEl({ element: contentEl, dir: "up" }));
|
viewContainerEl = Arr.firstOrNull(ObsAssistant.viewContainerEl({ element: contentEl, dir: "up" }));
|
||||||
|
|
||||||
let viewingMode = ProcessingContext.tryGetViewingMode(view ?? undefined, viewContainerEl ?? undefined);
|
const viewingMode = ProcessingContext.tryGetViewingMode(view ?? undefined, viewContainerEl ?? undefined);
|
||||||
|
|
||||||
let associatedFile: TFile | null = null;
|
let associatedFile: TFile | null = null;
|
||||||
if (view !== null)
|
if (view !== null)
|
||||||
|
|
@ -101,7 +101,7 @@ export class ProcessingContext {
|
||||||
|
|
||||||
let viewContainerEl: HTMLElement | null = null;
|
let viewContainerEl: HTMLElement | null = null;
|
||||||
|
|
||||||
let view = ProcessingContext.tryGetViewInstance(app, element);
|
const view = ProcessingContext.tryGetViewInstance(app, element);
|
||||||
if (view)
|
if (view)
|
||||||
viewContainerEl = view.containerEl;
|
viewContainerEl = view.containerEl;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export class ProcessingPass {
|
||||||
|
|
||||||
public retainCache(urlOrCache: string | CacheRequest) {
|
public retainCache(urlOrCache: string | CacheRequest) {
|
||||||
if (this.ctx.isCacheAccessReadWrite()) {
|
if (this.ctx.isCacheAccessReadWrite()) {
|
||||||
Env.assert(urlOrCache);
|
Env.assert(Env.str.nonEmpty(urlOrCache) !== undefined || Env.obj.is(urlOrCache));
|
||||||
this.ctx.logr.log(this.ctx.logr.t(() => this.ctx.logr.msg(`retainCache: Will retain ${CacheManager.createCacheKeyFromOriginalSrc(Env.str.is(urlOrCache) ? urlOrCache : urlOrCache.source)} at the end of pass`)));
|
this.ctx.logr.log(this.ctx.logr.t(() => this.ctx.logr.msg(`retainCache: Will retain ${CacheManager.createCacheKeyFromOriginalSrc(Env.str.is(urlOrCache) ? urlOrCache : urlOrCache.source)} at the end of pass`)));
|
||||||
|
|
||||||
if (Env.str.is(urlOrCache)) {
|
if (Env.str.is(urlOrCache)) {
|
||||||
|
|
@ -69,7 +69,7 @@ export class ProcessingPass {
|
||||||
|
|
||||||
public ignoreCache(src: string) {
|
public ignoreCache(src: string) {
|
||||||
if (this.ctx.isCacheAccessReadWrite()) {
|
if (this.ctx.isCacheAccessReadWrite()) {
|
||||||
Env.assert(src);
|
Env.assert(Env.str.nonEmpty(src) !== undefined);
|
||||||
this.ctx.logr.log(this.ctx.logr.t(() => this.ctx.logr.msg(`ignoreCache: Will ignore ${CacheManager.createCacheKeyFromOriginalSrc(src)} at the end of pass`)));
|
this.ctx.logr.log(this.ctx.logr.t(() => this.ctx.logr.msg(`ignoreCache: Will ignore ${CacheManager.createCacheKeyFromOriginalSrc(src)} at the end of pass`)));
|
||||||
|
|
||||||
this.requestsToIgnore.add(CacheManager.createRequest(src, this.ctx.associatedFile.path));
|
this.requestsToIgnore.add(CacheManager.createRequest(src, this.ctx.associatedFile.path));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ViewUpdate } from "@codemirror/view";
|
import { ViewUpdate } from "@codemirror/view";
|
||||||
import { Env } from "Env";
|
import { Env } from "Env";
|
||||||
import { HtmlAssistant, HTMLElementAttribute, HTMLElementCacheState } from "processing/HtmlAssistant";
|
import { HtmlAssistant, HTMLElementCacheState } from "processing/HtmlAssistant";
|
||||||
import { Url } from "utils/Url";
|
import { Url } from "utils/Url";
|
||||||
|
|
||||||
export class Workarounds {
|
export class Workarounds {
|
||||||
|
|
@ -29,7 +29,7 @@ export class Workarounds {
|
||||||
if (update.changes.empty)
|
if (update.changes.empty)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var sourcesToIgnore: string[] = [];
|
const sourcesToIgnore: string[] = [];
|
||||||
|
|
||||||
update.changes.iterChanges((_fromA, _toA, cursorStartPos, cursorEndPosition, inserted) => {
|
update.changes.iterChanges((_fromA, _toA, cursorStartPos, cursorEndPosition, inserted) => {
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ export class Workarounds {
|
||||||
* Will not match if there's no link because an img element is not inserted until the link
|
* Will not match if there's no link because an img element is not inserted until the link
|
||||||
* begins with `http:`, `https:`, `ftp:`, `ws:`, or `wss:`.
|
* begins with `http:`, `https:`, `ftp:`, `ws:`, or `wss:`.
|
||||||
*/
|
*/
|
||||||
private static readonly markdownLinkRegex = /(?<!\!)\[.*?\]\((.+?)\)/;
|
private static readonly markdownLinkRegex = /(?<!!)\[.*?\]\((.+?)\)/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since image elements are not available in {@link detectSourcesOfInvalidImageElements}, this method is used with the result of that when the elements are available.
|
* Since image elements are not available in {@link detectSourcesOfInvalidImageElements}, this method is used with the result of that when the elements are available.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Global (see tsconfig `include`)
|
// types.ts - Global type declarations
|
||||||
// Do not add `export`
|
|
||||||
|
|
||||||
type Prettify<T> = {
|
// Global type aliases
|
||||||
|
|
||||||
|
export type Prettify<T> = {
|
||||||
[K in keyof T]: T[K];
|
[K in keyof T]: T[K];
|
||||||
} & {};
|
} & {};
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ export class InfoModal extends Modal {
|
||||||
clearCacheButton!: ButtonComponent;
|
clearCacheButton!: ButtonComponent;
|
||||||
closeButton!: ButtonComponent;
|
closeButton!: ButtonComponent;
|
||||||
|
|
||||||
onOpen(): void {
|
override onOpen(): void {
|
||||||
super.onOpen();
|
super.onOpen();
|
||||||
this.cacheManager.registerMetadataChanged(this.onMetadataChangedCallback);
|
this.cacheManager.registerMetadataChanged(this.onMetadataChangedCallback);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -81,7 +81,7 @@ export class InfoModal extends Modal {
|
||||||
this.populate();
|
this.populate();
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose(): void {
|
override onClose(): void {
|
||||||
super.onClose();
|
super.onClose();
|
||||||
this.cacheManager.unregisterMetadataChanged(this.onMetadataChangedCallback);
|
this.cacheManager.unregisterMetadataChanged(this.onMetadataChangedCallback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
||||||
import { EditorView, ViewUpdate } from "@codemirror/view";
|
import { EditorView, ViewUpdate } from "@codemirror/view";
|
||||||
import { SyntaxNodeRef, Tree } from "@lezer/common";
|
import { Tree } from "@lezer/common";
|
||||||
import { Env } from "Env";
|
import { Env } from "Env";
|
||||||
|
|
||||||
export interface ParsedImage {
|
export interface ParsedImage {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export class Logger {
|
||||||
public readonly log: LoggerFn;
|
public readonly log: LoggerFn;
|
||||||
public readonly id: number;
|
public readonly id: number;
|
||||||
public readonly symbol: string;
|
public readonly symbol: string;
|
||||||
public readonly t = (action: () => string) => Env.isDev ? action() : "";
|
public readonly t = (action: () => string) => Env.isDev ? action() : Env.str.EMPTY;
|
||||||
|
|
||||||
constructor(log: LoggerFn, id: number, symbol: string) {
|
constructor(log: LoggerFn, id: number, symbol: string) {
|
||||||
this.log = log;
|
this.log = log;
|
||||||
|
|
@ -17,9 +17,15 @@ export class Logger {
|
||||||
return "— ID" + this.id;
|
return "— ID" + this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public msg(...args: unknown[]) {
|
protected joinArgs(args: unknown[]): string {
|
||||||
if (!Env.isDev) return "";
|
return args.length === 0 ? Env.str.EMPTY : `(${args.join(" ")})`;
|
||||||
const mappedArgs = args.map(arg => arg === undefined ? "undefined" : arg);
|
|
||||||
return `${this.symbol} ${mappedArgs.join(" ")} ${this.idString}`;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public msg(...args: unknown[]) {
|
||||||
|
if (!Env.isDev)
|
||||||
|
return Env.str.EMPTY;
|
||||||
|
|
||||||
|
const mappedArgs = args.map(arg => arg === undefined ? "undefined" : arg);
|
||||||
|
return `${this.symbol} ${mappedArgs.join(Env.str.SPACE)} ${this.idString}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Env } from "Env";
|
import { Env } from "Env";
|
||||||
import { App, FileView, getIcon, ItemView, MarkdownPostProcessorContext, MarkdownView, TextFileView, TFile, View } from "obsidian";
|
import { App, FileView, getIcon, ItemView, MarkdownPostProcessorContext, MarkdownView, TextFileView, TFile, View } from "obsidian";
|
||||||
|
import { Prettify } from "types";
|
||||||
import { childEl, firstChildEl, firstParentEl } from "utils/dom";
|
import { childEl, firstChildEl, firstParentEl } from "utils/dom";
|
||||||
import { Arr } from "utils/ts";
|
|
||||||
|
|
||||||
export type ObsViewMode = "none" | "reader" | "preview" | "source";
|
export type ObsViewMode = "none" | "reader" | "preview" | "source";
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ export class ObsAssistant {
|
||||||
/** Internal API */
|
/** Internal API */
|
||||||
public static containerElFromPostProcessorContext(postProcessorContext: MarkdownPostProcessorContext) {
|
public static containerElFromPostProcessorContext(postProcessorContext: MarkdownPostProcessorContext) {
|
||||||
Env.assert("containerEl" in postProcessorContext, "MarkdownPostProcessorContext does not have `containerEl`");
|
Env.assert("containerEl" in postProcessorContext, "MarkdownPostProcessorContext does not have `containerEl`");
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
const containerEl = postProcessorContext.containerEl;
|
const containerEl = postProcessorContext.containerEl;
|
||||||
if (containerEl)
|
if (containerEl)
|
||||||
return containerEl as HTMLElement;
|
return containerEl as HTMLElement;
|
||||||
|
|
@ -56,13 +56,13 @@ export class ObsAssistant {
|
||||||
* ```html
|
* ```html
|
||||||
* <div class="popover hover-popover">`
|
* <div class="popover hover-popover">`
|
||||||
* <div class="markdown-embed is-loaded">
|
* <div class="markdown-embed is-loaded">
|
||||||
* <div class="markdown-embed-content …">
|
* <div class="markdown-embed-content …">
|
||||||
* <div class="markdown-preview-view markdown-rendered …">
|
* <div class="markdown-preview-view markdown-rendered …">
|
||||||
* </div>
|
* </div>
|
||||||
* …
|
* …
|
||||||
* </div>
|
* </div>
|
||||||
* </div>
|
* </div>
|
||||||
* </div>
|
* </div>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @remarks `hover-popover` is a modifier class that extends or specifies the behavior. It indicates the popover appears on hover.
|
* @remarks `hover-popover` is a modifier class that extends or specifies the behavior. It indicates the popover appears on hover.
|
||||||
|
|
@ -89,31 +89,37 @@ export class ObsAssistant {
|
||||||
let element: HTMLElement[] | null = null;
|
let element: HTMLElement[] | null = null;
|
||||||
|
|
||||||
const findChildOrChildren = (options as TryGetAllOptions).allDescendants === true ? childEl : firstChildEl;
|
const findChildOrChildren = (options as TryGetAllOptions).allDescendants === true ? childEl : firstChildEl;
|
||||||
|
|
||||||
const find = (el: HTMLElement) => {
|
const find = (el: HTMLElement) => {
|
||||||
if (options.dir === "down") {
|
|
||||||
element = Arr.orNull(findChildOrChildren(el, selectors));
|
const orNull = (v: HTMLElement[] | HTMLElement | null): HTMLElement[] | null => v === null
|
||||||
}
|
? null
|
||||||
else if (options.dir === "up") {
|
: (Array.isArray(v) ? v : [v]);
|
||||||
element = Arr.orNull(firstParentEl(el, selectors));
|
|
||||||
}
|
switch (options.dir) {
|
||||||
else if (options.dir === "downup") {
|
case "down":
|
||||||
element = Arr.orNull(findChildOrChildren(el, selectors));
|
return orNull(findChildOrChildren(el, selectors));
|
||||||
if (element === null)
|
case "up":
|
||||||
element = Arr.orNull(firstParentEl(el, selectors));
|
return orNull(firstParentEl(el, selectors));
|
||||||
}
|
case "downup": {
|
||||||
else if (options.dir === "updown") {
|
let element = orNull(findChildOrChildren(el, selectors));
|
||||||
element = Arr.orNull(firstParentEl(el, selectors));
|
if (element === null)
|
||||||
if (element === null)
|
element = orNull(firstParentEl(el, selectors));
|
||||||
element = Arr.orNull(findChildOrChildren(el, selectors));
|
return element;
|
||||||
}
|
}
|
||||||
|
case "updown": {
|
||||||
|
let element = orNull(firstParentEl(el, selectors));
|
||||||
|
if (element === null)
|
||||||
|
element = orNull(findChildOrChildren(el, selectors));
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.view !== undefined) {
|
if (options.view !== undefined) {
|
||||||
if (options.view instanceof ItemView)
|
if (options.view instanceof ItemView)
|
||||||
find(options.view.contentEl);
|
element = find(options.view.contentEl);
|
||||||
else
|
else
|
||||||
find(options.view.containerEl);
|
element = find(options.view.containerEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element === null && options.postProcessorContext) {
|
if (element === null && options.postProcessorContext) {
|
||||||
|
|
@ -229,7 +235,7 @@ export class ObsAssistant {
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const iconColor = color ?? getComputedStyle(el).getPropertyValue(CssVar.Icon.COLOR).trim();
|
const iconColor = color ?? getComputedStyle(el).getPropertyValue(CssVar.Icon.COLOR).trim();
|
||||||
Env.assert(iconColor, "CSS variable not found:", CssVar.Icon.COLOR);
|
Env.assert(Env.str.nonEmpty(iconColor) !== undefined, "CSS variable not found:", CssVar.Icon.COLOR);
|
||||||
icon.setAttribute("stroke", iconColor || fallbackColor);
|
icon.setAttribute("stroke", iconColor || fallbackColor);
|
||||||
icon.setAttribute("stroke-width", "1");
|
icon.setAttribute("stroke-width", "1");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ export class Url {
|
||||||
try {
|
try {
|
||||||
const url = new URL(src);
|
const url = new URL(src);
|
||||||
return url.protocol === "http:" || url.protocol === "https:";
|
return url.protocol === "http:" || url.protocol === "https:";
|
||||||
} catch (error) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
|
|
||||||
export const Arr = {
|
export const Arr = {
|
||||||
firstOrNull: <T>(a: Array<T>): T | null => a.first() ?? null,
|
firstOrNull: <T>(a: Array<T>): T | null => a.first() ?? null,
|
||||||
orNull: <T>(v: T[] | T | null): T[] | null =>
|
|
||||||
v === null
|
|
||||||
? null
|
|
||||||
: (Array.isArray(v) ? v : [v]),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const Err = {
|
export const Err = {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,47 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "src",
|
// Enable to check TS7 compatibility.
|
||||||
"inlineSourceMap": true,
|
"stableTypeOrdering": false,
|
||||||
"inlineSources": true,
|
|
||||||
"module": "ESNext",
|
"rootDir": "./src",
|
||||||
"target": "ES2022",
|
"paths": {
|
||||||
"allowJs": true,
|
"*": ["./src/*"],
|
||||||
"noImplicitAny": true,
|
"#/*": ["./src/*"]
|
||||||
"moduleResolution": "node",
|
},
|
||||||
"importHelpers": true,
|
|
||||||
"isolatedModules": true,
|
"inlineSourceMap": true,
|
||||||
"strict": true,
|
"inlineSources": true,
|
||||||
"strictNullChecks": true,
|
|
||||||
"noUncheckedIndexedAccess": true,
|
"target": "ES2024",
|
||||||
"lib": ["DOM", "ES2022"]
|
"lib": [
|
||||||
},
|
"DOM",
|
||||||
"include": [
|
"ES2024"
|
||||||
"**/*.ts"
|
],
|
||||||
]
|
"types": ["node"],
|
||||||
|
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"isolatedModules": true,
|
||||||
|
|
||||||
|
"importHelpers": true,
|
||||||
|
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": false,
|
||||||
|
"erasableSyntaxOnly": false,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
|
||||||
|
// For ESLint to handle:
|
||||||
|
"allowUnreachableCode": true,
|
||||||
|
"allowUnusedLabels": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"./node_modules",
|
||||||
|
"./dist",
|
||||||
|
"./build",
|
||||||
|
"./main.js",
|
||||||
|
"./eslint.config.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"1.1.1": "1.8.0",
|
||||||
"1.1.0": "1.8.0",
|
"1.1.0": "1.8.0",
|
||||||
"1.0.7": "1.8.0",
|
"1.0.7": "1.8.0",
|
||||||
"1.0.6": "1.8.0",
|
"1.0.6": "1.8.0",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue