mirror of
https://github.com/devon22/obsidian-mediaviewer.git
synced 2026-07-22 11:50:28 +00:00
1.8.12
This commit is contained in:
parent
de2368af36
commit
03f2b0278d
10 changed files with 1195 additions and 952 deletions
11
main.ts
11
main.ts
|
|
@ -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'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
1705
src/galleryBlock.ts
1705
src/galleryBlock.ts
File diff suppressed because it is too large
Load diff
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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': 'ギャラリーサイズ',
|
||||
|
|
|
|||
66
styles.css
66
styles.css
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"1.8.11": "1.1.0"
|
||||
"1.8.12": "1.1.0"
|
||||
}
|
||||
Loading…
Reference in a new issue