mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 12:00:25 +00:00
Add files via upload
This commit is contained in:
parent
b33234b9c5
commit
9a3a806a39
4 changed files with 202 additions and 131 deletions
BIN
demo1.jpg
BIN
demo1.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 186 KiB |
250
main_src.js
250
main_src.js
|
|
@ -1,4 +1,4 @@
|
|||
const { Plugin, ItemView, TFolder, Setting, Menu } = require('obsidian');
|
||||
const { Plugin, ItemView, TFolder, Setting, Menu, setIcon } = require('obsidian');
|
||||
|
||||
// 語系檔案
|
||||
const TRANSLATIONS = {
|
||||
|
|
@ -38,6 +38,10 @@ const TRANSLATIONS = {
|
|||
'DEFAULT_SORT_TYPE_DESC': '設定開啟網格視圖時的預設排序方式',
|
||||
'GRID_ITEM_WIDTH': '網格項目寬度',
|
||||
'GRID_ITEM_WIDTH_DESC': '設定網格項目的寬度',
|
||||
'IMAGE_AREA_WIDTH': '圖片區域寬度',
|
||||
'IMAGE_AREA_WIDTH_DESC': '設定圖片預覽區域的寬度',
|
||||
'IMAGE_AREA_HEIGHT': '圖片區域高度',
|
||||
'IMAGE_AREA_HEIGHT_DESC': '設定圖片預覽區域的高度',
|
||||
|
||||
// 選擇資料夾對話框
|
||||
'SELECT_FOLDERS': '選擇資料夾',
|
||||
|
|
@ -80,6 +84,10 @@ const TRANSLATIONS = {
|
|||
'DEFAULT_SORT_TYPE_DESC': 'Set the default sorting method when opening Grid View',
|
||||
'GRID_ITEM_WIDTH': 'Grid Item Width',
|
||||
'GRID_ITEM_WIDTH_DESC': 'Set the width of grid items',
|
||||
'IMAGE_AREA_WIDTH': 'Image Area Width',
|
||||
'IMAGE_AREA_WIDTH_DESC': 'Set the width of the image preview area',
|
||||
'IMAGE_AREA_HEIGHT': 'Image Area Height',
|
||||
'IMAGE_AREA_HEIGHT_DESC': 'Set the height of the image preview area',
|
||||
|
||||
// Folder Selection Dialog
|
||||
'SELECT_FOLDERS': 'Select Folder',
|
||||
|
|
@ -122,6 +130,10 @@ const TRANSLATIONS = {
|
|||
'DEFAULT_SORT_TYPE_DESC': '设置开启网格视图时的預设排序方式',
|
||||
'GRID_ITEM_WIDTH': '网格项目宽度',
|
||||
'GRID_ITEM_WIDTH_DESC': '设置网格项目的宽度',
|
||||
'IMAGE_AREA_WIDTH': '圖片區域寬度',
|
||||
'IMAGE_AREA_WIDTH_DESC': '设置圖片預覽區域的寬度',
|
||||
'IMAGE_AREA_HEIGHT': '圖片區域高度',
|
||||
'IMAGE_AREA_HEIGHT_DESC': '设置圖片預覽區域的高度',
|
||||
|
||||
// 选择资料夹对话框
|
||||
'SELECT_FOLDERS': '选择文件夹',
|
||||
|
|
@ -164,6 +176,10 @@ const TRANSLATIONS = {
|
|||
'DEFAULT_SORT_TYPE_DESC': 'グリッドビューを開くときのデフォルトのソート方法を設定します',
|
||||
'GRID_ITEM_WIDTH': 'グリッドアイテムの幅',
|
||||
'GRID_ITEM_WIDTH_DESC': 'グリッドアイテムの幅を設定します',
|
||||
'IMAGE_AREA_WIDTH': '画像エリア幅',
|
||||
'IMAGE_AREA_WIDTH_DESC': '画像プレビュー領域の幅を設定します',
|
||||
'IMAGE_AREA_HEIGHT': '画像エリア高さ',
|
||||
'IMAGE_AREA_HEIGHT_DESC': '画像プレビュー領域の高さを設定します',
|
||||
|
||||
// フォルダ選択ダイアログ
|
||||
'SELECT_FOLDERS': 'フォルダを選択',
|
||||
|
|
@ -256,7 +272,6 @@ class GridView extends ItemView {
|
|||
this.sourceMode = ''; // 預設為書籤模式
|
||||
this.sourcePath = null; // 用於資料夾模式的路徑
|
||||
this.sortType = this.plugin.settings.defaultSortType; // 使用設定中的預設排序模式
|
||||
this.initHeaderButtons();
|
||||
}
|
||||
|
||||
getViewType() {
|
||||
|
|
@ -281,79 +296,6 @@ class GridView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
// 初始化導航列
|
||||
initHeaderButtons() {
|
||||
// 添加排序選單
|
||||
this.sortSelect = this.addAction('sort-desc', t('SORTING'), (evt) => {
|
||||
const menu = new Menu();
|
||||
|
||||
const sortOptions = [
|
||||
{ value: 'name-asc', label: t('SORT_NAME_ASC'), icon: 'sort-ascending' },
|
||||
{ value: 'name-desc', label: t('SORT_NAME_DESC'), icon: 'sort-descending' },
|
||||
{ value: 'mtime-desc', label: t('SORT_MTIME_DESC'), icon: 'clock' },
|
||||
{ value: 'mtime-asc', label: t('SORT_MTIME_ASC'), icon: 'clock' },
|
||||
{ value: 'ctime-desc', label: t('SORT_CTIME_DESC'), icon: 'calendar' },
|
||||
{ value: 'ctime-asc', label: t('SORT_CTIME_ASC'), icon: 'calendar' },
|
||||
{ value: 'random', label: t('SORT_RANDOM'), icon: 'dice' },
|
||||
];
|
||||
|
||||
sortOptions.forEach(option => {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(option.label)
|
||||
.setIcon(option.icon)
|
||||
.setChecked(this.sortType === option.value)
|
||||
.onClick(() => {
|
||||
this.sortType = option.value;
|
||||
this.rerender();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
menu.showAtMouseEvent(evt);
|
||||
});
|
||||
|
||||
// 添加重新選擇位置按鈕
|
||||
this.addAction('folder', t('RESELECT_FOLDER'), () => {
|
||||
showFolderSelectionModal(this.app, this.plugin, this);
|
||||
});
|
||||
|
||||
// 添加重新整理按鈕
|
||||
this.addAction('refresh-cw', t('REFRESH'), () => {
|
||||
this.rerender();
|
||||
});
|
||||
|
||||
// 添加回上層目錄按鈕(僅在資料夾模式且不在根目錄時顯示)
|
||||
this.upSelect = this.addAction('arrow-up', t('GO_UP'), () => {
|
||||
if (this.sourceMode === 'folder' && this.sourcePath !== '/') {
|
||||
const parentPath = this.sourcePath.split('/').slice(0, -1).join('/') || '/';
|
||||
this.setSource('folder', parentPath);
|
||||
}
|
||||
});
|
||||
|
||||
// 根據當前模式設定排序選單的顯示狀態
|
||||
this.updateSortSelectVisibility();
|
||||
}
|
||||
|
||||
// 更新排序選單的顯示狀態
|
||||
updateSortSelectVisibility() {
|
||||
if (this.sortSelect) {
|
||||
if (this.sourceMode === 'bookmarks') {
|
||||
this.sortSelect.style.display = 'none';
|
||||
} else {
|
||||
this.sortSelect.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.upSelect) {
|
||||
if (this.sourceMode === 'folder') {
|
||||
this.upSelect.style.display = '';
|
||||
} else {
|
||||
this.upSelect.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFiles() {
|
||||
if (this.sourceMode === 'folder' && this.sourcePath) {
|
||||
// 獲取指定資料夾內的所有 Markdown 檔案
|
||||
|
|
@ -475,51 +417,99 @@ class GridView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
async rerender() {
|
||||
setSource(mode, path = null) {
|
||||
this.sourceMode = mode;
|
||||
this.sourcePath = path;
|
||||
this.render();
|
||||
}
|
||||
|
||||
async render() {
|
||||
// 儲存當前捲動位置
|
||||
const scrollContainer = this.containerEl.querySelector('.view-content');
|
||||
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0;
|
||||
|
||||
// 清空容器
|
||||
const contentEl = this.containerEl.querySelector('.view-content');
|
||||
if (contentEl) {
|
||||
contentEl.empty();
|
||||
// 清空整個容器
|
||||
this.containerEl.empty();
|
||||
|
||||
// 創建頂部按鈕區域
|
||||
const headerButtonsDiv = this.containerEl.createDiv('header-buttons');
|
||||
|
||||
// 添加回上層按鈕(僅在資料夾模式且不在根目錄時顯示)
|
||||
if (this.sourceMode === 'folder' && this.sourcePath !== '/') {
|
||||
const upButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('GO_UP') } });
|
||||
upButton.addEventListener('click', () => {
|
||||
const parentPath = this.sourcePath.split('/').slice(0, -1).join('/') || '/';
|
||||
this.setSource('folder', parentPath);
|
||||
});
|
||||
setIcon(upButton, 'arrow-up');
|
||||
}
|
||||
|
||||
// 添加重新整理按鈕
|
||||
const refreshButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('REFRESH') } });
|
||||
refreshButton.addEventListener('click', () => {
|
||||
this.render();
|
||||
});
|
||||
setIcon(refreshButton, 'refresh-ccw');
|
||||
|
||||
// 添加重新選擇資料夾按鈕
|
||||
const reselectButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('RESELECT_FOLDER') } });
|
||||
reselectButton.addEventListener('click', () => {
|
||||
showFolderSelectionModal(this.app, this.plugin, this);
|
||||
});
|
||||
setIcon(reselectButton, "folder");
|
||||
|
||||
// 添加排序按鈕
|
||||
if (this.sourceMode !== 'bookmarks') {
|
||||
const sortButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('SORTING') } });
|
||||
sortButton.addEventListener('click', (evt) => {
|
||||
const menu = new Menu();
|
||||
const sortOptions = [
|
||||
{ value: 'name-asc', label: t('SORT_NAME_ASC'), icon: 'a-arrow-up' },
|
||||
{ value: 'name-desc', label: t('SORT_NAME_DESC'), icon: 'a-arrow-down' },
|
||||
{ value: 'mtime-desc', label: t('SORT_MTIME_DESC'), icon: 'clock' },
|
||||
{ value: 'mtime-asc', label: t('SORT_MTIME_ASC'), icon: 'clock' },
|
||||
{ value: 'ctime-desc', label: t('SORT_CTIME_DESC'), icon: 'calendar' },
|
||||
{ value: 'ctime-asc', label: t('SORT_CTIME_ASC'), icon: 'calendar' },
|
||||
{ value: 'random', label: t('SORT_RANDOM'), icon: 'dice' },
|
||||
];
|
||||
|
||||
sortOptions.forEach(option => {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(option.label)
|
||||
.setIcon(option.icon)
|
||||
.setChecked(this.sortType === option.value)
|
||||
.onClick(() => {
|
||||
this.sortType = option.value;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
});
|
||||
menu.showAtMouseEvent(evt);
|
||||
});
|
||||
setIcon(sortButton, 'arrow-down-up');
|
||||
}
|
||||
|
||||
// 創建內容區域
|
||||
const contentEl = this.containerEl.createDiv('view-content');
|
||||
|
||||
// 重新渲染內容
|
||||
await this.render();
|
||||
await this.grid_render();
|
||||
this.leaf.updateHeader()
|
||||
|
||||
if (this.sourceMode === '') {
|
||||
this.leaf.view.titleEl.textContent = t('GRID_VIEW_TITLE');
|
||||
} else if (this.sourceMode === 'bookmarks') {
|
||||
this.leaf.view.titleEl.textContent = t('BOOKMARKS_MODE');
|
||||
} else if (this.sourceMode === 'folder') {
|
||||
this.leaf.view.titleEl.textContent = this.sourcePath;
|
||||
} else if (this.sourceMode === 'search') {
|
||||
this.leaf.view.titleEl.textContent = t('SEARCH_RESULTS');
|
||||
} else if (this.sourceMode === 'backlinks') {
|
||||
this.leaf.view.titleEl.textContent = t('BACKLINKS_MODE');
|
||||
}
|
||||
|
||||
// 恢復捲動位置
|
||||
if (scrollContainer) {
|
||||
scrollContainer.scrollTop = scrollTop;
|
||||
contentEl.scrollTop = scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
setSource(mode, path = null) {
|
||||
this.sourceMode = mode;
|
||||
this.sourcePath = path;
|
||||
this.updateSortSelectVisibility();
|
||||
this.rerender();
|
||||
}
|
||||
|
||||
async render() {
|
||||
async grid_render() {
|
||||
const container = this.containerEl.children[1];
|
||||
container.empty();
|
||||
container.addClass('grid-container');
|
||||
container.style.setProperty('--grid-item-width', this.plugin.settings.gridItemWidth + 'px');
|
||||
container.style.setProperty('--image-area-width', this.plugin.settings.imageAreaWidth + 'px');
|
||||
container.style.setProperty('--image-area-height', this.plugin.settings.imageAreaHeight + 'px');
|
||||
|
||||
// 如果是書籤模式且書籤插件未啟用,顯示提示
|
||||
if (this.sourceMode === 'bookmarks' && !this.app.internalPlugins.plugins.bookmarks?.enabled) {
|
||||
|
|
@ -532,22 +522,6 @@ class GridView extends ItemView {
|
|||
new Notice(t(t(t('NO_BACKLINKS'))));
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是資料夾模式,添加上層資料夾連結(除了根目錄)
|
||||
if (this.sourceMode === 'folder' && this.sourcePath !== '/') {
|
||||
const parentPath = this.sourcePath.split('/').slice(0, -1).join('/') || '/';
|
||||
const parentEl = container.createDiv('grid-item folder-item');
|
||||
parentEl.addClass('parent-folder');
|
||||
|
||||
const contentArea = parentEl.createDiv('content-area');
|
||||
const titleEl = contentArea.createEl('h3');
|
||||
titleEl.textContent = '📁 ..';
|
||||
|
||||
// 點擊時切換到上層資料夾
|
||||
parentEl.addEventListener('click', () => {
|
||||
this.setSource('folder', parentPath);
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是資料夾模式,先顯示所有子資料夾
|
||||
if (this.sourceMode === 'folder') {
|
||||
|
|
@ -555,7 +529,13 @@ class GridView extends ItemView {
|
|||
if (currentFolder instanceof TFolder) {
|
||||
// 只取得當前資料夾中的 Markdown 檔案,不包含子資料夾
|
||||
const subfolders = currentFolder.children
|
||||
.filter(child => child instanceof TFolder)
|
||||
.filter(child => {
|
||||
if (!(child instanceof TFolder)) return false;
|
||||
// 檢查資料夾是否在忽略清單中
|
||||
return !this.plugin.settings.ignoredFolders.some(
|
||||
ignoredPath => child.path === ignoredPath || child.path.startsWith(ignoredPath + '/')
|
||||
);
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
for (const folder of subfolders) {
|
||||
|
|
@ -664,7 +644,7 @@ class GridView extends ItemView {
|
|||
this.sourceMode = state.state.sourceMode || '';
|
||||
this.sourcePath = state.state.sourcePath || null;
|
||||
this.sortType = state.state.sortType || 'mtime-desc';
|
||||
this.rerender();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -673,7 +653,9 @@ class GridView extends ItemView {
|
|||
const DEFAULT_SETTINGS = {
|
||||
ignoredFolders: [],
|
||||
defaultSortType: 'mtime-desc', // 預設排序模式:修改時間倒序
|
||||
gridItemWidth: 300 // 網格項目寬度,預設 300
|
||||
gridItemWidth: 300, // 網格項目寬度,預設 300
|
||||
imageAreaWidth: 100, // 圖片區域寬度,預設 100
|
||||
imageAreaHeight: 100 // 圖片區域高度,預設 100
|
||||
};
|
||||
|
||||
// 設定頁面類別
|
||||
|
|
@ -735,6 +717,32 @@ class GridExplorerSettingTab extends require('obsidian').PluginSettingTab {
|
|||
this.plugin.settings.gridItemWidth = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// 圖片區域寬度設定
|
||||
new Setting(containerEl)
|
||||
.setName(t('IMAGE_AREA_WIDTH'))
|
||||
.setDesc(t('IMAGE_AREA_WIDTH_DESC'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(50, 200, 10)
|
||||
.setValue(this.plugin.settings.imageAreaWidth)
|
||||
.setDynamicTooltip()
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.imageAreaWidth = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// 圖片區域高度設定
|
||||
new Setting(containerEl)
|
||||
.setName(t('IMAGE_AREA_HEIGHT'))
|
||||
.setDesc(t('IMAGE_AREA_HEIGHT_DESC'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(50, 200, 10)
|
||||
.setValue(this.plugin.settings.imageAreaHeight)
|
||||
.setDynamicTooltip()
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.imageAreaHeight = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.3",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
|
|
|
|||
81
styles.css
81
styles.css
|
|
@ -6,22 +6,26 @@
|
|||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-width, 300px), 1fr));
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
align-items: start;
|
||||
background: var(--background-secondary) !important;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.view-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
gap: 14px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
|
@ -37,8 +41,8 @@
|
|||
}
|
||||
|
||||
.image-area {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
width: var(--image-area-width);
|
||||
height: var(--image-area-height);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +55,7 @@
|
|||
|
||||
.grid-item h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 1.2em;
|
||||
font-size: 1.1em;
|
||||
color: var(--text-normal);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -61,7 +65,7 @@
|
|||
.grid-item p {
|
||||
margin: 0;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
|
|
@ -101,4 +105,63 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
/* 頂部按鈕區域樣式 */
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
background: var(--background-primary);
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.header-buttons button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
background-color: var(--interactive-normal);
|
||||
border-radius: 4px;
|
||||
color: var(--text-normal);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s, transform 0.1s;
|
||||
min-width: 45px;
|
||||
}
|
||||
|
||||
.header-buttons button:hover {
|
||||
background-color: var(--interactive-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.header-buttons button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 特定按鈕樣式 */
|
||||
.header-buttons .sort-button,
|
||||
.header-buttons .reselect-button,
|
||||
.header-buttons .refresh-button,
|
||||
.header-buttons .up-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-buttons .sort-button::before {
|
||||
content: "↕";
|
||||
}
|
||||
|
||||
.header-buttons .reselect-button::before {
|
||||
content: "📁";
|
||||
}
|
||||
|
||||
.header-buttons .refresh-button::before {
|
||||
content: "↻";
|
||||
}
|
||||
|
||||
.header-buttons .up-button::before {
|
||||
content: "↑";
|
||||
}
|
||||
Loading…
Reference in a new issue