mirror of
https://github.com/netajam/obsidian_note_uid_generator.git
synced 2026-07-22 05:45:32 +00:00
Add nanoID
This commit is contained in:
parent
9f619492d8
commit
781a449690
5 changed files with 188 additions and 27 deletions
27
package-lock.json
generated
27
package-lock.json
generated
|
|
@ -1,14 +1,15 @@
|
|||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"version": "1.0.0",
|
||||
"name": "note_uid_generator",
|
||||
"version": "Obsidian plugin to generate uid for your notes",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-sample-plugin",
|
||||
"version": "1.0.0",
|
||||
"name": "note_uid_generator",
|
||||
"version": "Obsidian plugin to generate uid for your notes",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"uuid": "^11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -1913,6 +1914,24 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export async function handleGenerateUpdateUid(plugin: UIDGenerator, overwrite: t
|
|||
const view = plugin.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (view?.file) {
|
||||
const file = view.file;
|
||||
const newUid = uidUtils.generateUID();
|
||||
const newUid = uidUtils.generateUID(plugin);
|
||||
const setResult = await uidUtils.setUID(plugin, file, newUid, overwrite); // Use util
|
||||
|
||||
if (setResult) {
|
||||
|
|
@ -36,7 +36,7 @@ export async function handleCreateUidIfMissing(plugin: UIDGenerator): Promise<vo
|
|||
return;
|
||||
}
|
||||
|
||||
const newUid = uidUtils.generateUID();
|
||||
const newUid = uidUtils.generateUID(plugin);
|
||||
const setResult = await uidUtils.setUID(plugin, file, newUid, false); // Use util, NO overwrite
|
||||
|
||||
if (setResult) {
|
||||
|
|
@ -234,10 +234,10 @@ export async function handleCopyTitlesAndUidsForSelection(plugin: UIDGenerator):
|
|||
return;
|
||||
}
|
||||
|
||||
// 3. Get the selected paths
|
||||
// 3. Get the selected paths
|
||||
const selectedPaths: string[] = view.selectedFiles;
|
||||
|
||||
// 4. Filter for Markdown files
|
||||
// 4. Filter for Markdown files
|
||||
const filesToProcess: TFile[] = [];
|
||||
for (const path of selectedPaths) {
|
||||
const file = plugin.app.vault.getAbstractFileByPath(path);
|
||||
|
|
@ -332,7 +332,7 @@ export async function handleAutoGenerateUid(plugin: UIDGenerator, file: TFile |
|
|||
}
|
||||
|
||||
// Generate and set
|
||||
const newUid = uidUtils.generateUID();
|
||||
const newUid = uidUtils.generateUID(plugin);
|
||||
await uidUtils.setUID(plugin, file, newUid, false);
|
||||
}
|
||||
export async function handleAddMissingUidsInScope(plugin: UIDGenerator): Promise<void> {
|
||||
|
|
@ -395,7 +395,7 @@ export async function handleAddMissingUidsInScope(plugin: UIDGenerator): Promise
|
|||
}
|
||||
|
||||
// --- Generate and Set UID ---
|
||||
const newUid = uidUtils.generateUID();
|
||||
const newUid = uidUtils.generateUID(plugin);
|
||||
try {
|
||||
const success = await uidUtils.setUID(plugin, file, newUid, false); // Do not overwrite
|
||||
if (success) {
|
||||
|
|
|
|||
130
src/settings.ts
130
src/settings.ts
|
|
@ -8,6 +8,10 @@ import { FolderExclusionModal } from './ui/FolderExclusionModal';
|
|||
export interface UIDGeneratorSettings {
|
||||
uidKey: string;
|
||||
autoGenerateUid: boolean;
|
||||
uidGenerator: 'uuid' | 'nanoid';
|
||||
nanoidLength: number;
|
||||
nanoidAlphabet: string;
|
||||
nanoidSeparators: Array<{ char: string; position: number }>;
|
||||
autoGenerationScope: 'vault' | 'folder';
|
||||
autoGenerationFolder: string;
|
||||
autoGenerationExclusions: string[];
|
||||
|
|
@ -20,6 +24,10 @@ export interface UIDGeneratorSettings {
|
|||
export const DEFAULT_SETTINGS: UIDGeneratorSettings = {
|
||||
uidKey: 'uid',
|
||||
autoGenerateUid: false,
|
||||
uidGenerator: 'uuid',
|
||||
nanoidLength: 21,
|
||||
nanoidAlphabet: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
||||
nanoidSeparators: [],
|
||||
autoGenerationScope: 'vault',
|
||||
autoGenerationFolder: '',
|
||||
autoGenerationExclusions: [],
|
||||
|
|
@ -60,6 +68,104 @@ export class UIDSettingTab extends PluginSettingTab {
|
|||
this.display(); // Re-render to potentially update descriptions elsewhere
|
||||
}));
|
||||
|
||||
// --- UID Generator Type ---
|
||||
new Setting(containerEl).setName('UID generator type').setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Generator algorithm')
|
||||
.setDesc('Choose between UUID (standard v4) or NanoID (customizable).')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('uuid', 'UUID')
|
||||
.addOption('nanoid', 'NanoID')
|
||||
.setValue(this.plugin.settings.uidGenerator)
|
||||
.onChange(async (value: 'uuid' | 'nanoid') => {
|
||||
this.plugin.settings.uidGenerator = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
|
||||
if (this.plugin.settings.uidGenerator === 'nanoid') {
|
||||
new Setting(containerEl)
|
||||
.setName('NanoID length')
|
||||
.setDesc('Length of the generated ID (excluding injected characters).')
|
||||
.addText(text => text
|
||||
.setValue(String(this.plugin.settings.nanoidLength))
|
||||
.onChange(async (value) => {
|
||||
const num = parseInt(value);
|
||||
if (!isNaN(num) && num > 0) {
|
||||
this.plugin.settings.nanoidLength = num;
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('NanoID alphabet')
|
||||
.setDesc('Custom characters to use for ID generation.')
|
||||
.addTextArea(text => text
|
||||
.setValue(this.plugin.settings.nanoidAlphabet)
|
||||
.onChange(async (value) => {
|
||||
if (value.length > 0) {
|
||||
this.plugin.settings.nanoidAlphabet = value;
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
}));
|
||||
|
||||
// --- NanoID Separator Groups ---
|
||||
containerEl.createEl('h3', { text: 'NanoID Separator Groups' });
|
||||
containerEl.createEl('p', { text: 'Define characters to inject into the NanoID at specific positions. Position is calculated from the raw Base ID.' }).addClass('setting-item-description');
|
||||
|
||||
this.plugin.settings.nanoidSeparators.forEach((separator, index) => {
|
||||
const groupContainer = containerEl.createEl('div', { cls: 'uid-separator-group-container' });
|
||||
|
||||
new Setting(groupContainer)
|
||||
.setName(`Separator Group ${index + 1}`)
|
||||
.setHeading()
|
||||
.addButton(button => button
|
||||
.setButtonText('Remove')
|
||||
.setWarning()
|
||||
.setTooltip('Remove this separator group')
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.nanoidSeparators.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
|
||||
new Setting(groupContainer)
|
||||
.setName(`Inject character`)
|
||||
.setDesc('Character to inject (e.g., "+").')
|
||||
.addText(text => text
|
||||
.setValue(separator.char)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.nanoidSeparators[index].char = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(groupContainer)
|
||||
.setName(`Injection position`)
|
||||
.setDesc('Index to inject the character. Negative numbers count from the end.')
|
||||
.addText(text => text
|
||||
.setValue(String(separator.position))
|
||||
.onChange(async (value) => {
|
||||
const num = parseInt(value);
|
||||
if (!isNaN(num)) {
|
||||
this.plugin.settings.nanoidSeparators[index].position = num;
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Add new separator group')
|
||||
.setDesc('Adds a new group for injecting a character at a specific position.')
|
||||
.addButton(button => button
|
||||
.setButtonText('Add Group')
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.nanoidSeparators.push({ char: '', position: -2 });
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
}
|
||||
|
||||
// --- Automatic UID Generation ---
|
||||
new Setting(containerEl).setName('Automatic uid generation').setHeading();
|
||||
|
|
@ -130,21 +236,21 @@ export class UIDSettingTab extends PluginSettingTab {
|
|||
.setName(`Generate missing ${this.plugin.settings.uidKey}s now`)
|
||||
.setDesc(`Manually scan notes based on the current 'Generation scope' and 'Excluded folders' settings above. Add a ${this.plugin.settings.uidKey} to any applicable notes that don't already have one. This may take time for large vaults.`)
|
||||
.addButton(button => button
|
||||
.setButtonText("Generate missing uids")
|
||||
.setTooltip("Scan and add missing uids respecting scope/exclusions")
|
||||
.setButtonText('Generate missing uids')
|
||||
.setTooltip('Scan and add missing uids respecting scope/exclusions')
|
||||
.onClick(async () => {
|
||||
button.setDisabled(true); // Disable button during processing
|
||||
button.setButtonText("Processing...");
|
||||
button.setButtonText('Processing...');
|
||||
try {
|
||||
await this.plugin.triggerAddMissingUidsInScope();
|
||||
} catch (e) {
|
||||
// Catch potential errors from the trigger function itself
|
||||
console.error("[UIDGenerator] Error triggering bulk uid generation:", e);
|
||||
new Notice("Failed to start bulk generation. See console.", 5000);
|
||||
console.error('[UIDGenerator] Error triggering bulk uid generation:', e);
|
||||
new Notice('Failed to start bulk generation. See console.', 5000);
|
||||
} finally {
|
||||
// Re-enable button regardless of success/failure
|
||||
button.setDisabled(false);
|
||||
button.setButtonText("Generate missing uids");
|
||||
button.setButtonText('Generate missing uids');
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
@ -203,7 +309,7 @@ export class UIDSettingTab extends PluginSettingTab {
|
|||
const uidKey = this.plugin.settings.uidKey;
|
||||
// Basic validation for the folder path input
|
||||
if (!folderPath || folderPath.trim() === '') {
|
||||
new Notice("Please specify a folder path in the 'Folder to clear uids from' setting above first.");
|
||||
new Notice('Please specify a folder path in the \'Folder to clear uids from\' setting above first.');
|
||||
return; // Prevent proceeding without a path
|
||||
}
|
||||
|
||||
|
|
@ -223,18 +329,18 @@ export class UIDSettingTab extends PluginSettingTab {
|
|||
await this.plugin.clearUIDsInFolder(folderPath);
|
||||
} catch (err) {
|
||||
// Catch potential errors during the clearing process
|
||||
console.error("[UIDGenerator] Error during bulk uid clearing process:", err);
|
||||
new Notice("An unexpected error occurred during uid clearing. Check console.", 5000);
|
||||
console.error('[UIDGenerator] Error during bulk uid clearing process:', err);
|
||||
new Notice('An unexpected error occurred during uid clearing. Check console.', 5000);
|
||||
} finally {
|
||||
// 3. This block runs whether the clearing succeeded or failed
|
||||
if (autoGenWasOn) {
|
||||
// Notify the user that auto-gen was turned off
|
||||
new Notice("Automatic uid generation was disabled. You can re-enable it in settings if desired.", 8000);
|
||||
new Notice('Automatic uid generation was disabled. You can re-enable it in settings if desired.', 8000);
|
||||
}
|
||||
// 4. Re-render the settings tab display to reflect any changes
|
||||
// 4. Re-render the settings tab display to reflect any changes
|
||||
this.display();
|
||||
}
|
||||
}).open();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,46 @@
|
|||
import { TFile, Notice } from 'obsidian';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import UIDGenerator from './main';
|
||||
|
||||
/**
|
||||
* Generates a unique ID using the UUID v4 standard.
|
||||
* @param plugin The UIDGenerator plugin instance (for settings).
|
||||
*/
|
||||
export function generateUID(): string {
|
||||
export function generateUID(plugin: UIDGenerator): string {
|
||||
if (plugin.settings.uidGenerator === 'nanoid') {
|
||||
const alphabet = plugin.settings.nanoidAlphabet;
|
||||
const length = plugin.settings.nanoidLength;
|
||||
const nanoid = customAlphabet(alphabet, length);
|
||||
let id = nanoid();
|
||||
|
||||
const separators = plugin.settings.nanoidSeparators;
|
||||
|
||||
if (separators && separators.length > 0) {
|
||||
// Create a mutable array of insertions, normalizing positions
|
||||
const insertions = separators
|
||||
.filter(s => s.char && s.char.length > 0) // Only process non-empty chars
|
||||
.map(s => {
|
||||
let actualPosition = s.position;
|
||||
// Normalize negative positions relative to the *initial* nanoid length
|
||||
if (actualPosition < 0) {
|
||||
actualPosition = length + actualPosition; // e.g., length=21, pos=-2 -> 19
|
||||
}
|
||||
// Ensure position is within bounds [0, length]
|
||||
actualPosition = Math.max(0, Math.min(length, actualPosition));
|
||||
return { char: s.char, position: actualPosition };
|
||||
})
|
||||
.sort((a, b) => b.position - a.position); // Sort descending by position
|
||||
|
||||
// Apply insertions from right to left to avoid index shifting issues
|
||||
for (const insertion of insertions) {
|
||||
const { char, position } = insertion;
|
||||
id = id.slice(0, position) + char + id.slice(position);
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
return uuidv4();
|
||||
}
|
||||
|
||||
|
|
@ -24,7 +59,7 @@ export function getUIDFromFile(plugin: UIDGenerator, file: TFile | null): string
|
|||
return null;
|
||||
}
|
||||
const uidValue = frontmatter[plugin.settings.uidKey];
|
||||
// Ensure it's treated as a string, even if stored as number in YAML
|
||||
// Ensure it\'s treated as a string, even if stored as number in YAML
|
||||
return typeof uidValue === 'string' || typeof uidValue === 'number' ? String(uidValue) : null;
|
||||
} catch (error) {
|
||||
console.error(`[UIDGenerator] Error reading metadata for ${file.path}:`, error);
|
||||
|
|
@ -33,7 +68,7 @@ export function getUIDFromFile(plugin: UIDGenerator, file: TFile | null): string
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets or updates the UID property in a file's frontmatter.
|
||||
* Sets or updates the UID property in a file\'s frontmatter.
|
||||
* @param plugin The UIDGenerator plugin instance.
|
||||
* @param file The TFile to modify.
|
||||
* @param uid The UID string to set.
|
||||
|
|
@ -68,7 +103,7 @@ export async function setUID(plugin: UIDGenerator, file: TFile, uid: string, ove
|
|||
frontmatter[key] = uid;
|
||||
uidWasSetOrOverwritten = true;
|
||||
} else {
|
||||
// If overwrite is true but value is the same, we didn't *really* change it
|
||||
// If overwrite is true but value is the same, we didn\'t *really* change it
|
||||
uidWasSetOrOverwritten = false;
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +128,7 @@ export async function setUID(plugin: UIDGenerator, file: TFile, uid: string, ove
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes the UID property from a file's frontmatter, respecting the custom key.
|
||||
* Removes the UID property from a file\'s frontmatter, respecting the custom key.
|
||||
* @param plugin The UIDGenerator plugin instance.
|
||||
* @param file The TFile to modify.
|
||||
* @returns Promise<boolean> - True if a UID was found and removed, false otherwise.
|
||||
|
|
|
|||
Loading…
Reference in a new issue