mirror of
https://github.com/mntno/obsidian-come-down.git
synced 2026-07-22 05:43:15 +00:00
162 lines
4.9 KiB
TypeScript
162 lines
4.9 KiB
TypeScript
import { MarkdownView, Plugin, editorLivePreviewField, editorInfoField, MarkdownFileInfo, MarkdownPostProcessorContext, Notice, debounce, normalizePath } from "obsidian";
|
|
import { SettingTab, SettingsManager } from "src/SettingTab";
|
|
import { CacheManager, CacheMetadataItem, CacheResourceType } from "src/CacheManager";
|
|
import { EditorView, ViewUpdate } from "@codemirror/view";
|
|
|
|
export default class PluginImplementation extends Plugin {
|
|
settingsManager: SettingsManager;
|
|
cacheManager: CacheManager;
|
|
|
|
async onload() {
|
|
|
|
const pluginDir = this.manifest.dir;
|
|
if (!pluginDir) {
|
|
const errorMsg = `${this.manifest.name}: Cannot load because plugin directory is unknown`;
|
|
new Notice(errorMsg, 0);
|
|
throw new Error(errorMsg);
|
|
}
|
|
this.cacheManager = new CacheManager(this.app, pluginDir + "/cache");
|
|
|
|
this.settingsManager = new SettingsManager(
|
|
await this.loadData(),
|
|
async (settings) => await this.saveData(settings),
|
|
);
|
|
this.addSettingTab(new SettingTab(this, this.settingsManager));
|
|
|
|
// The post processor runs after the Markdown has been processed into HTML. It lets you add, remove, or replace HTML elements to the rendered document.
|
|
this.registerMarkdownPostProcessor((element, context) => {
|
|
this.processReadingView(element, context);
|
|
});
|
|
|
|
// Handle live preview
|
|
this.registerEditorExtension(EditorView.updateListener.of((update: ViewUpdate) => {
|
|
this.processLivePreview(update);
|
|
}));
|
|
|
|
this.addCommand({
|
|
id: "clear-all-cache",
|
|
name: "Clear All Cached Files",
|
|
callback: () => {
|
|
this.cacheManager.clearCached();
|
|
}
|
|
});
|
|
|
|
this.addCommand({
|
|
id: 'redownload-all-images-in-this-file',
|
|
name: 'Redownload all images in this file.',
|
|
checkCallback: (checking: boolean) => {
|
|
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
|
if (markdownView) {
|
|
if (!checking) {
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
onunload() {
|
|
this.cacheManager?.cancelAllOngoing();
|
|
}
|
|
|
|
processLivePreview(update: ViewUpdate) {
|
|
const view = update.view;
|
|
const isInLivePreview = view.state.field(editorLivePreviewField);
|
|
if (!isInLivePreview)
|
|
return;
|
|
|
|
if (update.docChanged || update.viewportChanged) {
|
|
|
|
const imageElements = view.contentDOM.findAll("img") as HTMLImageElement[];
|
|
|
|
/* Gave up with this. Not sure if it's more efficient.
|
|
syntaxTree(view.state).iterate({
|
|
enter: ({ type, from, to }: SyntaxNodeRef) => {
|
|
console.log(`${view.state.doc.sliceString(from, to)}`);
|
|
}
|
|
});
|
|
*/
|
|
|
|
const markdownView = view.state.field(editorInfoField) as MarkdownFileInfo;
|
|
this.handleImages(imageElements, markdownView.file?.path);
|
|
}
|
|
}
|
|
|
|
processReadingView(element: HTMLElement, context: MarkdownPostProcessorContext) {
|
|
const imageElements = element.findAll("img") as HTMLImageElement[];
|
|
this.handleImages(imageElements, context.sourcePath);
|
|
}
|
|
|
|
/**
|
|
* Initiates a call to the {@link CacheManager} for each image element and changing the src to the cached file.
|
|
*
|
|
* @param imageElements All images in the post proceessed HTML.
|
|
* @param filePath Path to the file containing this HTML.
|
|
*/
|
|
handleImages(imageElements: HTMLImageElement[], filePath: string | undefined) {
|
|
|
|
//if (filePath)
|
|
// this.cacheManager.cancelOngoing(filePath);
|
|
|
|
imageElements
|
|
.filter((imageElement => imageElement.src.length > 0)) // In case src hasn't been set yet.
|
|
.filter((imageElement) => imageElement.dataset.comeDownCacheInitiated !== "true" ) // Avoid stampede.
|
|
.forEach((imageElement) => {
|
|
|
|
const metadata: CacheMetadataItem = {
|
|
key: imageElement.src,
|
|
filePath: filePath,
|
|
type: CacheResourceType.ExternalImage
|
|
};
|
|
|
|
const originalAltText = imageElement.alt;
|
|
|
|
imageElement.alt = "Loading...";
|
|
imageElement.dataset.comeDownCacheInitiated = "true";
|
|
imageElement.src = `data:image/svg+xml;charset=utf-8,${PluginImplementation.ENCODED_LOADING_ICON}`;
|
|
|
|
console.log(`handleImage: ${metadata.key}, alt: ${originalAltText}`);
|
|
|
|
this.cacheManager.getCache(metadata, (result) => {
|
|
imageElement.alt = originalAltText;
|
|
|
|
if (result.item) {
|
|
console.log(`Got CacheResult from cache: ${result.fromCache}, src: ${result.item?.metadata.key}`);
|
|
imageElement.src = result.item.filePath;
|
|
}
|
|
else
|
|
console.error("Failed to fetch cache", result.error);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @todo
|
|
* @see {@link https://lucide.dev/icons/loader}
|
|
*/
|
|
static readonly SVG_LOADING_ICON = `
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="24"
|
|
height="24"
|
|
viewBox="0 0 24 24"
|
|
fill="transparant"
|
|
stroke="currentColor"
|
|
stroke-width="1"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<path d="M12 2v4" />
|
|
<path d="m16.2 7.8 2.9-2.9" />
|
|
<path d="M18 12h4" />
|
|
<path d="m16.2 16.2 2.9 2.9" />
|
|
<path d="M12 18v4" />
|
|
<path d="m4.9 19.1 2.9-2.9" />
|
|
<path d="M2 12h4" />
|
|
<path d="m4.9 4.9 2.9 2.9" />
|
|
</svg>
|
|
`;
|
|
|
|
static readonly ENCODED_LOADING_ICON = encodeURIComponent(PluginImplementation.SVG_LOADING_ICON);
|
|
}
|
|
|