This commit is contained in:
Devon22 2025-07-20 23:37:17 +08:00
parent 7602e34d29
commit b6fe9e5dec
10 changed files with 100 additions and 39 deletions

View file

@ -15,7 +15,7 @@ const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
entryPoints: ["src/main.ts"],
bundle: true,
external: [
"obsidian",

View file

@ -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",

4
package-lock.json generated
View file

@ -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",

View file

@ -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": {

View file

@ -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<Media[]> {
// 獲取當前活動的 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;
}

View file

@ -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<void> {
async processGalleryBlock(source: string, el: HTMLElement, sourcePath?: string): Promise<void> {
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;

View file

@ -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<void> {
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<void> {
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;

View file

@ -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)
);
});
}

View file

@ -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 {

View file

@ -1,3 +1,3 @@
{
"1.9.12": "1.1.0"
"1.9.13": "1.1.0"
}