obsidiflip
obsidiflip copied to clipboard
Interactive Obsidian-Anki flashcards that flip and fade using CSS and two plugins
[^1]
🃏 Obsidiflip Readme
Interactive Obsidian-Anki flashcards that flip and fade using CSS and two plugins.
📜 Features
- Works in Live Preview and reading mode.
- Multi-line fields and custom note types supported.
- Cloze supported, with optional tradeoff of cleanliness for cloze numbering.
- Multiple flashcards arranged in a row in reading mode. Customizable min and max width.
🎥 Demo
Recent Changes
2022-09-30: Updated regex, added link to patched plugin
📑 Table of Contents
- 📜 Features
- 🎥 Demo
- Recent Changes
- 📑 Table of Contents
- 💿 Prerequisites
-
🧑💻 Installation and Usage
- 📞 Admonition Plugin Settings
- 🪟 CSS
-
🪨 Obsidian to Anki Plugin Settings
-
Cloze settings
- Highlight option
- Uglier, but more flexible option
-
🎴 Regex and corresponding sample flashcards
- General Principles
- Forward (Basic)
- Revealed Context
- Basic Anking
- Complex
- Cloze
- Cloze Write
- 🖼️ Modding the plugin to support images
-
Cloze settings
-
🎨 Customization
- Custom card types
- Card width
- Show front and back admonition title bar
-
💥 Troubleshooting
- Note type not showing
- Wrong field order
-
Other Obsidian-to-Anki Plugin Patches
- Fix Regenerate Note Type Button
💿 Prerequisites
Please have these apps and plugins installed and working.
- Obsidian
- Anki
- Obsidian Plugins
-
Obsidian to Anki
- Requires Anki plugin Anki Connect
- Obsidian Admonition
-
Obsidian to Anki
- Highly recommended: a text expander like espanso
- If you don't want to manually patch the plugin, I have patched the plugin here
- Please download
main.js
from the releases tab here
- Please download
🧑💻 Installation and Usage
📞 Admonition Plugin Settings
In the admonition plugin settings, please add these 6 admonition types. Feel free to customize the title, title display option, copy button, icon, and color, but don't change the admonition type unless you plan to modify the CSS and regex.
Flip Card | Fade Card |
---|---|
anki |
cloze |
anki-front |
cloze-front |
anki-back |
cloze-back |
🪟 CSS
- Go to Settings -> Appearance -> CSS Snippets and open your CSS Snippet folder.
- Click
obsidiflip.css
in this repository, right click raw, and save as
- Save
obsidiflip.css
to your snippets folder, refresh the CSS settings, and enable it.
🪨 Obsidian to Anki Plugin Settings
Cloze settings
Highlight option
- Enable the CurlyCloze option.
- Enable the Curlycloze -> Highlights to Clozes option.
- Clozes must be in the highlight syntax for the fade cloze cards to work properly. This unfortunately means no support for cloze field numbering.
Uglier, but more flexible option
This enables cloze numbering, but will highlight all clozes in Anki and will look messier in Obsidian.
- Enable the CurlyCloze option.
- Format clozes like this
=={1:cloze text}==
while making sure the Curlycloze -> Highlights to Clozes option is disabled (since the CSS relies on the highlight for fading in and out).- Please refer to the CurlyCloze documentation for formatting options
- If you want to use an element other than highlights such as italics or bold, you have to edit the CSS. Look for replacing the
mark
tag.
🎴 Regex and corresponding sample flashcards
Skip to next section
Skip to cloze cards
Please let me know in github issues if any of the regex isn't working properly or has compatibility issues.
General Principles
- Please use a text expander like espanso (and set
force_clipboard: true
)! Typing the templates in manually is slow. - Flip cards always contain
ad-anki-front
andad-anki-back
- I use horizontal rules with asterisks (
***
) to separate fields on the same card face. Horizontal rules with dashes will unintentionally create h2 headings. - Card types are identified by a comment on its own line after
ad-anki
and beforead-anki-front
. Ex.%%anki-forward%%
- Admonition plugin options, like title, can be set before this comment
- Field order in Anki must be the same as the regex capture group order. See Field Order Troubleshooting for details.
Forward (Basic)
This is a basic card with only front and back fields
Markdown
`````ad-anki
%%anki-forward%%
````ad-anki-front
Front
````
````ad-anki-back
Back
````
`````
Regex
%%anki-forward%%[\s\S]+?````ad-anki-front\n([\s\S]*?)````[\s\S]+?````ad-anki-back\n([\s\S]*?)````
Revealed Context
This is a personal forward and reversed card. It shows revealed context on the back of the card (such as sentences in a foreign language) in both forward and reversed mode.
Markdown
`````ad-anki
%%anki-revealed-context%%
````ad-anki-front
Front
````
````ad-anki-back
Back
***
Revealed Context
````
`````
Regex
%%anki-revealed-context%%$[\s\S]+?^````ad-anki-front$\n([\s\S]*?)^````$[\s\S]+?^````ad-anki-back$\n([\s\S]*?)(?:\*\*\*\n([\s\S]*?))?^````$\n
Basic Anking
On request by reddit user opendoorz.
Markdown
`````ad-anki
%%anki-basic-anking%%
````ad-anki-front
front
````
````ad-anki-back
back
***
personal notes
***
missed questions
````
`````
Regex
%%anki-basic-anking%%[\s\S]+?````ad-anki-front\n([\s\S]*?)````[\s\S]+?````ad-anki-back\n([\s\S]*?)(?:\*\*\*\n([\s\S]*?))?(?:\*\*\*\n([\s\S]*?))?````
Complex
I don't actually use this card. It's for demo purposes to show the flexibility of the format. The last ***
is necessary to keep the id within ad-anki
and allow for flashcards appearing next to each other.
Markdown
`````ad-anki
title:Complex Flashcard
%%anki-complex%%
````ad-anki-front
this is the front of the card
***
this context should show up on the front of the card
````
````ad-anki-back
this is the back of the card
***
this context should show up on the back of the card
````
this shows up on both sides
***
`````
Regex
%%anki-complex%%[\s\S]+?````ad-anki-front\n([\s\S]*?)(?:\*\*\*\n([\s\S]*?))?````[\s\S]+?````ad-anki-back\n([\s\S]*?)(?:\*\*\*\n([\s\S]*?))?````\n([\s\S]*?)\*\*\*\n
Cloze
A basic cloze card with some context
Markdown
`````ad-cloze
%%anki-cloze%%
````ad-cloze-front
This is a ==cloze== card
````
````ad-cloze-back
Here is some extra context
````
`````
Regex
%%anki-cloze%%[\s\S]+?````ad-cloze-front\n([\s\S]*?)````[\s\S]+?````ad-cloze-back\n([\s\S]*?)````\n
Cloze Write
My custom cloze card with a question field, and you type in the answers.
Markdown
`````ad-cloze
%%anki-cloze-write%%
````ad-cloze-front
Write your question here
***
This is a ==cloze== card with ==two== fields.
````
````ad-cloze-back
Here is some extra context
````
`````
Regex
^%%anki-cloze-write%%$[\s\S]+?^````ad-cloze-front$\n([\s\S]*?)\*\*\*\n([\s\S]*?)^````$[\s\S]+?^````ad-cloze-back$\n([\s\S]*?)^````$\n
🖼️ Modding the plugin to support images
Unfortunately the Obsidian to Anki plugin hasn't seen any updates in a while, so we need to edit it to support images (see this issue). Open main.js
in your favorite editor, and comment out the getAndFormatMedias
function (as backup). Add this right under it:
getAndFormatMedias(note_text) {
// By Jeff Chiou thanks to TobiasKlosek's code https://github.com/Pseudonium/Obsidian_to_Anki/issues/245
// TODO: support image resize format (ex. ![[og-image.png|200]])
// PLAN: create escaped regex pattern for images and audio to capture link and size separately. Add style tag to <img>.
let hasAudio = AUDIO_EXTS.some(ext => note_text.includes(ext));
let hasImage = IMAGE_EXTS.some(ext => note_text.includes(ext));
if (hasAudio || hasImage) {
let matches = note_text.match(/\!\[\[(.*)\]\]/g);
matches.forEach(match => {
let link = match.substring(3, match.length - 2);
this.detectedMedia.add(link);
if (AUDIO_EXTS.includes(path.extname(link))) {
note_text = note_text.replace(new RegExp(escapeRegex(match), "g"), "[sound:" + path.basename(link) + "]");
}
else if (IMAGE_EXTS.includes(path.extname(link))) {
note_text = note_text.replace(new RegExp(escapeRegex(match), "g"), '<img src="' + path.basename(link) + '" alt="' + link + '">');
}
else {
console.warn("Unsupported extension: ", path.extname(link));
}
});
}
return note_text;
}
I have plans to mod the plugin further but it is GPL licensed and this repo is MIT, so I might make a new repo so I can include a main.js
file.[^2]
🎨 Customization
Custom card types
See the General Principles section for custom card types.
Card width
If you want to see more or fewer flashcards next to each other you can either adjust your readable line length or the min width and max width here:
.admonition-anki-parent {
display: inline-grid;
margin: 10px;
min-width: 230px;
max-width: 471px;
}
See this custom line length CSS from knappsacks. Readable line length needs to be enabled.
Show front and back admonition title bar
If you like this aesthetic better you can comment out / remove these lines from the css:
.admonition-anki-front, .admonition-anki-back {
padding: 0;
border: 0;
box-shadow: none !important;
}
.admonition-anki-front .admonition-content, .admonition-anki-back .admonition-content {
border: none !important;
padding: 0;
}
.admonition-anki-front .admonition-title, .admonition-anki-back .admonition-title {
display: none;
}
💥 Troubleshooting
Note type not showing
If your note type isn't showing under the obsidian to anki plugin settings, you have to either uninstall and reinstall the plugin, add the note types manually, or mod the plugin.
See this section to patch the plugin to fix the regenerate note types button. See additional details at this commit and this issue.
Wrong field order
If your fields aren't in the right order, do this:
- Close Obsidian
- Reposition the fields in Anki with Tools -> Manage Note Types -> Fields -> Reposition. This requires a deck re-sync.
- In the Obsidian-to-Anki plugin folder, look at
data.json
and thefields_dict
section. Reorder the fields here for your note type and save and close. - Reopen Obsidian and do an Obsidian-to-Anki sync.
- Reopen
data.json
and double check the field order is correct.
Other Obsidian-to-Anki Plugin Patches
Modify at your own risk! I make no guarantees around data loss or corruption.
Fix Regenerate Note Type Button
Patch thanks to tlgman's pull request
Find the section of main.js
that starts like this:
new obsidian.Setting(action_buttons)
.setName("Regenerate Note Type Table")
.setDesc("Connect to Anki to regenerate the table with new note types, or get rid of deleted note types.")
Replace this:
plugin.note_types = await invoke('modelNames');
plugin.regenerateSettingsRegexps();
plugin.fields_dict = await plugin.loadFieldsDict();
if (Object.keys(plugin.fields_dict).length != plugin.note_types.length) {
new obsidian.Notice('Need to connect to Anki to generate fields dictionary...');
try {
plugin.fields_dict = await plugin.generateFieldsDict();
new obsidian.Notice("Fields dictionary successfully generated!");
}
catch (e) {
new obsidian.Notice("Couldn't connect to Anki! Check console for error message.");
return;
}
}
With this:
new obsidian.Notice("Need to connect to Anki to generate fields dictionary...");
plugin.note_types = await invoke('modelNames');
plugin.regenerateSettingsRegexps();
plugin.fields_dict = await plugin.loadFieldsDict();
try {
plugin.fields_dict = await plugin.generateFieldsDict();
new obsidian.Notice("Fields dictionary successfully generated!");
}
catch (e) {
new obsidian.Notice("Couldn't connect to Anki! Check console for error message.");
return;
}
Basically we are just removing the if statement check.
[^1]: Generated by Stable Diffusion locally on my laptop RTX 3080. Prompt was a variant of "thin shiny sharp black obsidian flashcards with shiny gold neurons on the front."
[^2]: I don't have the resources and knowledge to support a full fork yet.