I wanna autogenerate block-ids without searching for a block

Problem: I wanna link a block without searching through a really long document and also not wanna have to check a list of block-ID which I already have in use.

I now two ways of linking blocks.

  1. write [[file^]] and then search for a block and getting a automatic generated block-ID
  2. write ^ after a block end define a own block-ID

What I am searching for.

  1. write ^ after a block pressing enter end getting a automatic block-ID

Autogeneration is rather vague…
I am using this script to add an auto-generated blockID to a paragraph/line by running the script myself (so it’s more like semi-auto):

import * as obsidian from 'obsidian';

const blockIdCreator = async (app: obsidian.App): Promise<void> => {
    const editor = app.workspace.activeLeaf?.view?.editor;
    const currentFile = app.workspace.getActiveFile();
    
    if (!editor || !currentFile) return;
    
    let selection = editor.getSelection();
    const hasSelection = selection.length > 0;
    console.log("Has selection:", hasSelection);
    
    // If no selection, get the current line at cursor position
    if (!hasSelection) {
        const cursor = editor.getCursor();
        const line = editor.getLine(cursor.line);
        selection = line;
        console.log("Using current line:", selection);
    }

    // Get the entire file content to check for existing block IDs
    const fileContent = await app.vault.read(currentFile);
    const selectionIndex = fileContent.indexOf(selection);
    
    if (selectionIndex === -1) {
        console.log("Selection not found in file content.");
        return;
    }
    
    // Check if there's a block ID right after the selection in the file
    const textAfterSelection = fileContent.substring(selectionIndex + selection.length);
    const nextLineBlockIdMatch = textAfterSelection.match(/^\s*\n\^([a-zA-Z0-9-]+)/);
    const inlineBlockIdMatch = textAfterSelection.match(/^\s\^([a-zA-Z0-9-]+)/);
    
    // If an existing block ID is found, use it
    if (nextLineBlockIdMatch || inlineBlockIdMatch) {
        const existingBlockId = (nextLineBlockIdMatch || inlineBlockIdMatch)[1];
        console.log("Existing block ID found:", existingBlockId);
        
        const clipboardText = `![[${currentFile.basename}#^${existingBlockId}]]`;
        await navigator.clipboard.writeText(clipboardText);
        new obsidian.Notice(`Link ${clipboardText} has been added to the clipboard`);
        return;
    }

    // Regex to match Zotero links
    const zoteroRegex = /items\/([A-Z0-9]{6,10}).*?annotation=([A-Z0-9]{6,10})/g;
    // Check for inline block ID
    const blockIdInlineRegex = /\s\^([a-zA-Z0-9-]+)(?:\s|$)/;
    
    // Check for inline block ID in the selection
    const selectionInlineBlockId = selection.match(blockIdInlineRegex);
    if (selectionInlineBlockId) {
        console.log("Selection already has inline block ID:", selectionInlineBlockId[1]);
        const clipboardText = `![[${currentFile.basename}#^${selectionInlineBlockId[1]}]]`;
        await navigator.clipboard.writeText(clipboardText);
        new obsidian.Notice(`Link ${clipboardText} has been added to the clipboard`);
        return;
    }

    const allMatches = [...selection.matchAll(zoteroRegex)];

    if (allMatches.length > 0) {
        let createdBlockIds = [];
        // Split by paragraphs while preserving line breaks
        const paragraphs = selection.split(/\n{2,}/);
        
        const updatedParagraphs = paragraphs.map(paragraph => {
            // Check if this paragraph already has an inline block ID
            const existingBlockId = paragraph.match(blockIdInlineRegex);
            if (existingBlockId) {
                console.log("Paragraph already has inline block ID:", existingBlockId[1]);
                createdBlockIds.push(existingBlockId[1]);
                return paragraph; // Keep paragraph unchanged
            }
            
            // No existing block ID found, process for Zotero links
            zoteroRegex.lastIndex = 0;
            const match = zoteroRegex.exec(paragraph);
            if (match) {
                const combinedId = `${match[1]}-${match[2]}`;
                createdBlockIds.push(combinedId);
                
                // Add the block ID to the end of the paragraph
                return `${paragraph.trim()} ^${combinedId}`;
            }
            
            return paragraph;
        });

        // Replace the selection or the current line
        if (hasSelection) {
            editor.replaceSelection(updatedParagraphs.join('\n\n'));
        } else {
            const cursor = editor.getCursor();
            editor.setLine(cursor.line, updatedParagraphs[0]);
        }

        if (createdBlockIds.length === 1 && !currentFile.basename.startsWith('@')) {
            const clipboardText = `![[${currentFile.basename}#^${createdBlockIds[0]}]]`;
            await navigator.clipboard.writeText(clipboardText);
            new obsidian.Notice(`Link ${clipboardText} has been added to the clipboard`);
        } else if (createdBlockIds.length > 1) {
            new obsidian.Notice('Multiple block IDs created. No link added to the clipboard.');
        }
    } else {
        // Handle regular text without Zotero links
        // Remove trailing whitespace
        selection = selection.replace(/\s+$/, '');
        
        // Generate random block ID
        const randomBlockId = Array(6).fill(0)
            .map(() => 'abcdefghijklmnopqrstuvwxyz0123456789'
            .charAt(Math.floor(Math.random() * 36))).join('');
        
        // Add the block ID to the text
        const newText = `${selection} ^${randomBlockId}`;
        
        // Replace the selection or the current line
        if (hasSelection) {
            editor.replaceSelection(newText);
        } else {
            const cursor = editor.getCursor();
            editor.setLine(cursor.line, newText);
        }
        
        const clipboardText = `![[${currentFile.basename}#^${randomBlockId}]]`;
        await navigator.clipboard.writeText(clipboardText);
        new obsidian.Notice(`Link ${clipboardText} has been added to the clipboard`);
    }
};

export class BlockIdCreatorPlugin extends obsidian.Plugin {
    async onload() {
        this.addCommand({
            id: 'block-id-creator',
            name: 'Block ID Creator',
            callback: () => blockIdCreator(this.app)
        });
    }
}

export async function invoke(app: obsidian.App): Promise<void> {
    return blockIdCreator(app);
}

Select line/paragraph and run the script. You can just put the cursor on the line without selecting anything and it will still work.
If the line contains a Zotero annotation link, the blockID will be made up of the item ID, otherwise a random ID will be generated. If the blockID was already created (on the same line or the next line), it copies it to the clipboard.

  • Multiple blockID’s are only created when the lines contain Zotero item ID’s. Then the clipboard will not be populated with multiple links.

Save code as Create-BlockID.ts or something (doesn’t matter actually but it must end with .ts and name it so you find the script by the name you set when it comes to assigning hotkeys).

You’ll need the CodeScript Toolkit plugin that will automatically load the .ts file, you just need to add in the settings, which folder it will look for, as I showed in this post with screenshots.

There is no other way? I mean obsidian already mark ‘^’ after a block as block-ID, so why I can’t just press enter after and get the auto-block-ID which Obsidian by itself would produce if I doing it after form 1? Thats sad.

A different workflow (a few mouse clicks) than

but you could try out:

You can make up your own block IDs. You can write, say, ^foobar26 and then that is the block ID you’d use to reference it. This can be helpful because they can be more mnemonic.

Obsidian expects some interaction from the user.
My method works with a bound shortcut, e.g. CTRL+SHIFT+B for blockID generation.
Pretty quick.
There is no such thing as auto-generation with blockID’s or any other thing, and no ^ characters at the end of the lines either.
In fact, a block differs from a line in Obsidian. Why would Obsidian pollute lines or blocks of texts automatically? It’s only those lines or blocks that the user wants should be treated, no?

Yes, but that is not what I ment. I also just not wanna block ID’s messing up my doc. I just don’t wanna look always trough my file, and searching for the biggest ID.

If I would have:

Block1^1

Block1^3

Block1^2

I would after give the next Block a 3 or just would search for ever, for the last given ID.

And if I wanna have a automatic ID I always have to search again and again for the file, where I am in currently.

So both ways combined would be the goal.

I write Block - press ^ - press Enter and getting the next bigger ID, what would be in my example, 4.