diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 0c38f47..4b6ad4b 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -15,7 +15,7 @@ const context = await esbuild.context({ banner: { js: banner, }, - entryPoints: ["main.ts"], + entryPoints: ["src/main.ts"], bundle: true, external: [ "obsidian", diff --git a/manifest.json b/manifest.json index 346d570..d0ce4bc 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "mediaviewer", "name": "Media Viewer", - "version": "1.9.12", + "version": "1.9.13", "minAppVersion": "1.1.0", "description": "View and manage media files within your notes.", "author": "Devon22", diff --git a/package-lock.json b/package-lock.json index 3c95adc..670bb31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mediaviewer", - "version": "1.9.12", + "version": "1.9.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mediaviewer", - "version": "1.9.12", + "version": "1.9.13", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index 2c70d5a..9f968f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mediaviewer", - "version": "1.9.12", + "version": "1.9.13", "description": "This is a sample plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/src/fullscreen.ts b/src/fullscreen.ts index 1d96827..5ea0a15 100644 --- a/src/fullscreen.ts +++ b/src/fullscreen.ts @@ -1,5 +1,5 @@ import { App, Modal, getFrontMatterInfo, TFile, Notice } from 'obsidian'; -import MediaViewPlugin from '../main'; +import MediaViewPlugin from './main'; import { MediaViewSettings } from './settings'; import { t } from './translations'; @@ -25,8 +25,9 @@ export class FullScreenModal extends Modal { handleWheel: ((event: WheelEvent) => void) | null; autoPlayTimer: number | null; isAutoPlaying: boolean; + sourcePath?: string; // 來源檔案路徑(由 Gallery Block 傳入) - constructor(app: App, plugin: MediaViewPlugin, openType = 'command') { + constructor(app: App, plugin: MediaViewPlugin, openType: string = 'command', sourcePath?: string) { super(app); this.plugin = plugin; this.mediaUrls = []; @@ -38,11 +39,16 @@ export class FullScreenModal extends Modal { this.handleWheel = null; //儲存滾輪事件處理程序 this.autoPlayTimer = null; //儲存自動播放計時器 this.isAutoPlaying = false; //自動播放狀態 + this.sourcePath = sourcePath; } async scanMedia(): Promise { - // 獲取當前活動的 markdown 文件 - const activeFile = this.app.workspace.getActiveFile(); + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return []; @@ -507,7 +513,6 @@ export class FullScreenModal extends Modal { } registerMediaEvents() { - // 點擊預覽區域背景時關閉 this.fullMediaView.onclick = (event) => { // 檢查點擊的是否為預覽區域本身或其直接子元素 @@ -521,19 +526,23 @@ export class FullScreenModal extends Modal { // 圖片點擊事件(放大) this.fullImage.onclick = (event) => { - // 阻止事件冒泡,避免觸發外層的點擊事件 event.stopPropagation(); - // if (this.plugin.settings.displayOriginalSize && this.fullImage.naturalWidth < this.fullMediaView.clientWidth && this.fullImage.naturalHeight < this.fullMediaView.clientHeight) { - return; + return; } if (event.target === this.fullImage) { if (!this.isZoomed) { // 縮放 + // 計算點擊位置相對於圖片的百分比 + const rect = this.fullImage.getBoundingClientRect(); + const clickX = event.clientX - rect.left; + const clickY = event.clientY - rect.top; + const clickXPercent = clickX / rect.width; + const clickYPercent = clickY / rect.height; if (this.fullMediaView.clientWidth > this.fullMediaView.clientHeight) { if (this.fullImage.naturalHeight < this.fullMediaView.clientHeight) { @@ -557,9 +566,9 @@ export class FullScreenModal extends Modal { this.fullMediaView.style.overflowY = 'hidden'; // 將事件處理程序存儲在類別屬性中 - this.handleWheel = (event) => { - event.preventDefault(); - this.fullMediaView.scrollLeft += event.deltaY; + this.handleWheel = (e) => { + e.preventDefault(); + this.fullMediaView.scrollLeft += e.deltaY; }; this.fullMediaView.addEventListener('wheel', this.handleWheel); } @@ -573,6 +582,20 @@ export class FullScreenModal extends Modal { this.fullImage.style.transform = 'none'; this.fullImage.style.cursor = 'zoom-out'; this.isZoomed = true; + + // 在下一幀執行滾動,確保圖片已經放大 + requestAnimationFrame(() => { + if (this.fullMediaView.scrollWidth > this.fullMediaView.clientWidth) { + // 水平滾動到點擊位置 + const scrollX = (this.fullImage.scrollWidth * clickXPercent) - (this.fullMediaView.clientWidth / 2); + this.fullMediaView.scrollLeft = Math.max(0, Math.min(scrollX, this.fullImage.scrollWidth - this.fullMediaView.clientWidth)); + } + if (this.fullMediaView.scrollHeight > this.fullMediaView.clientHeight) { + // 垂直滾動到點擊位置 + const scrollY = (this.fullImage.scrollHeight * clickYPercent) - (this.fullMediaView.clientHeight / 2); + this.fullMediaView.scrollTop = Math.max(0, Math.min(scrollY, this.fullImage.scrollHeight - this.fullMediaView.clientHeight)); + } + }); } else { this.resetImageStyles(); } @@ -634,7 +657,12 @@ export class FullScreenModal extends Modal { const media = this.mediaUrls[index]; - const activeFile = this.app.workspace.getActiveFile(); + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { return; } diff --git a/src/galleryBlock.ts b/src/galleryBlock.ts index 93c39a7..e7e6668 100644 --- a/src/galleryBlock.ts +++ b/src/galleryBlock.ts @@ -1,5 +1,5 @@ import { App, TFile, Menu, Notice, Platform } from 'obsidian'; -import MediaViewPlugin from '../main'; +import MediaViewPlugin from './main'; import { FullScreenModal } from './fullscreen'; import { MediaViewSettings } from './settings'; import { ImageUploadModal } from './imageUpload'; @@ -42,6 +42,7 @@ interface MediaUrlsData { export class GalleryBlock { app: App; plugin: MediaViewPlugin; + sourcePath?: string; // 來源檔案路徑(由 Gallery Block 傳入) constructor(app: App, plugin: MediaViewPlugin) { this.app = app; @@ -49,7 +50,9 @@ export class GalleryBlock { } // 處理 gallery 區塊 - async processGalleryBlock(source: string, el: HTMLElement): Promise { + async processGalleryBlock(source: string, el: HTMLElement, sourcePath?: string): Promise { + this.sourcePath = sourcePath; // 記錄來源路徑,方便後續開啟全螢幕時使用 + if (el.querySelector('.mvgb-media-gallery-grid')) { return; } @@ -500,7 +503,7 @@ export class GalleryBlock { addIcon.appendChild(addIconText); addContainer.onclick = () => { - const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv); + const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv, this.sourcePath); modal.open(); }; @@ -562,7 +565,7 @@ export class GalleryBlock { text: t('add_image') }); addButton.onclick = () => { - const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv); + const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv, this.sourcePath); modal.open(); }; } @@ -623,7 +626,7 @@ export class GalleryBlock { } const resolvedFiles = await Promise.all(files); - const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv); + const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv, this.sourcePath); await modal.handleFiles(resolvedFiles); }); @@ -639,7 +642,7 @@ export class GalleryBlock { .setTitle(t('add_image')) .setIcon("image") .onClick(() => { - const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv); + const modal = new ImageUploadModal(this.app, this.plugin, galleryDiv, this.sourcePath); modal.open(); }); }); @@ -655,7 +658,12 @@ export class GalleryBlock { } // 獲取當前筆記的文件 - const activeFile = this.app.workspace.getActiveFile(); + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return; @@ -713,7 +721,12 @@ export class GalleryBlock { } // 獲取當前筆記的文件 - const activeFile = this.app.workspace.getActiveFile(); + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return; @@ -992,7 +1005,7 @@ export class GalleryBlock { if ((!this.plugin.settings.disableClickToOpenMediaOnGallery && media.type === 'image') || media.type === 'video') { container.onclick = () => { - const modal = new FullScreenModal(this.app, this.plugin, 'thumbnail'); + const modal = new FullScreenModal(this.app, this.plugin, 'thumbnail', this.sourcePath); modal.open(); setTimeout(() => { const allUrls = modal.mediaUrls; diff --git a/src/imageUpload.ts b/src/imageUpload.ts index 7da7e55..49e9c7b 100644 --- a/src/imageUpload.ts +++ b/src/imageUpload.ts @@ -1,17 +1,19 @@ import { App, Modal, TFile, Notice } from 'obsidian'; -import MediaViewPlugin from '../main'; +import MediaViewPlugin from './main'; import { t } from './translations'; export class ImageUploadModal extends Modal { plugin: MediaViewPlugin; galleryElement: HTMLElement; insertAtEnd: boolean; // 新增變數來儲存插入位置選項 + sourcePath?: string; // 新增來源路徑 - constructor(app: App, plugin: MediaViewPlugin, galleryElement: HTMLElement) { + constructor(app: App, plugin: MediaViewPlugin, galleryElement: HTMLElement, sourcePath?: string) { super(app); this.plugin = plugin; this.galleryElement = galleryElement; // 儲存觸發上傳的 gallery 元素 this.insertAtEnd = this.plugin.settings.insertAtEnd; // 初始化為設定中的值 + this.sourcePath = sourcePath; // 儲存來源路徑 } onOpen() { @@ -66,7 +68,13 @@ export class ImageUploadModal extends Modal { if (imageType) { const blob = await item.getType(imageType); - const activeFile = this.app.workspace.getActiveFile(); + // 嘗試取得目前的 Markdown 檔案 + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return; @@ -176,7 +184,13 @@ export class ImageUploadModal extends Modal { } async handleFiles(files: File[]): Promise { - const activeFile = this.app.workspace.getActiveFile(); + // 嘗試取得目前的 Markdown 檔案 + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return; @@ -331,7 +345,13 @@ export class ImageUploadModal extends Modal { } async handleLinks(links: string[]): Promise { - const activeFile = this.app.workspace.getActiveFile(); + // 嘗試取得目前的 Markdown 檔案 + // 優先使用來源路徑,若無則使用當前開啟的檔案 + let activeFile: TFile | null = null; + activeFile = this.sourcePath + ? this.app.vault.getAbstractFileByPath(this.sourcePath) as TFile | null + : this.app.workspace.getActiveFile(); + if (!activeFile) { new Notice(t('please_open_note')); return; diff --git a/main.ts b/src/main.ts similarity index 94% rename from main.ts rename to src/main.ts index abd45d8..efd144a 100644 --- a/main.ts +++ b/src/main.ts @@ -1,9 +1,9 @@ import { Plugin, Notice, MarkdownView } from 'obsidian'; -import { MediaViewSettingTab, DEFAULT_SETTINGS, MediaViewSettings } from './src/settings'; -import { FullScreenModal } from './src/fullscreen'; -import { GalleryBlock } from './src/galleryBlock'; -import { GalleryBlockGenerateModal } from './src/galleryBlockGenerate'; -import { t } from './src/translations'; +import { MediaViewSettingTab, DEFAULT_SETTINGS, MediaViewSettings } from './settings'; +import { FullScreenModal } from './fullscreen'; +import { GalleryBlock } from './galleryBlock'; +import { GalleryBlockGenerateModal } from './galleryBlockGenerate'; +import { t } from './translations'; export default class MediaViewPlugin extends Plugin { settings: MediaViewSettings; @@ -98,7 +98,7 @@ export default class MediaViewPlugin extends Plugin { // 統一使用 registerMarkdownCodeBlockProcessor 來處理兩種模式 this.registerMarkdownCodeBlockProcessor("gallery", (source, el, ctx) => - galleryProcessor.processGalleryBlock(source, el) + galleryProcessor.processGalleryBlock(source, el, ctx?.sourcePath) ); }); } diff --git a/src/settings.ts b/src/settings.ts index 47ff7b0..c264a97 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,5 +1,5 @@ import { App, PluginSettingTab, Setting } from 'obsidian'; -import MediaViewPlugin from '../main'; +import MediaViewPlugin from './main'; import { t } from './translations'; export interface MediaViewSettings { diff --git a/versions.json b/versions.json index 465fe6c..7a226ce 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "1.9.12": "1.1.0" + "1.9.13": "1.1.0" } \ No newline at end of file