This commit is contained in:
Devon22 2026-07-19 12:45:48 +08:00
parent e71cdf29da
commit a0c15ccacd
6 changed files with 237 additions and 12 deletions

View file

@ -1,7 +1,7 @@
{
"id": "mediaviewer",
"name": "Media Viewer",
"version": "2.1.13",
"version": "2.2.0",
"minAppVersion": "1.8.7",
"description": "Transform your notes with beautiful interactive media galleries. Seamlessly view, zoom, and manage images, videos, and audio files.",
"author": "Devon22",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "mediaviewer",
"version": "2.1.13",
"version": "2.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mediaviewer",
"version": "2.1.13",
"version": "2.2.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",

View file

@ -1,6 +1,6 @@
{
"name": "mediaviewer",
"version": "2.1.13",
"version": "2.2.0",
"description": "Transform your notes with beautiful interactive media galleries. Seamlessly view, zoom, and manage images, videos, and audio files.",
"main": "main.js",
"scripts": {

View file

@ -24,17 +24,19 @@ class RenameModal extends Modal {
const { contentEl } = this;
contentEl.createEl('h2', { text: t('rename') });
new Setting(contentEl)
const inputSetting = new Setting(contentEl)
.setName(t('new_filename'))
.addText(text => text
.setValue(this.currentName)
.onChange(value => this.newName = value)
.inputEl.addEventListener('keydown', (e) => {
.addText(text => {
text.setValue(this.currentName)
.onChange(value => this.newName = value);
text.inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
this.onConfirm(this.newName);
this.close();
}
}));
});
});
inputSetting.settingEl.addClass('mv-settings-input-flex');
new Setting(contentEl)
.addButton(button => button
@ -55,6 +57,75 @@ class RenameModal extends Modal {
}
}
// 編輯 Alt 對話框
class AltModal extends Modal {
altText: string;
onConfirm: (altText: string) => void;
onDelete?: () => void;
currentAlt: string;
isEdit: boolean;
constructor(app: App, currentAlt: string, isEdit: boolean, onConfirm: (altText: string) => void, onDelete?: () => void) {
super(app);
this.currentAlt = currentAlt;
this.altText = currentAlt;
this.isEdit = isEdit;
this.onConfirm = onConfirm;
this.onDelete = onDelete;
}
onOpen() {
const { contentEl } = this;
contentEl.createEl('h2', { text: this.isEdit ? t('edit_alt') || 'Edit alt' : t('add_alt') || 'Add alt' });
const inputSetting = new Setting(contentEl)
.setName('Alt')
.addText(text => {
text.setValue(this.currentAlt)
.onChange(value => this.altText = value);
text.inputEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
this.onConfirm(this.altText);
this.close();
}
});
});
inputSetting.settingEl.addClass('mv-settings-input-flex');
const buttonsSetting = new Setting(contentEl);
if (this.isEdit && this.onDelete) {
buttonsSetting.addButton(button => button
.setButtonText(t('delete') || 'Delete')
.setWarning()
.onClick(() => {
if (this.onDelete) {
this.onDelete();
}
this.close();
}));
}
buttonsSetting.addButton(button => button
.setButtonText(t('confirm'))
.setCta()
.onClick(() => {
this.onConfirm(this.altText);
this.close();
}));
buttonsSetting.addButton(button => button
.setButtonText(t('cancel') || 'Cancel')
.onClick(() => this.close()));
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}
interface GalleryItem {
type: string;
url?: string;
@ -712,11 +783,11 @@ export class GalleryBlock {
}
const appWithPlugins = this.app as ObsidianAppWithPlugins;
const zipPlugin = appWithPlugins.plugins?.plugins?.['gridexplorer'];
if (!zipPlugin || typeof zipPlugin.getThumbnail !== 'function') {
return null;
}
return await zipPlugin.getThumbnail(zipPath);
}
@ -1240,6 +1311,7 @@ export class GalleryBlock {
}
this.addCopyAltMenuItem(menu, item);
this.addEditAltMenuItem(menu, item, galleryId, sourcePath, container);
if (item.file) {
menu.addItem((menuItem) => {
@ -1641,6 +1713,7 @@ export class GalleryBlock {
}
this.addCopyAltMenuItem(menu, media);
this.addEditAltMenuItem(menu, media, galleryId, sourcePath, container);
if (media.file) {
menu.addItem((menuItem) => {
@ -1712,6 +1785,43 @@ export class GalleryBlock {
});
}
private addEditAltMenuItem(menu: Menu, item: GalleryItem, galleryId: string, sourcePath?: string, container?: HTMLElement) {
if (!item.loc) {
return;
}
const hasAlt = !!(item.altText && item.altText.trim() !== '');
menu.addItem((menuItem) => {
menuItem
.setTitle(hasAlt ? (t('edit_alt') || 'Edit alt') : (t('add_alt') || 'Add alt'))
.setIcon("pencil")
.onClick(() => {
let currentPage = 1;
if (container) {
const galleryDiv = container.closest('.mvgb-media-gallery-grid');
if (galleryDiv) {
const paginationDiv = galleryDiv.parentElement?.querySelector('.mvgb-pagination');
if (paginationDiv?.instanceOf(HTMLElement) && paginationDiv.dataset.currentPage) {
currentPage = parseInt(paginationDiv.dataset.currentPage);
}
}
}
const modal = new AltModal(
this.app,
item.altText || '',
hasAlt,
(newAlt) => {
void this.editGalleryItemAlt(galleryId, item, false, newAlt, sourcePath, currentPage);
},
() => {
void this.editGalleryItemAlt(galleryId, item, true, '', sourcePath, currentPage);
}
);
modal.open();
});
});
}
private addGalleryBlockMenuItems(menu: Menu, galleryDiv: HTMLElement, galleryId: string, sourcePath?: string) {
menu.addItem((item) => {
item
@ -2188,4 +2298,87 @@ export class GalleryBlock {
});
modal.open();
}
async editGalleryItemAlt(galleryId: string, item: GalleryItem, isDelete: boolean, newAlt: string, sourcePath?: string, currentPage?: number) {
let activeFile: TFile | null = null;
if (sourcePath) {
activeFile = this.app.vault.getAbstractFileByPath(sourcePath) as TFile | null;
}
if (!activeFile) {
activeFile = this.app.workspace.getActiveFile();
}
if (!activeFile) {
new Notice(t('please_open_note') || 'Please open a note first');
return;
}
try {
const content = await this.app.vault.read(activeFile);
const galleryBlockRegex = /```gallery\r?\n([\s\S]*?)```/g;
let match;
while ((match = galleryBlockRegex.exec(content)) !== null) {
const blockContent = match[1];
const blockId = 'gallery-' + this.hashString(blockContent.trim());
if (blockId === galleryId) {
if (item.loc) {
const lines = blockContent.split('\n');
const start = item.loc.start;
const end = item.loc.end;
let hasAltLine = false;
let altIndex = -1;
for (let i = start; i <= end; i++) {
if (/^\s*alt:\s*(.*)$/i.test(lines[i])) {
hasAltLine = true;
altIndex = i;
break;
}
}
if (isDelete) {
if (hasAltLine && altIndex !== -1) {
lines.splice(altIndex, 1);
}
} else {
if (hasAltLine && altIndex !== -1) {
lines[altIndex] = `alt: ${newAlt}`;
} else {
// 原本沒有 alt在項目的最前面插入一行 alt:
lines.splice(start, 0, `alt: ${newAlt}`);
}
}
const newBlockContent = lines.join('\n');
const newId = 'gallery-' + this.hashString(newBlockContent.trim());
if (currentPage) {
GalleryBlock.paginationState.set(newId, currentPage);
}
const matchStart = match.index;
const matchEnd = match.index + match[0].length;
const newBlock = '```gallery\n' + newBlockContent + '```';
const restoreScroll = captureScrollRestore(this.app, galleryId);
await this.app.vault.process(activeFile, (fileContent) => {
return fileContent.substring(0, matchStart) +
newBlock +
fileContent.substring(matchEnd);
});
restoreScroll(newId);
new Notice(isDelete ? (t('alt_removed') || 'Alt text removed') : (t('alt_updated') || 'Alt text updated'));
}
break;
}
}
} catch (error) {
console.error('Error editing alt:', error);
new Notice(t('error_updating_alt') || 'Error updating alt text');
}
}
}

View file

@ -48,6 +48,11 @@ const TRANSLATIONS: Translations = {
'new_filename': '新的檔案名稱',
'copy_alt': '複製 alt 內容',
'alt_copied': '已複製 alt 內容',
'add_alt': '新增 alt',
'edit_alt': '編輯 alt',
'alt_removed': '已移除 alt 內容',
'alt_updated': '已更新 alt 內容',
'error_updating_alt': '更新 alt 內容時發生錯誤',
'choose_note': '選擇筆記',
'choose_note_placeholder': '搜尋筆記名稱...',
@ -132,6 +137,11 @@ const TRANSLATIONS: Translations = {
'new_filename': 'New filename',
'copy_alt': 'Copy alt text',
'alt_copied': 'Alt text copied',
'add_alt': 'Add alt',
'edit_alt': 'Edit alt',
'alt_removed': 'Alt text removed',
'alt_updated': 'Alt text updated',
'error_updating_alt': 'Error updating alt text',
'choose_note': 'Choose Note',
'choose_note_placeholder': 'Search note name...',
@ -216,6 +226,11 @@ const TRANSLATIONS: Translations = {
'new_filename': '新的文件名',
'copy_alt': '复制 alt 内容',
'alt_copied': '已复制 alt 内容',
'add_alt': '新增 alt',
'edit_alt': '编辑 alt',
'alt_removed': '已移除 alt 内容',
'alt_updated': '已更新 alt 内容',
'error_updating_alt': '更新 alt 内容时发生错误',
'choose_note': '选择笔记',
'choose_note_placeholder': '搜索笔记名称...',
@ -298,6 +313,11 @@ const TRANSLATIONS: Translations = {
'new_filename': '新しいファイル名',
'copy_alt': 'alt テキストをコピー',
'alt_copied': 'alt テキストをコピーしました',
'add_alt': 'alt 追加',
'edit_alt': 'alt 編集',
'alt_removed': 'alt テキストを削除しました',
'alt_updated': 'alt テキストを更新しました',
'error_updating_alt': 'alt テキストの更新中にエラーが発生しました',
'choose_note': 'ノートを選択',
'choose_note_placeholder': 'ノートの名前を検索...',

View file

@ -1016,3 +1016,15 @@
width: 20px;
height: 20px;
}
/* Alt & Rename Modals */
.mv-settings-input-flex .setting-item-control {
flex-grow: 1;
}
.mv-settings-input-flex .setting-item-control input[type="text"] {
width: 100% !important;
min-width: 180px;
max-width: 350px;
}