How to recover data that went to 'Untitled' or 'Unnamed' document?

This is not about restoring deleted rems. If that is what you are looking check this - How to recover my lost data or document?

Sometimes things get messed up and some of your data gets removed from its original document and magically ends up in a document called Untitled or Unnamed like this
image

If you search for it and try to open it, you may not find it (or able open it).

This Path rem (along with bunch of other stuff) is supposed to be in my Gimp document, but what I have now in Gimp is this

image

One way to access these lost rems is as follows

  1. If you don’t know the name of your top level rems, use this code snippet from @hannesfrank and find out all your top level rems Find out empty top level rems

  2. Go to some document and create a portal to one of these rems from the Untitled document.

image

image

  1. Then essentially you can recursively expand (Ctrl + Shift + Down Key) this portal and move your rems out of this Unnamed document as follows. And maybe delete that ‘Unnamed’ doc to avoid confusion

Thank you. I have the same issue, which I described here.

  1. Basically, we need to manually restore those rems to their original location? I was hoping mods will somehow automatically restore this to a state before this happened (December 3rd).
  2. Has anyone experience data loss? Besides this documents splitting.

@Mechkalito Contacting Remnote support (through any means) is your option to restore the state

@liberated_potato This is pretty strange. Actually my current lnotes database version is 24 and there is a quanta. I refreshed the page but maybe you have some updates I don’t have. Can’t say what the problem is for you. Works flawlessly for me with version 24:

Yea, not sure what is happening in my case :roll_eyes:

Here is a snippet to find those (empty) top-level rem. Paste it in the DevTools (F12) console. And click a button that appeared on the bottom of a page.

function getRemText(remId, remStore, exploredRem=[]) {
    let rem = remStore[remId];
    if (!rem) return;

    const richTextElementsText = rem.key.map((richTextElement)=>{
        // If the element is a string, juts return it
        if (typeof richTextElement == 'string') {
            return richTextElement;
            // If the element is a Rem Reference (i == "q"), then recursively get that Rem Reference's text.
        } else if (richTextElement.i == 'q' && !exploredRem.includes(richTextElement._id)) {
            return getRemText(richTextElement._id, remStore, exploredRem.concat([richTextElement._id]));
        } else {
            // If the Rem is some other rich text element, just take its .text property.
            return richTextElement.text;
        }
    }
    );
    return richTextElementsText.join('');
}

(function() {
    let test = true;
    let targetContainerId = 'ReferenceLinks';
    let buttonId = 'list-reference-button';
    let listId = 'reference-list';

    let linksContainer = document.getElementById(targetContainerId);

    if (!test && document.getElementById(buttonId))
        return;

    function compareChildren(map) {
        return (aId, bId) => {
            const a = map[aId];
            const b = map[bId];
            return a.numChildren > b.numChildren ? -1 : 1;
        }
    }

    function compareText(map) {
        return (aId, bId) => {
            if (aId == bId) return 0;
            const ta = map[aId].text;
            const tb = map[bId].text;
            if (ta === tb) {
                return compareChildren(map)(aId, bId);
            }
            if (ta === undefined) return 1;
            if (tb === undefined) return -1;
            return ta.toLowerCase() < tb.toLowerCase() ? -1 : 1;
        }
    }

    let button = document.createElement('input');
    button.type = 'button';
    button.value = "List top-level Rem by most children";
    button.onclick = () => listRem(compareChildren);
    linksContainer.appendChild(button);

    let button2 = document.createElement('input');
    button2.type = 'button';
    button2.value = "List top-level Rem alphabetically";
    button2.onclick = () => listRem(compareText);
    linksContainer.appendChild(button2);
    
    function listRem(compare) {
        if (!test && document.getElementById(listId))
            return;

        query((rems)=>{
            let remStore = Object.fromEntries(rems.map(rem => [rem._id, rem]));
            let topLevel = [];

            for (let rem of rems) {
                if (!rem.parent) {
                    topLevel.push(rem);
                }
            }
            let topLevelMap = [...topLevel].reduce((map, rem) => {
                map[rem._id] = { id: rem._id, text: getRemText(rem._id, remStore), numChildren: rem.children.length };
                return map
             }, {});
             console.warn(topLevelMap)
            let list = document.getElementById(listId) || document.createElement('ul');
            list.id = listId;
            list.innerHTML = '';

            for (let rem of Object.keys(topLevelMap).sort(compare(topLevelMap))) {
                let link = document.createElement('a');
                link.href = `https://www.remnote.io/document/${rem}`;
                link.appendChild(document.createTextNode(`${topLevelMap[rem].text} (${topLevelMap[rem].numChildren})`));
                let li = document.createElement('li');
                li.appendChild(link);
                list.appendChild(li);
            }

            linksContainer.appendChild(list);
        }
        );
    }

    function query(handler) {
        let openRequest = indexedDB.open('lnotes', 24)

        openRequest.onsuccess = ()=>{
            console.info('Connected to database.')
            let db = openRequest.result;
            let t = db.transaction('quanta');
            let quanta = t.objectStore('quanta');
            let request = quanta.getAll();
            request.onsuccess = ()=>{
                console.info('Loaded Rems');
                handler(request.result);
            }
        }
        openRequest.onerror = ()=>{
            console.warn('There was an error:', openRequest.error);
        }
        openRequest.onupgradeneeded = ()=>{
            console.warn('The database version has got an upgrade. Update this script!');
        }
    }
}
)();

This is cool. I am not able to get it to work. Just paste it and press enter? do I have to call any functions? Also, should I keep any document open when doing this?
I just paste and enter, then I see undefined in the console, but no button at the bottom

@liberated_potato yes, just paste and enter. The function is called automatically. You need an arbitrary rem open because it adds a few elements to the bottom there.

I see these exceptions and your warning about potential database upgrade. Looks like script needs an update. This script would definitely be handy. Now I am realizing that this is something you shared (or at least discussed about) in discord a while ago (if I remember correctly)

image

Yeah, the database gets pretty frequent updates these days. Replace the 24 with the next higher integer and try again.

Now I see this
image

Where is the dev tools console?

Does this method of finding empty top level rems make sense for a non-programmer user?

Thanks

@Kongulu You can open DevTools with F12 or Ctrl + Shift + I (Desktop App).

Nowadays it is easier to just go to the documents table, sort by name, Include Top-Level Rem and look for Untitled. This is also more accessible for non-technical users.

@hannesfrank
when i type this code it also shows deleted top level rems. why it still shows if it was deleted from database

Well, I do not have the code that the app uses internally. I just look what raw data is in the Local Storage. Maybe they have some extra logic to decide which Rems are in the Trash and which are not.

oh i understand thank you