Memrise card template [support thread]

The bar at the top of your screenshot isn’t a part of a card. It likely comes from an add-on, but I don’t recognize it. If you can ask the author of the screenshot directly, that would probably be the best option for finding out where exactly does it come from. You can also ask around here on the forum in a separate thread, where other people are more likely to see the question. Someone might recognize it and help you out (my guess would be that @Shigeyuki is likely to know).

I have plans on adding the timer into Memrise card template itself as well, similarly to how it functioned on the old site, but it likely won’t be a part of either of the imminent updates. I’ve logged in your request, so that I’ll remember to notify you when I eventually come around to finishing it.

If you only need it for the deck icons, that add-on is obsolete (and I recommend to uninstall it), since this functionality is now built into the main support add-on for the memrise cards. You can find detailed instructions for setting up the icons in the last update post here: Memrise card template [support thread] - #346 by Eltaurus

You don’t have to concern yourself that much about it​:sweat_smile:. I genuinely appreciate any activity in this thread, so if you have any further questions or require clarification on the answer above, feel free to get back at me at your convenience.

Probably that’s the version of the progress bar add-on I fixed, as far as I know these are the progress bar add-ons that work fine with the latest version of Anki (for desktop).

New Basic card type and note type manager options

The support add-on is updated with a new Note Type, which is designed to be a cross-platform substitute for Anki “basic…” Note Types. By itself, it has minimal content and is intended to serve as a basis for original card templates, while including complete boilerplate, automatically assembled to make all essential Anki features fully functional on all platforms. This covers typing and error highlights for AnkiWeb (which also makes it accessible for card scripts on AnkiMobile, unlike the stock typing field), working audio buttons and TTS for AnkiWeb (which can also be used as alternative for [sound:] on all platforms to allow for more control over audio playback from card scripts), and style normalization to unify the appearance produced by card styles between desktop, web, and mobile.

New cross-platform basic note type can be created the exact same way as the stock basic types: ToolsManage Note TypesAdd → “Add: Basic (Lτ)” → Ok → type in a name → Ok.


The creation of a new Memrise note type can now be initiated from this window as well: selecting “Add: Memrise (Lτ)” from the list opens the full customization dialog with all the options provided by the template.

Card info (minor add-on update)

The add-on now makes certain learning info available to templates so the card appearance can be adjusted accordingly.

For example, @trongan04032002 and @butter_muffin previously asked about displaying full card details the first time it is studied instead of starting with a test right away:

Now this can be implemented by adding

(()=>{
  infoL = document.getElementById("lt-card-info");
  if (!infoL || !!document.getElementById('backwrap')) return;
  info = JSON.parse(infoL.textContent);
  if (info.reviewCount === 0 && window.pycmd) {
    pycmd("ans");
  }
})();

to the front sides (user scripts section) of a Memrise template, and adding

(()=>{
  infoL = document.getElementById("lt-card-info");
  if (!infoL) return;
  info = JSON.parse(infoL.textContent);
  if (info.reviewCount === 0) {
    MemFlip(true);
  }
})();

to the user posterior scripts section of the back template.

Unfortunately, there will likely remain a brief flash of a test side on the first card viewing, because Anki has to go through the front to get to the back details. Although this is corrigible too, that will require a bit more drastic modifications to the template, so I won’t post them here unless someone really wants to dig into the matter.


As another usecase, if there still are people interested in displaying card learning progress in the old memrise style with flower images, this also became possible


In general, any other template can make use of the card info (as long as the card is reviewed in the desktop app and the support add-on is installed and updated to the latest version). The minimal code required to access the data in a script:

info = JSON.parse(document.getElementById("lt-card-info")?.textContent || "{}");

Hello, thanks for share a really complex project to the community, i wanted to ask if you can help me with a question i have about your code, i have a interpretation of your code but not sure if its correct:
In the front when the user writes the answer in the input the code share the information that the user puts in a global variable, and then you have a temporal frontside in which the information its evaluated and then you show more things.
My doubt is how you share that information i dont understand how the global variable just stays in the back and then its deleted?

Submitted answer is passed using standard sessionStorage. You can find the minimal code for typing and error comparison here (also in the Basic Note Type provided by the add-on).

I don’t really understand the second part of your question. If you are referring to the switch between displaying the question form and extra info on the backside, this is done by putting each layout in a respective container, which in turn are assigned display: none to leave only one on the screen at a time.

Memrise-styled progress bar can be incorporated into to the template cards with the help of the Progression add-on:

The style for recreating the original design (including the bouncing change animation, excluding the corner aliasing bug) can be found below. It does not require any setup beyond copy-pasting as a whole to the ⚙️ USER STYLES ⚙️ section in the Styling tab of the Card Type editor:

#lt-progress-bar::before, /* resets */
#lt-progress-bar::after,
#lt-progress-bar *::before,
#lt-progress-bar *::after {
    display: none;
}
#lt-progress-bar,
#lt-progress-bar * {
    all: revert;
}


#lt-progress-bar {
    display: flex;
    flex-wrap: wrap;
    height: fit-content;
    box-sizing: border-box;
    background: var(--col-input-bg);

    position: static;
    max-width: calc(60rem - 6.75rem);
    min-width: 13rem;
    box-sizing: content-box;

    width: calc(100vw - 2 * 1rem);
    margin: 1rem auto calc(0rem);

    border-radius: 0.25rem;
    overflow: hidden;
}

#lt-progress-bar span {
    display: none;
}

body.card:has(#lt-progress-bar) {
    min-height: calc(100vh - 1rem);
}

@media (min-width: 33.25rem) {
  #lt-progress-bar {
    margin: 2rem auto calc(-2rem - 0rem);
  }
  body.card:has(#lt-progress-bar) {
    min-height: calc(100vh - 2rem);
  }
}

@media (min-width: 38.25rem) {
  #lt-progress-bar {
    margin: 2rem auto calc(-2rem - 1rem);
    width: calc(100vw - 2 * 2rem);
  }
}

@media (min-width: 53.25rem) {
  #lt-progress-bar {
    margin: 2rem auto calc(-2rem - 2rem);  /* height + top-margin */
  }
}

#lt-progress-bar .prog-segment.done:not(.empty):not(:has(~ .done:not(.empty))) {
    border-radius: 0 0.25rem 0.25rem 0;
}

#lt-progress-bar .prog-segment {
    flex-basis: 0;
    flex-grow: var(--count);
    height: 2rem;
}

#lt-progress-bar .prog-segment.done {
    background: var(--col-audio-bg);
    order: -1;
}

#lt-progress-bar .prog-segment.future,
#lt-progress-bar .prog-segment.cooldown {
    display: none;
}

#lt-progress-bar .prog-segment.animated {
    transition: flex-grow 0.2s;
    transition-timing-function: cubic-bezier(.28, 2.0, .77, .77);	
}

Additional adjustments can be appended to further differentiate between different Anki card states (new/review/learn/relearn) in the not completed segment of the progress bar.

(pinging @tienppham2 as a follow-up to the progress bar discussion).

I have the “Memrise (Lτ)” note types. I installed them a while ago. Today I decided to install the Anki ones I created two weeks ago: Releases · ankitects/anki · GitHub
File “anki-26.05-win-x64.msi”.
Anki 26.05 (e64c6b1a) (ao)
Python 3.13.13 Qt 6.11.0 PyQt 6.11.0
Platform: Windows-10-10.0.19045

What else does the addon do? https://ankiweb.net/shared/info/884199977

I installed it because I thought there might be something new in the design of the “Memrise (Lτ)” note types. I needed to analyze the “Close” note type and figure out what could be changed and improved. I don’t really use your addons, but overall they’re useful, of course.

I took screenshots. I had other addons at the beginning, too. But okay, I’ve only kept yours, and still, right after I start using Anki, it constantly loads and leaks memory.





The errors in the screenshots are coming from the deck thumbnails. Since JS doesn’t have a direct access to the collection folder, it can’t detect the correct extensions for the thumbnails beforehand. So it relies on cycling through the predefined list until a file is loaded successfully or the list is exhausted. Errors coming from missing assets also cannot be caught by JS, so they are left in the console. If you don’t use any thumbnails, you can disable this whole part of the add-on by setting “deck styling” to false in the configs.

I can’t think of the way the urls can get down to what is shown in your case, though. They should contain names for the decks as well as the prefix _thumb_. Can you share the names for the decks on your main screen? Is the “default” deck kept there?

I’m not sure what part of the screenshots should indicate a memory leakage, for me enabling the add-on has barely any influence on the ram usage (my specs are the same, except for win11 instead of 10). The add-on also doesn’t do anything with the mpv player, so I assume the running process is from some other activity inside Anki.

Yes, it helped.

The error only occurred in one profile, which had a lot of decks, about 9 screens down, or 59 if you’re looking at it on the web.
The AI ​​suggested this solution, and the error also went away.

/*
function retry(imgL) {
  const currentExt = imgL.src.split('.').pop();
  const i = extList.indexOf(currentExt) + 1;
  if (i < extList.length) {
    imgL.src = imgL.src.slice(0, -currentExt.length) + extList[i];
  } else {
    imgL.remove();
  }
}
function thumbHTML(deckname) {
  if (typeof(extList) === 'undefined' || !extList[0]) return ``;
  return `<img src='_thumb_${deckname}.{extList[0]}' height="0" width="0" onload="this.classList.add('deckthumb');this.removeAttribute('height');this.removeAttribute('width')" onerror="retry(this)"\>`;
}
*/

function retry(imgL) {
    // Infinite loop protection
    if (!imgL.dataset.retryCount) {
        imgL.dataset.retryCount = 0;
    }
    
    if (parseInt(imgL.dataset.retryCount) >= extList.length) {
        imgL.remove();
        return;
    }
    
    imgL.dataset.retryCount = parseInt(imgL.dataset.retryCount) + 1;
    
    try {
        const url = imgL.src;
        const baseUrl = url.split('?')[0].split('#')[0];
        const lastDot = baseUrl.lastIndexOf('.');
        
        if (lastDot === -1) {
            imgL.remove();
            return;
        }
        
        const currentExt = baseUrl.substring(lastDot + 1);
        const baseName = baseUrl.substring(0, lastDot);
        const i = extList.indexOf(currentExt) + 1;
        
        if (i < extList.length && extList[i]) {
            imgL.src = baseName + '.' + extList[i];
        } else {
            imgL.remove();
        }
    } catch (e) {
        imgL.remove();
    }
}

function thumbHTML(deckname) {
    if (typeof(extList) === 'undefined' || !extList || !extList[0]) return '';
        
    return `<img src='_thumb_${deckname}.${extList[0]}' 
                 height="0" width="0" 
                 onload="this.classList.add('deckthumb');this.removeAttribute('height');this.removeAttribute('width')" 
                 onerror="retry(this)">`;
}

Thank you for the information. From the code alone, I can’t find what is so functionally different between the two implementations and what edge case throws the original one into an infinite loop. But I’ll probably move the thumbnail file search to the python section at some point anyway, as console errors do get increasingly annoying over time.

AI:
What are the problems in this commented code and why does uncommented code help?

Looking at the commented code and the uncommented code, here are the main problems in the commented version and how the uncommented code fixes them:

Problems in the commented code:
1. Infinite Loop Risk

// Commented code - no retry count tracking
function retry(imgL) {
  const currentExt = imgL.src.split('.').pop();
  const i = extList.indexOf(currentExt) + 1;
  if (i < extList.length) {
    imgL.src = imgL.src.slice(0, -currentExt.length) + extList[i];
  }
}

Problem: If the image keeps failing to load, the onerror handler will keep calling retry() indefinitely, creating an infinite loop.

Fix: The uncommented code tracks retry attempts with dataset.retryCount and stops after trying all extensions.

2. URL Parsing Vulnerabilities

// Commented code - naive parsing
const currentExt = imgL.src.split('.').pop();
imgL.src = imgL.src.slice(0, -currentExt.length) + extList[i];

Problems:

Doesn't handle URL parameters (? or #)

Assumes the last dot is the extension delimiter

Vulnerable to URLs with multiple dots in the path

Fix: The uncommented code properly parses URLs:

Problems:

Doesn't handle URL parameters (? or #)

Assumes the last dot is the extension delimiter

Vulnerable to URLs with multiple dots in the path

Fix: The uncommented code properly parses URLs:
const url = imgL.src;
const baseUrl = url.split('?')[0].split('#')[0]; // Remove query/hash
const lastDot = baseUrl.lastIndexOf('.'); // Find actual extension
const baseName = baseUrl.substring(0, lastDot); // Get base name

3. No Error Handling
Problem: The commented code has no try-catch block, so any unexpected error would break execution.

Fix: The uncommented code wraps the logic in a try-catch block and removes the image on any error.

4. Missing Extension Validation
Problem: The commented code doesn't check if extList[i] exists or is valid before using it.

Fix: The uncommented code checks extList[i] before constructing the new URL.

5. No Clean Exit on Failure
Problem: When all extensions are exhausted, the commented code just does nothing (the image stays broken).

Fix: The uncommented code properly removes the image when all retry attempts are exhausted.

6. HTML Attribute Issues
Problem: The commented code has {extList[0]} as a literal in the HTML string instead of concatenating properly.

Fix: The uncommented code properly concatenates: '_thumb_${deckname}.${extList[0]}'

7. Missing Self-Closing Slash
Problem: The commented code ends with \> (escaped slash) which is invalid HTML.

Fix: The uncommented code uses proper HTML: >

Key Improvements Summary:
Retry limiting - Prevents infinite loops

Proper URL parsing - Handles query strings, hash fragments, and complex paths

Error handling - Graceful failure with try-catch

Validation - Checks for valid extensions before using them

Cleanup - Removes the image when all options are exhausted

Proper string interpolation - Correct HTML generation

State tracking - Uses data attributes to track retry attempts per image

As expected of an AI response, it mostly provides general suggestions that might be valid in some other cases, but do not bear much relevance when the full context is considered. For example, the suggestion about parsing urls does not take into account that in this case, we aren’t dealing with arbitrary inputs. They are generated by the add-on itself and thus, by construction, cannot contain any parameters.