This commit is contained in:
Devon22 2025-03-15 00:47:00 +08:00
parent de2368af36
commit 03f2b0278d
10 changed files with 1195 additions and 952 deletions

11
main.ts
View file

@ -105,7 +105,16 @@ export default class MediaViewPlugin extends Plugin {
if (activeView) {
const editor = activeView.editor;
const selectedText = editor.getSelection();
new GalleryBlockGenerateModal(this.app, editor, selectedText).open();
//new GalleryBlockGenerateModal(this.app, selectedText).open();
// 創建並打開設定對話框
const modal = new GalleryBlockGenerateModal(this.app, selectedText);
// 設置確認後的回調函數,使用 vault.process 修改文件
modal.onConfirm = (newGalleryBlock: string) => {
editor.replaceSelection(newGalleryBlock);
};
modal.open();
} else {
new Notice(t('please_open_note'));
}

View file

@ -1,7 +1,7 @@
{
"id": "mediaviewer",
"name": "Media Viewer",
"version": "1.8.11",
"version": "1.8.12",
"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.8.11",
"version": "1.8.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mediaviewer",
"version": "1.8.11",
"version": "1.8.12",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",

View file

@ -1,6 +1,6 @@
{
"name": "mediaviewer",
"version": "1.8.11",
"version": "1.8.12",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {

File diff suppressed because it is too large Load diff

View file

@ -1,88 +1,139 @@
import { App, Modal,Setting, Editor} from 'obsidian';
import { t } from './translations';
// Gallery 生成對話框
export class GalleryBlockGenerateModal extends Modal {
selectedText: string;
title: string;
size: string;
addButton: boolean;
pagination: number;
editor: Editor;
constructor(app: App, editor: Editor, selectedText: string) {
super(app);
this.editor = editor;
this.selectedText = selectedText;
this.title = '';
this.size = 'medium';
this.addButton = true;
this.pagination = 0;
}
onOpen() {
const { contentEl } = this;
// 標題
new Setting(contentEl)
.setName(t('gallery_title'))
.setDesc(t('gallery_title_desc'))
.addText(text => text
.setValue(this.title)
.onChange(value => this.title = value));
// 尺寸選擇
new Setting(contentEl)
.setName(t('gallery_size'))
.setDesc(t('gallery_size_desc'))
.addDropdown(dropdown => dropdown
.addOption('small', t('grid_size_small'))
.addOption('medium', t('grid_size_medium'))
.addOption('large', t('grid_size_large'))
.setValue(this.size)
.onChange(value => this.size = value));
// 上傳按鈕選項
new Setting(contentEl)
.setName(t('gallery_add_button'))
.setDesc(t('gallery_add_button_desc'))
.addToggle(toggle => toggle
.setValue(this.addButton)
.onChange(value => this.addButton = value));
// 分頁設定
new Setting(contentEl)
.setName(t('gallery_pagination'))
.setDesc(t('gallery_pagination_desc'))
.addText(text => text
.setValue(this.pagination ? this.pagination.toString() : '')
.setPlaceholder('0')
.onChange(value => {
const numValue = parseInt(value);
this.pagination = !isNaN(numValue) && numValue > 0 ? numValue : 0;
}));
// 確認按鈕
new Setting(contentEl)
.addButton(button => button
.setButtonText(t('confirm'))
.setCta()
.onClick(() => {
const galleryBlock = ['```gallery'];
galleryBlock.push(`title: ${this.title}`);
galleryBlock.push(`size: ${this.size}`);
galleryBlock.push(`addButton: ${this.addButton}`);
galleryBlock.push(`pagination: ${this.pagination}`);
if (this.selectedText) galleryBlock.push(this.selectedText);
galleryBlock.push('```\n');
this.editor.replaceSelection(galleryBlock.join('\n'));
this.close();
}));
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
import { App, Modal,Setting, EditorPosition} from 'obsidian';
import { t } from './translations';
// Gallery 生成對話框
export class GalleryBlockGenerateModal extends Modal {
selectedText: string;
title: string;
size: string;
addButton: boolean;
pagination: number;
replaceRange: { from: EditorPosition, to: EditorPosition } | null;
onConfirm: ((newGalleryBlock: string) => void) | null;
constructor(app: App, selectedText: string) {
super(app);
this.selectedText = this.parseExistingContent(selectedText);
this.replaceRange = null;
this.onConfirm = null;
if (this.title === undefined) this.title = '';
if (this.size === undefined) this.size = 'medium';
if (this.addButton === undefined) this.addButton = false;
if (this.pagination === undefined) this.pagination = 0;
}
// 解析現有的 gallery 區塊內容
parseExistingContent(content: string): string {
const lines = content.split('\n');
let mediaItems = '';
for (const line of lines) {
const trimmedLine = line.trim();
// 解析標題
const titleMatch = trimmedLine.match(/^title:\s*(.+)$/);
if (titleMatch) {
this.title = titleMatch[1];
continue;
}
// 解析尺寸
const sizeMatch = trimmedLine.match(/^size:\s*(small|medium|large)$/i);
if (sizeMatch) {
this.size = sizeMatch[1].toLowerCase();
continue;
}
// 解析新增按鈕設定
const addButtonMatch = trimmedLine.match(/^addbutton:\s*(true|false)$/i);
if (addButtonMatch) {
this.addButton = addButtonMatch[1].toLowerCase() === 'true';
continue;
}
// 解析分頁設定
const paginationMatch = trimmedLine.match(/^pagination:\s*(\d+)$/i);
if (paginationMatch) {
this.pagination = parseInt(paginationMatch[1]) || 0;
continue;
}
// 將非設定行收集為媒體項目
if (!trimmedLine.match(/^(title|size|addbutton|pagination):/i) && trimmedLine) {
mediaItems += line + '\n';
}
}
return mediaItems.trim();
}
onOpen() {
const { contentEl } = this;
// 標題
new Setting(contentEl)
.setName(t('gallery_title'))
.setDesc(t('gallery_title_desc'))
.addText(text => text
.setValue(this.title || '')
.onChange(value => this.title = value));
// 尺寸選擇
new Setting(contentEl)
.setName(t('gallery_size'))
.setDesc(t('gallery_size_desc'))
.addDropdown(dropdown => dropdown
.addOption('small', t('grid_size_small'))
.addOption('medium', t('grid_size_medium'))
.addOption('large', t('grid_size_large'))
.setValue(this.size)
.onChange(value => this.size = value));
// 上傳按鈕選項
new Setting(contentEl)
.setName(t('gallery_add_button'))
.setDesc(t('gallery_add_button_desc'))
.addToggle(toggle => toggle
.setValue(this.addButton)
.onChange(value => this.addButton = value));
// 分頁設定
new Setting(contentEl)
.setName(t('gallery_pagination'))
.setDesc(t('gallery_pagination_desc'))
.addText(text => text
.setValue(this.pagination ? this.pagination.toString() : '')
.setPlaceholder('0')
.onChange(value => {
const numValue = parseInt(value);
this.pagination = !isNaN(numValue) && numValue > 0 ? numValue : 0;
}));
// 確認按鈕
new Setting(contentEl)
.addButton(button => button
.setButtonText(t('confirm'))
.setCta()
.onClick(() => {
const galleryBlock = ['```gallery'];
galleryBlock.push(`title: ${this.title}`);
galleryBlock.push(`size: ${this.size}`);
galleryBlock.push(`addButton: ${this.addButton}`);
galleryBlock.push(`pagination: ${this.pagination}`);
if (this.selectedText) galleryBlock.push(this.selectedText);
galleryBlock.push('```\n');
const newGalleryBlockContent = galleryBlock.join('\n');
if (this.onConfirm) {
this.onConfirm(newGalleryBlockContent.trim());
}
this.close();
}));
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}

View file

@ -5,10 +5,13 @@ import { t } from './translations';
export class ImageUploadModal extends Modal {
plugin: MediaViewPlugin;
galleryElement: HTMLElement;
insertAtEnd: boolean; // 新增變數來儲存插入位置選項
constructor(app: App, plugin: MediaViewPlugin, galleryElement: HTMLElement) {
super(app);
this.plugin = plugin;
this.galleryElement = galleryElement; // 儲存觸發上傳的 gallery 元素
this.insertAtEnd = this.plugin.settings.insertAtEnd; // 初始化為設定中的值
}
onOpen() {
@ -32,44 +35,6 @@ export class ImageUploadModal extends Modal {
const instructions = dropZone.createDiv('mvgb-upload-instructions');
instructions.setText(t('drag_and_drop'));
// 處理拖放事件
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.addClass('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.removeClass('drag-over');
});
dropZone.addEventListener('drop', async (e) => {
e.preventDefault();
dropZone.removeClass('drag-over');
if (e.dataTransfer === null) return;
const files = (e.dataTransfer.files as any);
await this.handleFiles(files);
});
const fileInput = contentEl.createEl('input', {
type: 'file',
attr: {
accept: 'image/*,video/*',
multiple: true,
style: 'display: none;' // 隱藏但保持在DOM中
}
});
fileInput.addEventListener('change', async () => {
if (fileInput.files && fileInput.files.length > 0) {
await this.handleFiles(Array.from(fileInput.files));
}
});
dropZone.addEventListener('click', () => {
fileInput.click();
});
// 新增貼上剪貼簿按鈕
const pasteButton = contentEl.createEl('button', {
text: t('paste_from_clipboard'),
@ -133,6 +98,81 @@ export class ImageUploadModal extends Modal {
console.error('剪貼簿讀取錯誤:', err);
}
});
// 新增插入位置選項
const insertPositionContainer = contentEl.createDiv('mvgb-insert-position-container');
insertPositionContainer.addClass('mvgb-setting-item');
const insertPositionLabel = insertPositionContainer.createDiv('mvgb-setting-label');
insertPositionLabel.setText(t('insert_position'));
const insertPositionControl = insertPositionContainer.createDiv('mvgb-setting-control');
// 建立下拉選單
const insertPositionDropdown = insertPositionControl.createEl('select', {
cls: 'mvgb-insert-position-dropdown'
});
// 添加選項
const endOption = insertPositionDropdown.createEl('option', {
text: t('insert_at_end'),
value: 'true'
});
const startOption = insertPositionDropdown.createEl('option', {
text: t('insert_at_start'),
value: 'false'
});
// 設定初始選擇的選項
if (this.insertAtEnd) {
endOption.selected = true;
} else {
startOption.selected = true;
}
// 添加變更事件
insertPositionDropdown.addEventListener('change', () => {
this.insertAtEnd = insertPositionDropdown.value === 'true';
});
// 處理拖放事件
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.addClass('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.removeClass('drag-over');
});
dropZone.addEventListener('drop', async (e) => {
e.preventDefault();
dropZone.removeClass('drag-over');
if (e.dataTransfer === null) return;
const files = (e.dataTransfer.files as any);
await this.handleFiles(files);
});
const fileInput = contentEl.createEl('input', {
type: 'file',
attr: {
accept: 'image/*,video/*',
multiple: true,
style: 'display: none;' // 隱藏但保持在DOM中
}
});
fileInput.addEventListener('change', async () => {
if (fileInput.files && fileInput.files.length > 0) {
await this.handleFiles(Array.from(fileInput.files));
}
});
dropZone.addEventListener('click', () => {
fileInput.click();
});
}
async handleFiles(files: File[]): Promise<void> {
@ -258,8 +298,8 @@ export class ImageUploadModal extends Modal {
// 如果這個 gallery 區塊的 ID 與觸發上傳的元素相同
if (currentGalleryId === galleryId) {
// 在 gallery 區塊內容的最前或最後一行插入新連結 (根據設定)
const newBlockContent = this.plugin.settings.insertAtEnd
// 在 gallery 區塊內容的最前或最後一行插入新連結 (根據選擇的選項)
const newBlockContent = this.insertAtEnd
? blockContent.trimEnd() + `\n${newLinks.join('\n')}\n`
: `${newLinks.join('\n')}\n` + blockContent + '\n';
@ -314,8 +354,8 @@ export class ImageUploadModal extends Modal {
// 如果這個 gallery 區塊的 ID 與觸發上傳的元素相同
if (currentGalleryId === galleryId) {
// 在 gallery 區塊內容的最前或最後一行插入新連結 (根據設定)
const newBlockContent = this.plugin.settings.insertAtEnd
// 在 gallery 區塊內容的最前或最後一行插入新連結 (根據選擇的選項)
const newBlockContent = this.insertAtEnd
? blockContent.trimEnd() + `\n${links.join('\n')}\n`
: `${links.join('\n')}\n` + blockContent + '\n';
@ -355,7 +395,7 @@ export class ImageUploadModal extends Modal {
getSafeFileName(originalName: string) {
// 移除不安全的字元,只保留字母、數字、底線和檔案副檔名
const name = originalName.replace(/[<>:"/\\|?*]/g, '_');
const name = originalName.replace(/[#<>:"/\\|?*]/g, '_');
// 確保檔案名稱唯一
return name;
}

View file

@ -73,6 +73,7 @@ const TRANSLATIONS: Translations = {
// 命令
'open_media_viewer': '打開媒體瀏覽器',
'generate_gallery': '生成 Gallery 區塊',
'setting_gallery': '設定 Gallery 區塊',
'gallery_title': 'Gallery 標題',
'gallery_title_desc': '設定 Gallery 的標題(選填)',
'gallery_size': 'Gallery 尺寸',
@ -133,6 +134,7 @@ const TRANSLATIONS: Translations = {
// Commands
'open_media_viewer': 'Open media viewer',
'generate_gallery': 'Generate gallery block',
'setting_gallery': 'Setting gallery block',
'gallery_title': 'Gallery title',
'gallery_title_desc': 'Set the title of the gallery (optional)',
'gallery_size': 'Gallery size',
@ -193,6 +195,7 @@ const TRANSLATIONS: Translations = {
// 命令
'open_media_viewer': '打开媒体浏览器',
'generate_gallery': '生成 Gallery 区块',
'setting_gallery': '设置 Gallery 区块',
'gallery_title': '相册标题',
'gallery_title_desc': '设置相册的标题(选填)',
'gallery_size': '相册尺寸',
@ -253,6 +256,7 @@ const TRANSLATIONS: Translations = {
// コマンド
'open_media_viewer': 'メディアビューワーを開く',
'generate_gallery': 'ギャラリーブロックを生成',
'setting_gallery': 'ギャラリーブロックを設定',
'gallery_title': 'ギャラリータイトル',
'gallery_title_desc': 'ギャラリーのタイトルを設定(オプション)',
'gallery_size': 'ギャラリーサイズ',

View file

@ -598,4 +598,68 @@
height: 48px;
color: var(--text-muted);
margin-bottom: 10px;
}
}
.mvgb-paste-button {
display: block;
width: 100%;
padding: 4px;
margin-top: 15px;
background-color: var(--interactive-accent);
color: var(--text-on-accent);
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
}
.mvgb-paste-button:hover {
background-color: var(--interactive-accent-hover);
}
/* 上傳檔案的位置選擇 */
.mvgb-insert-position-container {
margin-top: 15px;
display: flex;
align-items: center;
justify-content: space-between;
}
.mvgb-setting-item {
display: flex;
align-items: center;
padding: 8px 0;
}
.mvgb-setting-label {
flex: 0 0 100px;
font-weight: 500;
color: var(--text-normal);
}
.mvgb-setting-control {
flex: 1;
}
.mvgb-insert-position-dropdown {
width: 50%;
padding: 4px 8px;
border-radius: 4px;
border: 1px solid var(--background-modifier-border);
background-color: var(--background-primary);
color: var(--text-normal);
font-size: 14px;
transition: border-color 0.2s ease;
}
.mvgb-insert-position-dropdown:focus {
border-color: var(--interactive-accent);
outline: none;
}
.mvgb-insert-position-dropdown option {
background-color: var(--background-primary);
color: var(--text-normal);
}

View file

@ -1,3 +1,3 @@
{
"1.8.11": "1.1.0"
"1.8.12": "1.1.0"
}