This commit is contained in:
Marek Lenczewski
2026-04-18 07:42:54 +02:00
parent c7e13c0168
commit 1d7c50f0c0
2 changed files with 373 additions and 139 deletions

505
main.js
View File

@@ -54,8 +54,6 @@ var TRANSLATIONS = {
// 按鈕和標籤 // 按鈕和標籤
sorting: "\u6392\u5E8F\u65B9\u5F0F", sorting: "\u6392\u5E8F\u65B9\u5F0F",
refresh: "\u91CD\u65B0\u6574\u7406", refresh: "\u91CD\u65B0\u6574\u7406",
reselect_folder: "\u91CD\u65B0\u9078\u64C7\u4F4D\u7F6E",
go_up: "\u8FD4\u56DE\u4E0A\u5C64\u8CC7\u6599\u593E",
no_backlinks: "\u6C92\u6709\u53CD\u5411\u9023\u7D50", no_backlinks: "\u6C92\u6709\u53CD\u5411\u9023\u7D50",
search: "\u641C\u5C0B", search: "\u641C\u5C0B",
search_placeholder: "\u641C\u5C0B\u95DC\u9375\u5B57", search_placeholder: "\u641C\u5C0B\u95DC\u9375\u5B57",
@@ -65,6 +63,13 @@ var TRANSLATIONS = {
untitled: "\u672A\u547D\u540D", untitled: "\u672A\u547D\u540D",
files: "\u500B\u6A94\u6848", files: "\u500B\u6A94\u6848",
add: "\u65B0\u589E", add: "\u65B0\u589E",
new_folder: "\u65B0\u589E\u8CC7\u6599\u593E",
new_feature: "\u65B0\u589E\u529F\u80FD",
add_feature_files: "\u65B0\u589E\u529F\u80FD\u6A94\u6848",
rename_folder: "\u91CD\u65B0\u547D\u540D\u8CC7\u6599\u593E",
folder_name_placeholder: "\u8CC7\u6599\u593E\u540D\u7A31",
create: "\u5EFA\u7ACB",
rename: "\u91CD\u65B0\u547D\u540D",
// 視圖標題 // 視圖標題
grid_view_title: "\u7DB2\u683C\u8996\u5716", grid_view_title: "\u7DB2\u683C\u8996\u5716",
bookmarks_mode: "\u66F8\u7C64", bookmarks_mode: "\u66F8\u7C64",
@@ -180,9 +185,10 @@ var TRANSLATIONS = {
delete_note: "\u522A\u9664\u6A94\u6848", delete_note: "\u522A\u9664\u6A94\u6848",
open_folder_note: "\u958B\u555F\u8CC7\u6599\u593E\u7B46\u8A18", open_folder_note: "\u958B\u555F\u8CC7\u6599\u593E\u7B46\u8A18",
create_folder_note: "\u5EFA\u7ACB\u8CC7\u6599\u593E\u7B46\u8A18", create_folder_note: "\u5EFA\u7ACB\u8CC7\u6599\u593E\u7B46\u8A18",
ignore_folder: "\u5FFD\u7565\u6B64\u8CC7\u6599\u593E", delete_folder: "\u522A\u9664\u8CC7\u6599\u593E",
delete_folder_confirm: "\u78BA\u5B9A\u8981\u522A\u9664\u8CC7\u6599\u593E\u300C{name}\u300D\u53CA\u5176\u6240\u6709\u5167\u5BB9\u55CE\uFF1F",
delete: "\u522A\u9664",
searching: "\u641C\u5C0B\u4E2D...", searching: "\u641C\u5C0B\u4E2D...",
no_files: "\u6C92\u6709\u627E\u5230\u4EFB\u4F55\u6A94\u6848",
filter_folders: "\u7BE9\u9078\u8CC7\u6599\u593E...", filter_folders: "\u7BE9\u9078\u8CC7\u6599\u593E...",
}, },
en: { en: {
@@ -191,8 +197,6 @@ var TRANSLATIONS = {
// Buttons and Labels // Buttons and Labels
sorting: "Sort by", sorting: "Sort by",
refresh: "Refresh", refresh: "Refresh",
reselect_folder: "Reselect folder",
go_up: "Go Up",
no_backlinks: "No backlinks", no_backlinks: "No backlinks",
search: "Search", search: "Search",
search_placeholder: "Search keyword", search_placeholder: "Search keyword",
@@ -202,6 +206,13 @@ var TRANSLATIONS = {
untitled: "Untitled", untitled: "Untitled",
files: "files", files: "files",
add: "Add", add: "Add",
new_folder: "New folder",
new_feature: "New feature",
add_feature_files: "Add feature files",
rename_folder: "Rename folder",
folder_name_placeholder: "Folder name",
create: "Create",
rename: "Rename",
// View Titles // View Titles
grid_view_title: "Grid view", grid_view_title: "Grid view",
bookmarks_mode: "Bookmarks", bookmarks_mode: "Bookmarks",
@@ -299,9 +310,10 @@ var TRANSLATIONS = {
delete_note: "Delete file", delete_note: "Delete file",
open_folder_note: "Open folder note", open_folder_note: "Open folder note",
create_folder_note: "Create folder note", create_folder_note: "Create folder note",
ignore_folder: "Ignore this folder", delete_folder: "Delete folder",
delete_folder_confirm: "Delete folder \"{name}\" and all its contents?",
delete: "Delete",
searching: "Searching...", searching: "Searching...",
no_files: "No files found",
filter_folders: "Filter folders...", filter_folders: "Filter folders...",
}, },
zh: { zh: {
@@ -311,8 +323,6 @@ var TRANSLATIONS = {
// 按钮和标签 // 按钮和标签
sorting: "\u6392\u5E8F\u65B9\u5F0F", sorting: "\u6392\u5E8F\u65B9\u5F0F",
refresh: "\u5237\u65B0", refresh: "\u5237\u65B0",
reselect_folder: "\u91CD\u65B0\u9009\u62E9\u4F4D\u7F6E",
go_up: "\u8FD4\u56DE\u4E0A\u7EA7\u6587\u4EF6\u5939",
no_backlinks: "\u6CA1\u6709\u53CD\u5411\u94FE\u63A5", no_backlinks: "\u6CA1\u6709\u53CD\u5411\u94FE\u63A5",
search: "\u641C\u7D22", search: "\u641C\u7D22",
search_placeholder: "\u641C\u7D22\u5173\u952E\u5B57", search_placeholder: "\u641C\u7D22\u5173\u952E\u5B57",
@@ -322,6 +332,13 @@ var TRANSLATIONS = {
untitled: "\u672A\u547D\u540D", untitled: "\u672A\u547D\u540D",
files: "\u4E2A\u6587\u4EF6", files: "\u4E2A\u6587\u4EF6",
add: "\u6DFB\u52A0", add: "\u6DFB\u52A0",
new_folder: "\u65B0\u5EFA\u6587\u4EF6\u5939",
new_feature: "\u65B0\u5EFA\u529F\u80FD",
add_feature_files: "\u65B0\u5EFA\u529F\u80FD\u6587\u4EF6",
rename_folder: "\u91CD\u547D\u540D\u6587\u4EF6\u5939",
folder_name_placeholder: "\u6587\u4EF6\u5939\u540D\u79F0",
create: "\u521B\u5EFA",
rename: "\u91CD\u547D\u540D",
// 视图标题 // 视图标题
grid_view_title: "\u7F51\u683C\u89C6\u56FE", grid_view_title: "\u7F51\u683C\u89C6\u56FE",
bookmarks_mode: "\u4E66\u7B7E", bookmarks_mode: "\u4E66\u7B7E",
@@ -437,9 +454,10 @@ var TRANSLATIONS = {
delete_note: "\u5220\u9664\u6587\u4EF6", delete_note: "\u5220\u9664\u6587\u4EF6",
open_folder_note: "\u6253\u5F00\u6587\u4EF6\u5939\u7B14\u8BB0", open_folder_note: "\u6253\u5F00\u6587\u4EF6\u5939\u7B14\u8BB0",
create_folder_note: "\u521B\u5EFA\u6587\u4EF6\u5939\u7B14\u8BB0", create_folder_note: "\u521B\u5EFA\u6587\u4EF6\u5939\u7B14\u8BB0",
ignore_folder: "\u5FFD\u7565\u6B64\u6587\u4EF6\u5939", delete_folder: "\u5220\u9664\u6587\u4EF6\u5939",
delete_folder_confirm: "\u786E\u5B9A\u5220\u9664\u6587\u4EF6\u5939\u201C{name}\u201D\u53CA\u5176\u6240\u6709\u5185\u5BB9\u5417\uFF1F",
delete: "\u5220\u9664",
searching: "\u641C\u7D22\u4E2D...", searching: "\u641C\u7D22\u4E2D...",
no_files: "\u6CA1\u6709\u627E\u5230\u4EFB\u4F55\u6587\u4EF6",
filter_folders: "\u7B5B\u9009\u6587\u4EF6\u5939...", filter_folders: "\u7B5B\u9009\u6587\u4EF6\u5939...",
}, },
ja: { ja: {
@@ -449,8 +467,6 @@ var TRANSLATIONS = {
// ボタンとラベル // ボタンとラベル
sorting: "\u4E26\u3073\u66FF\u3048", sorting: "\u4E26\u3073\u66FF\u3048",
refresh: "\u66F4\u65B0", refresh: "\u66F4\u65B0",
reselect_folder: "\u30D5\u30A9\u30EB\u30C0\u3092\u518D\u9078\u629E",
go_up: "\u4E0A\u306E\u968E\u5C64\u3078",
no_backlinks: "\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u306A\u3057", no_backlinks: "\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u306A\u3057",
search: "\u691C\u7D22", search: "\u691C\u7D22",
search_placeholder: "\u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22", search_placeholder: "\u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22",
@@ -461,6 +477,13 @@ var TRANSLATIONS = {
untitled: "\u7121\u984C", untitled: "\u7121\u984C",
files: "\u30D5\u30A1\u30A4\u30EB", files: "\u30D5\u30A1\u30A4\u30EB",
add: "\u8FFD\u52A0", add: "\u8FFD\u52A0",
new_folder: "\u65B0\u898F\u30D5\u30A9\u30EB\u30C0",
new_feature: "\u65B0\u898F\u6A5F\u80FD",
add_feature_files: "\u6A5F\u80FD\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0",
rename_folder: "\u30D5\u30A9\u30EB\u30C0\u540D\u3092\u5909\u66F4",
folder_name_placeholder: "\u30D5\u30A9\u30EB\u30C0\u540D",
create: "\u4F5C\u6210",
rename: "\u540D\u524D\u3092\u5909\u66F4",
// ビュータイトル // ビュータイトル
grid_view_title: "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC", grid_view_title: "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC",
bookmarks_mode: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF", bookmarks_mode: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF",
@@ -598,10 +621,10 @@ var TRANSLATIONS = {
"\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u958B\u304F", "\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u958B\u304F",
create_folder_note: create_folder_note:
"\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u4F5C\u6210", "\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u4F5C\u6210",
ignore_folder: "\u3053\u306E\u30D5\u30A9\u30EB\u30C0\u3092\u7121\u8996", delete_folder: "\u30D5\u30A9\u30EB\u30C0\u3092\u524A\u9664",
delete_folder_confirm: "\u30D5\u30A9\u30EB\u30C0\u300C{name}\u300D\u3068\u305D\u306E\u3059\u3079\u3066\u306E\u5185\u5BB9\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
delete: "\u524A\u9664",
searching: "\u691C\u7D22\u4E2D...", searching: "\u691C\u7D22\u4E2D...",
no_files:
"\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
filter_folders: filter_folders:
"\u30D5\u30A9\u30EB\u30C0\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0...", "\u30D5\u30A9\u30EB\u30C0\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0...",
}, },
@@ -1316,6 +1339,89 @@ function showSearchModal(app, gridView, defaultQuery = "") {
new SearchModal(app, gridView, defaultQuery).open(); new SearchModal(app, gridView, defaultQuery).open();
} }
var FolderNameModal = class extends import_obsidian4.Modal {
constructor(app, titleKey, submitKey, defaultValue, onSubmit) {
super(app);
this.titleKey = titleKey;
this.submitKey = submitKey;
this.defaultValue = defaultValue || "";
this.onSubmit = onSubmit;
}
onOpen() {
const { contentEl } = this;
contentEl.empty();
new import_obsidian4.Setting(contentEl)
.setName(t(this.titleKey))
.setHeading();
const inputContainer = contentEl.createDiv("ge-search-container");
const input = inputContainer.createEl("input", {
type: "text",
value: this.defaultValue,
placeholder: t("folder_name_placeholder"),
});
const buttonContainer = contentEl.createDiv("ge-button-container");
const submitButton = buttonContainer.createEl("button", {
text: t(this.submitKey),
});
const cancelButton = buttonContainer.createEl("button", {
text: t("cancel"),
});
const submit = () => {
const name = input.value.trim();
if (!name) return;
this.close();
this.onSubmit(name);
};
submitButton.addEventListener("click", submit);
input.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
e.preventDefault();
e.stopPropagation();
submit();
}
});
cancelButton.addEventListener("click", () => this.close());
input.focus();
input.setSelectionRange(input.value.length, input.value.length);
}
onClose() {
this.contentEl.empty();
}
};
var ConfirmModal = class extends import_obsidian4.Modal {
constructor(app, title, message, confirmLabel, onConfirm) {
super(app);
this.title = title;
this.message = message;
this.confirmLabel = confirmLabel;
this.onConfirm = onConfirm;
}
onOpen() {
const { contentEl } = this;
contentEl.empty();
new import_obsidian4.Setting(contentEl).setName(this.title).setHeading();
contentEl.createEl("p", { text: this.message });
const buttonContainer = contentEl.createDiv("ge-button-container");
const confirmButton = buttonContainer.createEl("button", {
text: this.confirmLabel,
cls: "mod-warning",
});
const cancelButton = buttonContainer.createEl("button", {
text: t("cancel"),
});
confirmButton.addEventListener("click", () => {
this.close();
this.onConfirm();
});
cancelButton.addEventListener("click", () => this.close());
cancelButton.focus();
}
onClose() {
this.contentEl.empty();
}
};
// src/FileWatcher.ts // src/FileWatcher.ts
var import_obsidian6 = require("obsidian"); var import_obsidian6 = require("obsidian");
var FileWatcher = class { var FileWatcher = class {
@@ -1434,6 +1540,7 @@ var GridView = class extends import_obsidian7.ItemView {
this.fileWatcher.registerFileWatcher(); this.fileWatcher.registerFileWatcher();
} }
this.registerDomEvent(document, "keydown", (event) => { this.registerDomEvent(document, "keydown", (event) => {
if (document.body.querySelector(".modal-container")) return;
if (this.app.workspace.getActiveViewOfType(GridView) === this) { if (this.app.workspace.getActiveViewOfType(GridView) === this) {
this.handleKeyDown(event); this.handleKeyDown(event);
} }
@@ -1684,70 +1791,100 @@ var GridView = class extends import_obsidian7.ItemView {
}); });
(0, import_obsidian8.setIcon)(newNoteButton, "square-pen"); (0, import_obsidian8.setIcon)(newNoteButton, "square-pen");
} }
if ( if (this.sourceMode === "folder" && this.searchQuery === "") {
this.sourceMode === "folder" && const newFolderButton = headerButtonsDiv.createEl("button", {
this.sourcePath !== "/" && attr: { "aria-label": t("new_folder") },
this.searchQuery === ""
) {
const upButton = headerButtonsDiv.createEl("button", {
attr: { "aria-label": t("go_up") },
}); });
upButton.addEventListener("click", () => { newFolderButton.addEventListener("click", () => {
const parentPath = new FolderNameModal(
this.sourcePath.split("/").slice(0, -1).join("/") || "/"; this.app,
this.setSource("folder", parentPath); "new_folder",
this.clearSelection(); "create",
}); "",
(0, import_obsidian8.setIcon)(upButton, "arrow-up"); async (name) => {
if (import_obsidian7.Platform.isDesktop) { const basePath = this.sourcePath === "/" ? "" : this.sourcePath;
upButton.addEventListener("dragover", (event) => { let folderPath = basePath ? `${basePath}/${name}` : name;
event.preventDefault(); let counter = 1;
event.dataTransfer.dropEffect = "move"; while (this.app.vault.getAbstractFileByPath(folderPath)) {
upButton.addClass("ge-dragover"); folderPath = basePath
}); ? `${basePath}/${name} ${counter}`
upButton.addEventListener("dragleave", () => { : `${name} ${counter}`;
upButton.removeClass("ge-dragover"); counter++;
}); }
upButton.addEventListener("drop", async (event) => {
var _a;
event.preventDefault();
upButton.removeClass("ge-dragover");
const filePath =
(_a = event.dataTransfer) == null
? void 0
: _a.getData("text/plain");
if (!filePath) return;
const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1");
const parentPath =
this.sourcePath.split("/").slice(0, -1).join("/") || "/";
if (!parentPath) return;
const file = this.app.vault.getAbstractFileByPath(cleanedFilePath);
const folder = this.app.vault.getAbstractFileByPath(parentPath);
if (
file instanceof import_obsidian7.TFile &&
folder instanceof import_obsidian7.TFolder
) {
try { try {
const newPath = `${parentPath}/${file.name}`; await this.app.vault.createFolder(folderPath);
await this.app.fileManager.renameFile(file, newPath);
this.render(); this.render();
} catch (error) { } catch (error) {
console.error( console.error(
"An error occurred while moving the file to parent folder:", "An error occurred while creating a new folder:",
error error
); );
} }
} }
}); ).open();
} });
(0, import_obsidian8.setIcon)(newFolderButton, "folder");
}
if (this.sourceMode === "folder" && this.searchQuery === "") {
const newFeatureButton = headerButtonsDiv.createEl("button", {
attr: { "aria-label": t("new_feature") },
});
newFeatureButton.addEventListener("click", () => {
new FolderNameModal(
this.app,
"new_feature",
"create",
"",
async (name) => {
const basePath = this.sourcePath === "/" ? "" : this.sourcePath;
let folderPath = basePath ? `${basePath}/${name}` : name;
let counter = 1;
while (this.app.vault.getAbstractFileByPath(folderPath)) {
folderPath = basePath
? `${basePath}/${name} ${counter}`
: `${name} ${counter}`;
counter++;
}
try {
await this.app.vault.createFolder(folderPath);
await this.app.vault.create(`${folderPath}/features.md`, "");
await this.app.vault.create(`${folderPath}/specs.md`, "");
this.render();
} catch (error) {
console.error(
"An error occurred while creating a feature folder:",
error
);
}
}
).open();
});
(0, import_obsidian8.setIcon)(newFeatureButton, "folder-kanban");
}
if (this.sourceMode === "folder" && this.searchQuery === "") {
const addFeatureFilesButton = headerButtonsDiv.createEl("button", {
attr: { "aria-label": t("add_feature_files") },
});
addFeatureFilesButton.addEventListener("click", async () => {
const basePath = this.sourcePath === "/" ? "" : this.sourcePath;
const filenames = ["features.md", "specs.md"];
try {
for (const filename of filenames) {
const filePath = basePath ? `${basePath}/${filename}` : filename;
if (!this.app.vault.getAbstractFileByPath(filePath)) {
await this.app.vault.create(filePath, "");
}
}
this.render();
} catch (error) {
console.error(
"An error occurred while creating feature files:",
error
);
}
});
(0, import_obsidian8.setIcon)(addFeatureFilesButton, "files");
} }
const reselectButton = headerButtonsDiv.createEl("button", {
attr: { "aria-label": t("reselect_folder") },
});
reselectButton.addEventListener("click", () => {
showFolderSelectionModal(this.app, this.plugin, this);
});
(0, import_obsidian8.setIcon)(reselectButton, "folder");
const refreshButton = headerButtonsDiv.createEl("button", { const refreshButton = headerButtonsDiv.createEl("button", {
attr: { "aria-label": t("refresh") }, attr: { "aria-label": t("refresh") },
}); });
@@ -1798,43 +1935,6 @@ var GridView = class extends import_obsidian7.ItemView {
}); });
(0, import_obsidian8.setIcon)(sortButton, "arrow-up-narrow-wide"); (0, import_obsidian8.setIcon)(sortButton, "arrow-up-narrow-wide");
} }
if (this.sourceMode !== "random-note") {
const searchButtonContainer = headerButtonsDiv.createDiv(
"ge-search-button-container"
);
const searchButton = searchButtonContainer.createEl("button", {
cls: "search-button",
attr: { "aria-label": t("search") },
});
(0, import_obsidian8.setIcon)(searchButton, "search");
searchButton.addEventListener("click", () => {
this.showSearchModal();
});
if (this.searchQuery) {
searchButton.style.display = "none";
const searchTextContainer = searchButtonContainer.createDiv(
"ge-search-text-container"
);
const searchText = searchTextContainer.createEl("span", {
cls: "ge-search-text",
text: this.searchQuery,
});
searchText.style.cursor = "pointer";
searchText.addEventListener("click", () => {
this.showSearchModal(this.searchQuery);
});
const clearButton = searchTextContainer.createDiv("ge-clear-button");
(0, import_obsidian8.setIcon)(clearButton, "x");
clearButton.addEventListener("click", (e) => {
e.stopPropagation();
this.searchQuery = "";
this.searchAllFiles = true;
this.clearSelection();
this.render();
this.app.workspace.requestSaveLayout();
});
}
}
if ( if (
this.sourceMode === "random-note" && this.sourceMode === "random-note" &&
this.plugin.settings.showMediaFiles this.plugin.settings.showMediaFiles
@@ -1963,13 +2063,52 @@ var GridView = class extends import_obsidian7.ItemView {
parentFolderEl.removeClass("ge-dragover"); parentFolderEl.removeClass("ge-dragover");
}); });
parentFolderEl.addEventListener("drop", async (event) => { parentFolderEl.addEventListener("drop", async (event) => {
var _a2; var _a2, _b2;
event.preventDefault(); event.preventDefault();
parentFolderEl.removeClass("ge-dragover"); parentFolderEl.removeClass("ge-dragover");
const filePath = const draggedFolderPath =
(_a2 = event.dataTransfer) == null (_a2 = event.dataTransfer) == null
? void 0 ? void 0
: _a2.getData("text/plain"); : _a2.getData("application/x-obsidian-grid-folder");
if (draggedFolderPath) {
const sourceFolder =
this.app.vault.getAbstractFileByPath(draggedFolderPath);
const targetFolder =
this.app.vault.getAbstractFileByPath(parentPath);
if (
!(sourceFolder instanceof import_obsidian7.TFolder) ||
!(targetFolder instanceof import_obsidian7.TFolder)
)
return;
if (
parentPath === draggedFolderPath ||
parentPath.startsWith(draggedFolderPath + "/")
)
return;
const currentParent = sourceFolder.parent
? sourceFolder.parent.path
: "/";
if (currentParent === parentPath) return;
const newPath =
parentPath === "/" || parentPath === ""
? sourceFolder.name
: `${parentPath}/${sourceFolder.name}`;
if (this.app.vault.getAbstractFileByPath(newPath)) return;
try {
await this.app.fileManager.renameFile(sourceFolder, newPath);
this.render();
} catch (error) {
console.error(
"An error occurred while moving the folder to parent folder:",
error
);
}
return;
}
const filePath =
(_b2 = event.dataTransfer) == null
? void 0
: _b2.getData("text/plain");
if (!filePath) return; if (!filePath) return;
const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1"); const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1");
const file = this.app.vault.getAbstractFileByPath(cleanedFilePath); const file = this.app.vault.getAbstractFileByPath(cleanedFilePath);
@@ -2040,6 +2179,23 @@ var GridView = class extends import_obsidian7.ItemView {
const folderEl = container.createDiv("ge-grid-item ge-folder-item"); const folderEl = container.createDiv("ge-grid-item ge-folder-item");
this.gridItems.push(folderEl); this.gridItems.push(folderEl);
folderEl.dataset.folderPath = folder.path; folderEl.dataset.folderPath = folder.path;
if (import_obsidian7.Platform.isDesktop) {
folderEl.setAttribute("draggable", "true");
folderEl.addEventListener("dragstart", (event) => {
var _a2;
(_a2 = event.dataTransfer) == null
? void 0
: _a2.setData(
"application/x-obsidian-grid-folder",
folder.path
);
event.dataTransfer.effectAllowed = "move";
folderEl.addClass("ge-dragging");
});
folderEl.addEventListener("dragend", () => {
folderEl.removeClass("ge-dragging");
});
}
const contentArea = folderEl.createDiv("ge-content-area"); const contentArea = folderEl.createDiv("ge-content-area");
const titleContainer = contentArea.createDiv("ge-title-container"); const titleContainer = contentArea.createDiv("ge-title-container");
titleContainer.createEl("span", { titleContainer.createEl("span", {
@@ -2094,12 +2250,62 @@ var GridView = class extends import_obsidian7.ItemView {
} }
menu.addItem((item) => { menu.addItem((item) => {
item item
.setTitle(t("ignore_folder")) .setTitle(t("rename_folder"))
.setIcon("x") .setIcon("pencil")
.onClick(() => { .onClick(() => {
this.plugin.settings.ignoredFolders.push(folder.path); new FolderNameModal(
this.plugin.saveSettings(); this.app,
this.render(); "rename_folder",
"rename",
folder.name,
async (newName) => {
if (newName === folder.name) return;
const parentPath = folder.parent
? folder.parent.path
: "/";
const newPath =
parentPath === "/" || parentPath === ""
? newName
: `${parentPath}/${newName}`;
try {
await this.app.fileManager.renameFile(folder, newPath);
this.render();
} catch (error) {
console.error(
"An error occurred while renaming folder:",
error
);
}
}
).open();
});
});
menu.addItem((item) => {
item
.setTitle(t("delete_folder"))
.setIcon("trash-2")
.onClick(() => {
const msg = t("delete_folder_confirm").replace(
"{name}",
folder.name
);
new ConfirmModal(
this.app,
t("delete_folder"),
msg,
t("delete"),
async () => {
try {
await this.app.fileManager.trashFile(folder);
this.render();
} catch (error) {
console.error(
"An error occurred while deleting folder:",
error
);
}
}
).open();
}); });
}); });
menu.showAtMouseEvent(event); menu.showAtMouseEvent(event);
@@ -2151,13 +2357,8 @@ var GridView = class extends import_obsidian7.ItemView {
if (this.sourceMode === "random-note") { if (this.sourceMode === "random-note") {
files = files.slice(0, 10); files = files.slice(0, 10);
} }
if (files.length === 0) { if (files.length === 0 && this.plugin.statusBarItem) {
const noFilesDiv = container.createDiv("ge-no-files"); this.plugin.statusBarItem.setText("");
noFilesDiv.setText(t("no_files"));
if (this.plugin.statusBarItem) {
this.plugin.statusBarItem.setText("");
}
return;
} }
const observer = new IntersectionObserver( const observer = new IntersectionObserver(
(entries, observer2) => { (entries, observer2) => {
@@ -2209,7 +2410,7 @@ var GridView = class extends import_obsidian7.ItemView {
(contentWithoutMediaLinks.length > summaryLength ? "" : ""); (contentWithoutMediaLinks.length > summaryLength ? "" : "");
contentArea.createEl("p", { text: preview.trim() }); contentArea.createEl("p", { text: preview.trim() });
imageUrl = await findFirstImageInNote(this.app, content); imageUrl = await findFirstImageInNote(this.app, content);
} else { } else if (file.extension.toLowerCase() !== "pdf") {
contentArea.createEl("p", { contentArea.createEl("p", {
text: file.extension.toUpperCase(), text: file.extension.toUpperCase(),
}); });
@@ -2280,7 +2481,7 @@ var GridView = class extends import_obsidian7.ItemView {
const iconContainer = titleContainer.createDiv( const iconContainer = titleContainer.createDiv(
"ge-icon-container ge-pdf" "ge-icon-container ge-pdf"
); );
(0, import_obsidian8.setIcon)(iconContainer, "paperclip"); (0, import_obsidian8.setIcon)(iconContainer, "ge-pdf-file");
} else if (extension === "canvas") { } else if (extension === "canvas") {
const iconContainer = titleContainer.createDiv( const iconContainer = titleContainer.createDiv(
"ge-icon-container ge-canvas" "ge-icon-container ge-canvas"
@@ -2384,17 +2585,53 @@ var GridView = class extends import_obsidian7.ItemView {
folderItem.removeClass("ge-dragover"); folderItem.removeClass("ge-dragover");
}); });
folderItem.addEventListener("drop", async (event) => { folderItem.addEventListener("drop", async (event) => {
var _a2; var _a2, _b2;
event.preventDefault(); event.preventDefault();
folderItem.removeClass("ge-dragover"); folderItem.removeClass("ge-dragover");
const filePath =
(_a2 = event.dataTransfer) == null
? void 0
: _a2.getData("text/plain");
if (!filePath) return;
const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1");
const folderPath = folderItem.dataset.folderPath; const folderPath = folderItem.dataset.folderPath;
if (!folderPath) return; if (!folderPath) return;
const draggedFolderPath =
(_a2 = event.dataTransfer) == null
? void 0
: _a2.getData("application/x-obsidian-grid-folder");
if (draggedFolderPath) {
const sourceFolder =
this.app.vault.getAbstractFileByPath(draggedFolderPath);
const targetFolder =
this.app.vault.getAbstractFileByPath(folderPath);
if (
!(sourceFolder instanceof import_obsidian7.TFolder) ||
!(targetFolder instanceof import_obsidian7.TFolder)
)
return;
if (
folderPath === draggedFolderPath ||
folderPath.startsWith(draggedFolderPath + "/")
)
return;
const currentParent = sourceFolder.parent
? sourceFolder.parent.path
: "/";
if (currentParent === folderPath) return;
const newPath = `${folderPath}/${sourceFolder.name}`;
if (this.app.vault.getAbstractFileByPath(newPath)) return;
try {
await this.app.fileManager.renameFile(sourceFolder, newPath);
this.render();
} catch (error) {
console.error(
"An error occurred while moving the folder:",
error
);
}
return;
}
const filePath =
(_b2 = event.dataTransfer) == null
? void 0
: _b2.getData("text/plain");
if (!filePath) return;
const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1");
const file = this.app.vault.getAbstractFileByPath(cleanedFilePath); const file = this.app.vault.getAbstractFileByPath(cleanedFilePath);
const folder = this.app.vault.getAbstractFileByPath(folderPath); const folder = this.app.vault.getAbstractFileByPath(folderPath);
if ( if (
@@ -3184,6 +3421,10 @@ var GridExplorerSettingTab = class extends import_obsidian9.PluginSettingTab {
var GridExplorerPlugin = class extends import_obsidian10.Plugin { var GridExplorerPlugin = class extends import_obsidian10.Plugin {
async onload() { async onload() {
await this.loadSettings(); await this.loadSettings();
(0, import_obsidian10.addIcon)(
"ge-pdf-file",
`<path d="M60 10H25c-4 0-7 3-7 7v66c0 4 3 7 7 7h50c4 0 7-3 7-7V33L60 10z" fill="none" stroke="currentColor" stroke-width="7" stroke-linejoin="round"/><path d="M60 10v23h23" fill="none" stroke="currentColor" stroke-width="7" stroke-linejoin="round"/><text x="50" y="78" fill="currentColor" font-size="24" font-weight="800" font-family="Arial,sans-serif" text-anchor="middle" stroke="none">PDF</text>`
);
this.registerView("grid-view", (leaf) => new GridView(leaf, this)); this.registerView("grid-view", (leaf) => new GridView(leaf, this));
this.addSettingTab(new GridExplorerSettingTab(this.app, this)); this.addSettingTab(new GridExplorerSettingTab(this.app, this));
this.addCommand({ this.addCommand({

View File

@@ -445,13 +445,6 @@
color: var(--text-muted); color: var(--text-muted);
} }
.ge-no-files {
text-align: center;
padding: 2em;
color: var(--text-muted);
font-size: 1.2em;
}
/* 資料夾搜尋輸入框樣式 */ /* 資料夾搜尋輸入框樣式 */
.ge-folder-search-container { .ge-folder-search-container {
margin-bottom: 16px; margin-bottom: 16px;