devon22_obsidian-gridexplorer/src/utils/createItemUtils.ts

312 lines
12 KiB
TypeScript
Raw Normal View History

2025-08-26 16:17:36 +00:00
import { App, TFile, Notice } from 'obsidian';
2025-11-04 13:14:26 +00:00
import { showNameInputModal } from '../modal/folderRenameModal';
import { t } from '../translations';
2025-08-26 16:17:36 +00:00
2026-05-15 09:52:33 +00:00
interface ShortcutFrontmatter {
type?: 'mode' | 'folder' | 'file' | 'search' | 'uri';
redirect?: string;
searchCurrentLocationOnly?: boolean;
searchFilesNameOnly?: boolean;
searchMediaFiles?: boolean;
}
2025-08-26 16:17:36 +00:00
/**
*
* @param app Obsidian App
* @param folderPath
*/
export async function createNewNote(app: App, folderPath: string) {
let newFileName = `${t('untitled')}.md`;
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
// 檢查檔案是否已存在,如果存在則遞增編號
let counter = 1;
while (app.vault.getAbstractFileByPath(newFilePath)) {
newFileName = `${t('untitled')} ${counter}.md`;
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
counter++;
}
try {
// 建立新筆記
const newFile = await app.vault.create(newFilePath, '');
// 開啟新筆記
await app.workspace.getLeaf().openFile(newFile);
} catch (error) {
console.error('An error occurred while creating a new note:', error);
}
}
/**
*
* @param app Obsidian App
* @param folderPath
* @param onSuccess 調
*/
export async function createNewFolder(app: App, folderPath: string, onSuccess?: () => void) {
2025-09-04 16:27:45 +00:00
// 預設名稱
const defaultName = `${t('untitled')}`;
// 先彈出名稱輸入框
showNameInputModal(app, {
title: t('new_folder'),
description: t('enter_new_folder_name'),
defaultValue: defaultName,
2026-05-13 12:57:18 +00:00
onSubmit: (inputName: string) => {
void (async () => {
let baseName = (inputName && inputName.trim().length > 0) ? inputName.trim() : defaultName;
let newFolderName = baseName;
let newFolderPath = !folderPath || folderPath === '/' ? newFolderName : `${folderPath}/${newFolderName}`;
2025-09-04 16:27:45 +00:00
2026-05-13 12:57:18 +00:00
// 檢查資料夾是否已存在,如果存在則遞增編號
let counter = 1;
while (app.vault.getAbstractFileByPath(newFolderPath)) {
newFolderName = `${baseName} ${counter}`;
newFolderPath = !folderPath || folderPath === '/' ? newFolderName : `${folderPath}/${newFolderName}`;
counter++;
}
2025-09-04 16:27:45 +00:00
2026-05-13 12:57:18 +00:00
try {
// 建立新資料夾
await app.vault.createFolder(newFolderPath);
// 執行成功回調
if (onSuccess) {
onSuccess();
}
} catch (error) {
console.error('An error occurred while creating a new folder:', error);
2025-09-04 16:27:45 +00:00
}
2026-05-13 12:57:18 +00:00
})();
2025-09-04 16:27:45 +00:00
},
});
2025-08-26 16:17:36 +00:00
}
/**
*
* @param app Obsidian App
* @param folderPath
*/
export async function createNewCanvas(app: App, folderPath: string) {
let newFileName = `${t('untitled')}.canvas`;
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
// 檢查檔案是否已存在,如果存在則遞增編號
let counter = 1;
while (app.vault.getAbstractFileByPath(newFilePath)) {
newFileName = `${t('untitled')} ${counter}.canvas`;
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
counter++;
}
try {
// 建立新畫布
const newFile = await app.vault.create(newFilePath, '');
// 開啟新畫布
await app.workspace.getLeaf().openFile(newFile);
} catch (error) {
console.error('An error occurred while creating a new canvas:', error);
}
}
/**
* base
* @param app Obsidian App
* @param folderPath
*/
export async function createNewBase(app: App, folderPath: string) {
let newFileName = `${t('untitled')}.base`;
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
// 檢查檔案是否已存在,如果存在則遞增編號
let counter = 1;
while (app.vault.getAbstractFileByPath(newFilePath)) {
newFileName = `${t('untitled')} ${counter}.base`;
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
counter++;
}
try {
// 建立新 base 檔案
const newFile = await app.vault.create(newFilePath, '');
// 開啟新檔案
await app.workspace.getLeaf().openFile(newFile);
} catch (error) {
console.error('An error occurred while creating a new base:', error);
}
}
/**
*
* @param app Obsidian App
* @param folderPath
* @param option
*/
export async function createShortcut(
app: App,
folderPath: string,
option: {
type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
value: string;
display: string;
searchOptions?: {
searchCurrentLocationOnly: boolean;
searchFilesNameOnly: boolean;
searchMediaFiles: boolean;
};
}
) {
try {
// 生成不重複的檔案名稱
let counter = 0;
let shortcutName: string;
// 對於 URI 類型,使用特殊的檔名生成邏輯
if (option.type === 'uri') {
shortcutName = generateFilenameFromUri(option.value);
} else {
shortcutName = `${option.display}`;
}
let newName = `${shortcutName}.md`;
let newPath = !folderPath || folderPath === '/' ? newName : `${folderPath}/${newName}`;
while (app.vault.getAbstractFileByPath(newPath)) {
counter++;
const baseName = option.type === 'uri' ? generateFilenameFromUri(option.value) : option.display;
shortcutName = `${baseName} ${counter}`;
newName = `${shortcutName}.md`;
newPath = !folderPath || folderPath === '/' ? newName : `${folderPath}/${newName}`;
}
// 創建新檔案
const newFile = await app.vault.create(newPath, '');
// 使用 processFrontMatter 來更新 frontmatter
2026-05-15 09:52:33 +00:00
await app.fileManager.processFrontMatter(newFile, (frontmatter: ShortcutFrontmatter) => {
2025-08-26 16:17:36 +00:00
if (option.type === 'mode') {
frontmatter.type = 'mode';
frontmatter.redirect = option.value;
} else if (option.type === 'folder') {
frontmatter.type = 'folder';
frontmatter.redirect = option.value;
} else if (option.type === 'file') {
2026-05-15 09:52:33 +00:00
const targetFile = app.vault.getAbstractFileByPath(option.value);
if (!(targetFile instanceof TFile)) {
return;
}
const link = app.fileManager.generateMarkdownLink(targetFile, "");
2025-08-26 16:17:36 +00:00
frontmatter.type = "file";
frontmatter.redirect = link;
} else if (option.type === 'search') {
frontmatter.type = 'search';
frontmatter.redirect = option.value;
// 添加搜尋選項到 frontmatter
if (option.searchOptions) {
frontmatter.searchCurrentLocationOnly = option.searchOptions.searchCurrentLocationOnly;
frontmatter.searchFilesNameOnly = option.searchOptions.searchFilesNameOnly;
frontmatter.searchMediaFiles = option.searchOptions.searchMediaFiles;
}
} else if (option.type === 'uri') {
frontmatter.type = 'uri';
frontmatter.redirect = option.value;
}
});
new Notice(`${t('shortcut_created')}: ${shortcutName}`);
} catch (error) {
console.error('Create shortcut error', error);
new Notice(t('failed_to_create_shortcut'));
}
}
/**
* URI
* @param uri URI
* @returns
*/
function generateFilenameFromUri(uri: string): string {
try {
// 處理 obsidian:// 協議
if (uri.startsWith('obsidian://')) {
const match = uri.match(/obsidian:\/\/([^?]+)/);
let vaultName = '';
// 嘗試提取 vault 參數
const vaultMatch = uri.match(/[?&]vault=([^&]+)/);
if (vaultMatch) {
vaultName = decodeURIComponent(vaultMatch[1]);
// 清理 vault 名稱,移除不適合檔名的字符
vaultName = vaultName.replace(/[<>:"/\\|?*]/g, '_');
}
if (match) {
const action = match[1];
const vaultSuffix = vaultName ? ` (${vaultName})` : '';
// 根據不同的 obsidian 動作生成檔名
switch (action) {
case 'open':
return `🌐 Obsidian Open${vaultSuffix}`;
case 'new':
return `🌐 Obsidian New${vaultSuffix}`;
case 'search':
return `🌐 Obsidian Search${vaultSuffix}`;
case 'hook-get-address':
return `🌐 Obsidian Hook${vaultSuffix}`;
default:
return `🌐 Obsidian ${action}${vaultSuffix}`;
}
}
return vaultName ? `🌐 Obsidian Link (${vaultName})` : '🌐 Obsidian Link';
}
// 處理 file:// 協議
if (uri.startsWith('file://')) {
const filename = uri.split('/').pop() || 'Local File';
return `🌐 ${filename}`;
}
// 處理 http/https 協議
if (uri.startsWith('http://') || uri.startsWith('https://')) {
const url = new URL(uri);
let domain = url.hostname;
// 移除 www. 前綴
if (domain.startsWith('www.')) {
domain = domain.substring(4);
}
// 如果有路徑,嘗試提取有意義的部分
if (url.pathname && url.pathname !== '/') {
const pathParts = url.pathname.split('/').filter(part => part.length > 0);
if (pathParts.length > 0) {
const lastPart = pathParts[pathParts.length - 1];
// 如果最後一部分看起來像檔名或有意義的標識符
if (lastPart.length < 50 && !lastPart.includes('?')) {
return `🌐 ${domain} - ${lastPart}`;
}
}
}
return `🌐 ${domain}`;
}
// 其他協議的處理
const protocolMatch = uri.match(/^([^:]+):/);
if (protocolMatch) {
const protocol = protocolMatch[1].toUpperCase();
return `🌐 ${protocol} Link`;
}
// 如果不是標準 URI直接使用前 30 個字符
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
return `🌐 ${cleanUri}`;
2026-05-15 09:52:33 +00:00
} catch {
2025-08-26 16:17:36 +00:00
// 如果解析失敗,使用安全的預設名稱
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
return `🌐 ${cleanUri}`;
}
}