devon22_obsidian-gridexplorer/src/utils/createItemUtils.ts
2026-05-15 17:52:33 +08:00

311 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { App, TFile, Notice } from 'obsidian';
import { showNameInputModal } from '../modal/folderRenameModal';
import { t } from '../translations';
interface ShortcutFrontmatter {
type?: 'mode' | 'folder' | 'file' | 'search' | 'uri';
redirect?: string;
searchCurrentLocationOnly?: boolean;
searchFilesNameOnly?: boolean;
searchMediaFiles?: boolean;
}
/**
* 創建新筆記的共用函數
* @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) {
// 預設名稱
const defaultName = `${t('untitled')}`;
// 先彈出名稱輸入框
showNameInputModal(app, {
title: t('new_folder'),
description: t('enter_new_folder_name'),
defaultValue: defaultName,
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}`;
// 檢查資料夾是否已存在,如果存在則遞增編號
let counter = 1;
while (app.vault.getAbstractFileByPath(newFolderPath)) {
newFolderName = `${baseName} ${counter}`;
newFolderPath = !folderPath || folderPath === '/' ? newFolderName : `${folderPath}/${newFolderName}`;
counter++;
}
try {
// 建立新資料夾
await app.vault.createFolder(newFolderPath);
// 執行成功回調
if (onSuccess) {
onSuccess();
}
} catch (error) {
console.error('An error occurred while creating a new folder:', error);
}
})();
},
});
}
/**
* 創建新畫布的共用函數
* @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
await app.fileManager.processFrontMatter(newFile, (frontmatter: ShortcutFrontmatter) => {
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') {
const targetFile = app.vault.getAbstractFileByPath(option.value);
if (!(targetFile instanceof TFile)) {
return;
}
const link = app.fileManager.generateMarkdownLink(targetFile, "");
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}`;
} catch {
// 如果解析失敗,使用安全的預設名稱
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
return `🌐 ${cleanUri}`;
}
}