This commit is contained in:
Netajam 2025-04-10 10:43:50 +02:00
parent 961eac27b9
commit 64c2e0f88f
8 changed files with 121 additions and 55 deletions

View file

@ -1,4 +1,4 @@
import { Editor, MarkdownView, TFile, TFolder, Notice, normalizePath, WorkspaceLeaf } from 'obsidian';
import { Editor, MarkdownView, TFile, TFolder, Notice, normalizePath, WorkspaceLeaf, FileExplorerView } from 'obsidian';
import UIDGenerator from './main';
import * as uidUtils from './uidUtils';
@ -126,7 +126,6 @@ export function handleCopyTitleUid(plugin: UIDGenerator, specificFile?: TFile):
/** Logic for Folder Context Menu Action */
export async function handleCopyTitlesAndUidsFromFolder(plugin: UIDGenerator, folder: TFolder): Promise<void> {
console.log(`[UIDGenerator] Copying titles+${plugin.settings.uidKey}s for folder: ${folder.path}`);
const markdownFiles = plugin.app.vault.getMarkdownFiles();
const filesInFolder: TFile[] = [];
const targetPath = folder.path;
@ -182,8 +181,6 @@ export async function handleCopyTitlesAndUidsForMultipleFiles(plugin: UIDGenerat
return;
}
console.log(`[UIDGenerator] Copying titles+${plugin.settings.uidKey}s for ${files.length} selected files via context menu.`);
let outputLines: string[] = [];
let filesWithUidCount = 0;
const formatExists = plugin.settings.copyFormatString;
@ -213,23 +210,34 @@ export async function handleCopyTitlesAndUidsForMultipleFiles(plugin: UIDGenerat
/** Logic for Command Palette: Copy titles+UIDs for selected files in File Explorer */
export async function handleCopyTitlesAndUidsForSelection(plugin: UIDGenerator): Promise<void> {
// Find the active File Explorer leaf/view
const fileExplorerLeaf = plugin.app.workspace.getLeavesOfType('file-explorer')
.find(leaf => (leaf.view as any).selectedFiles && plugin.app.workspace.activeLeaf === leaf);
// 1. Find the active File Explorer leaf more reliably
let fileExplorerLeaf: WorkspaceLeaf | undefined = undefined;
const explorerLeaves = plugin.app.workspace.getLeavesOfType('file-explorer');
if (explorerLeaves.length > 0) {
// Prefer the active leaf if it's a file explorer
if (plugin.app.workspace.activeLeaf && plugin.app.workspace.activeLeaf.view.getViewType() === 'file-explorer') {
fileExplorerLeaf = plugin.app.workspace.activeLeaf;
} else {
fileExplorerLeaf = explorerLeaves[0];
}
}
if (!fileExplorerLeaf || !(fileExplorerLeaf.view as any).selectedFiles) {
new Notice("No active file explorer with selected files found.");
if (!fileExplorerLeaf) {
new Notice("No active file explorer found.");
return;
}
// Access selected files
const selectedPaths: string[] = (fileExplorerLeaf.view as any).selectedFiles || [];
if (selectedPaths.length === 0) {
// 2. Assert the view type and check for selectedFiles property
const view = fileExplorerLeaf.view as FileExplorerView;
if (!view || !view.selectedFiles || view.selectedFiles.length === 0) {
new Notice("No files selected in the file explorer.");
return;
}
// 3. Get the selected paths
const selectedPaths: string[] = view.selectedFiles;
// 4. Filter for Markdown files
const filesToProcess: TFile[] = [];
for (const path of selectedPaths) {
const file = plugin.app.vault.getAbstractFileByPath(path);
@ -243,7 +251,7 @@ export async function handleCopyTitlesAndUidsForSelection(plugin: UIDGenerator):
return;
}
// Delegate to the same logic used by the context menu
// 5. Delegate to the multi-file handler (no changes needed here)
await handleCopyTitlesAndUidsForMultipleFiles(plugin, filesToProcess);
}
@ -256,7 +264,6 @@ export async function handleClearUIDsInFolder(plugin: UIDGenerator, folderPath:
const folder = plugin.app.vault.getAbstractFileByPath(normalizedFolderPath);
if (!folder || !(folder instanceof TFolder)) { new Notice(`Folder not found or path is not a folder: ${folderPath}`); return; }
console.log(`[UIDGenerator] Starting UID clearing process for folder: ${folder.path} using key "${plugin.settings.uidKey}"`);
new Notice(`Clearing ${plugin.settings.uidKey}s in "${folder.name}"... This may take a moment.`);
const markdownFiles = plugin.app.vault.getMarkdownFiles();
@ -275,7 +282,6 @@ export async function handleClearUIDsInFolder(plugin: UIDGenerator, folderPath:
return;
}
console.log(`[UIDGenerator] Found ${filesToProcess.length} markdown files to process.`);
let clearedCount = 0;
let errorCount = 0;
@ -293,7 +299,6 @@ export async function handleClearUIDsInFolder(plugin: UIDGenerator, folderPath:
message += ` Encountered ${errorCount} errors (check console).`;
}
new Notice(message, 10000);
console.log(`[UIDGenerator] UID clearing finished. Removed: ${clearedCount}, Errors: ${errorCount}`);
}
@ -327,7 +332,6 @@ export async function handleAutoGenerateUid(plugin: UIDGenerator, file: TFile |
}
// Generate and set
console.log(`[UIDGenerator] Auto-generating ${plugin.settings.uidKey} for: ${file.path}`);
const newUid = uidUtils.generateUID();
await uidUtils.setUID(plugin, file, newUid, false);
}
@ -347,7 +351,6 @@ export async function handleAddMissingUidsInScope(plugin: UIDGenerator): Promise
const noticeMessage = `Processing ${totalFiles} files for missing UIDs...`;
const notice = new Notice(noticeMessage, 0); // Show notice indefinitely until updated/hidden
console.log(`[UIDGenerator] Starting bulk check for missing UIDs. Scope: ${plugin.settings.autoGenerationScope}.`);
try { // Wrap the loop for final notice update
for (let i = 0; i < totalFiles; i++) {
@ -422,6 +425,5 @@ export async function handleAddMissingUidsInScope(plugin: UIDGenerator): Promise
}
new Notice(summary, 10000 + errorCount * 100); // Show longer if errors
console.log(`[UIDGenerator] ${summary.replace(/\n- /g, ', ')}`);
}
}

View file

@ -1,6 +1,7 @@
import {
App, Editor, MarkdownView, Notice, Plugin, TFile, TFolder,
debounce, Menu, TAbstractFile, WorkspaceLeaf
debounce, Menu, TAbstractFile, WorkspaceLeaf,
FileExplorerView
} from 'obsidian';
import { UIDGeneratorSettings, DEFAULT_SETTINGS, UIDSettingTab } from './settings';
import * as commands from './commands';
@ -10,7 +11,6 @@ export default class UIDGenerator extends Plugin {
settings: UIDGeneratorSettings;
async onload() {
console.log('Loading UID Generator Plugin v7 (files-menu)');
await this.loadSettings();
// --- Ribbon Icon ---
@ -98,16 +98,24 @@ export default class UIDGenerator extends Plugin {
name: `Copy titles+${this.settings.uidKey}s for selected files`,
checkCallback: (checking: boolean): boolean | void => {
const fileExplorerLeaf = this.app.workspace.getLeavesOfType('file-explorer')
?.find(leaf => (leaf.view as any).selectedFiles && this.app.workspace.activeLeaf === leaf);
?.find(leaf => leaf.view.getViewType() === 'file-explorer' && this.app.workspace.activeLeaf === leaf); // More robust check
let markdownSelected = false;
if (fileExplorerLeaf) {
const selectedPaths: string[] = (fileExplorerLeaf.view as any).selectedFiles || [];
markdownSelected = selectedPaths.some(path => {
const file = this.app.vault.getAbstractFileByPath(path);
return file instanceof TFile && file.extension === 'md';
});
}
let markdownSelected = false;
if (fileExplorerLeaf) {
// Use the augmented interface with a type assertion
const view = fileExplorerLeaf.view as FileExplorerView; // <-- Use the interface
const selectedPaths: string[] = view.selectedFiles || []; // <-- Access via typed view
// Check if at least one selected file is markdown
markdownSelected = selectedPaths.some(path => {
// 'this' refers to the Plugin instance in main.ts
// 'plugin' refers to the Plugin instance in commands.ts
// Make sure to use the correct variable (this or plugin) depending on the file
const file = this.app.vault.getAbstractFileByPath(path); // Or plugin.app.vault...
return file instanceof TFile && file.extension === 'md';
});
}
const canRun = !!fileExplorerLeaf && markdownSelected;
@ -166,7 +174,6 @@ export default class UIDGenerator extends Plugin {
}
onunload() {
console.log('Unloading UID Generator Plugin v7 (files-menu)');
}
// --- Settings Management ---

View file

@ -118,23 +118,15 @@ export class UIDSettingTab extends PluginSettingTab {
new FolderExclusionModal(this.app, this.plugin, () => this.display()).open();
}));
// Display the current list of excluded folders (read-only in this view)
const exclusionListEl = containerEl.createEl('ul', { cls: 'uid-exclusion-list' });
if (this.plugin.settings.autoGenerationExclusions.length > 0) {
// Sort before displaying for consistency
const sortedExclusions = [...this.plugin.settings.autoGenerationExclusions].sort();
sortedExclusions.forEach(folderPath => {
exclusionListEl.createEl('li', { text: folderPath });
});
} else {
// Display message if no folders are excluded
exclusionListEl.createEl('li', { text: 'No folders excluded.' });
}
// Basic styling for the list
exclusionListEl.style.marginTop = '5px';
exclusionListEl.style.marginBottom = '15px';
exclusionListEl.style.paddingLeft = '20px';
exclusionListEl.style.listStyle = 'none'; // Or 'disc', 'circle' etc.
}
new Setting(containerEl)
@ -227,7 +219,6 @@ export class UIDSettingTab extends PluginSettingTab {
autoGenWasOn = true;
this.plugin.settings.autoGenerateUid = false;
await this.plugin.saveSettings();
console.log("[UIDGenerator] Automatic UID generation temporarily disabled due to manual clear action.");
}
try {

View file

@ -1,5 +1,5 @@
// src/obsidian.d.ts
import 'obsidian';
// src/typings/obsidian.d.ts
import 'obsidian';
declare module 'obsidian' {
interface Workspace {
@ -7,11 +7,19 @@ declare module 'obsidian' {
name: 'files-menu',
callback: (
menu: Menu,
files: TAbstractFile[],
files: TAbstractFile[], // Note: it's an array
source: string,
leaf?: WorkspaceLeaf
) => any,
ctx?: any
): EventRef;
}
}
interface FileExplorerView extends View {
selectedFiles?: string[];
}
}

View file

@ -1,4 +1,4 @@
import { App, Modal, TFolder, Setting, debounce } from 'obsidian';
import { App, Modal, TFolder, debounce } from 'obsidian';
import UIDGenerator from '../main';
export class FolderExclusionModal extends Modal {

View file

@ -59,7 +59,6 @@ export async function setUID(plugin: UIDGenerator, file: TFile, uid: string, ove
initialUidExists = currentUid !== undefined && currentUid !== null && currentUid !== ''; // Check before potential modification
if (initialUidExists && !overwrite) {
console.log(`[UIDGenerator] ${key} already exists for ${file.path}, not overwriting.`);
uidWasSetOrOverwritten = false; // Explicitly false
return; // Exit processor
}
@ -83,7 +82,6 @@ export async function setUID(plugin: UIDGenerator, file: TFile, uid: string, ove
if (uidWasSetOrOverwritten) {
// Determine action based on initial state and overwrite flag
const action = initialUidExists && overwrite ? 'Overwrote' : 'Set';
console.log(`[UIDGenerator] ${action} ${key} for ${file.path}.`);
}
return uidWasSetOrOverwritten;
@ -112,7 +110,6 @@ export async function removeUID(plugin: UIDGenerator, file: TFile): Promise<bool
}
});
if (uidWasPresent) {
console.log(`[UIDGenerator] Removed key "${key}" from ${file.path}`);
}
return uidWasPresent;
} catch (error) {

View file

@ -1,8 +1,63 @@
/*
/* Styles for UID Generator Plugin Settings */
This CSS file will be included with your plugin, and
available in the app when your plugin is enabled.
/* Style the list of excluded folders in settings */
.uid-generator-exclusion-list {
margin-top: 5px;
margin-bottom: 15px;
padding-left: 20px; /* Indent list slightly */
list-style: none; /* Remove default bullets */
max-height: 150px; /* Optional: Limit height */
overflow-y: auto; /* Optional: Allow scrolling */
background-color: var(--background-secondary); /* Optional: Subtle background */
border: 1px solid var(--background-modifier-border); /* Optional: Border */
border-radius: var(--radius-m); /* Optional: Rounded corners */
padding-top: 5px; /* Optional: Inner padding */
padding-bottom: 5px; /* Optional: Inner padding */
}
If your plugin does not need CSS, delete this file.
.uid-generator-exclusion-list li {
padding: 2px 0; /* Optional: Spacing between list items */
}
*/
/* You might also want the styles for the FolderExclusionModal here */
.uid-folder-exclusion-modal .uid-search-input {
width: 95%;
margin-bottom: 10px;
}
.uid-folder-exclusion-modal .uid-suggestion-container {
max-height: 300px;
overflow-y: auto;
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
padding: 5px;
}
.uid-folder-exclusion-modal .setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 8px;
border-bottom: 1px solid var(--background-modifier-border);
}
.uid-folder-exclusion-modal .setting-item:last-child {
border-bottom: none;
}
.uid-folder-exclusion-modal .setting-item-info {
flex-grow: 1;
margin-right: 10px;
overflow: hidden; /* Prevent long paths breaking layout */
text-overflow: ellipsis; /* Show ... for long paths */
white-space: nowrap; /* Keep path on one line */
}
.uid-folder-exclusion-modal .setting-item-control button {
margin-left: auto;
flex-shrink: 0; /* Prevent button shrinking */
}
.uid-no-results {
padding: 10px;
text-align: center;
color: var(--text-muted);
}

View file

@ -18,7 +18,13 @@
"ES7"
]
},
"typeRoots": [
"./node_modules/@types", // Standard location for installed types
"./src/typings" // Your custom typings folder
],
"include": [
"**/*.ts"
"**/*.ts","**/*.d.ts", "src/typings/obsidian.d.ts"
]
}