mirror of
https://github.com/devon22/obsidian-mediaviewer.git
synced 2026-07-22 05:35:13 +00:00
2.2.0
This commit is contained in:
parent
e71cdf29da
commit
a0c15ccacd
6 changed files with 237 additions and 12 deletions
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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': 'ノートの名前を検索...',
|
||||
|
||||
|
|
|
|||
12
styles.css
12
styles.css
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue