Adds option to append ID's inline (#35)

* Adds option to append ID's inline
* Images in cards can now be stored in any folder that Obsidian recognizes
* Removed unnecessary utils functions
This commit is contained in:
Shreyas 2021-06-23 12:35:01 -07:00 committed by GitHub
parent 69b9984fd7
commit c14e58272c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 85 deletions

3
.gitignore vendored
View File

@ -23,3 +23,6 @@ move.sh
# Local History for Visual Studio Code
.history/
# Gtihub
.DS_Store

134
README.md
View File

@ -1,67 +1,67 @@
# Flashcards
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/reuseman/flashcards-obsidian?style=for-the-badge&sort=semver)](https://github.com/reuseman/flashcards-obsidian/releases/latest)
![GitHub All Releases](https://img.shields.io/github/downloads/reuseman/flashcards-obsidian/total?style=for-the-badge)
Anki integration for [Obsidian](https://obsidian.md/).
## Features
🗃️ Simple flashcards with **#card**
🎴 Reversed flashcards with **#card-reverse**
📅 Spaced-only cards with **#card-spaced**
✍️ Inline style with **Question::Answer**
✍️ Inline style reversed with **Question:::Answer**
🧠 **Context-aware** mode
🏷️ Global and local **tags**
🔢 Support for **LaTeX**
🖼️ Support for **images**
🔗 Support for **Obsidian URI**
⚓ Support for **reference to note**
📟 Support for **code syntax highlight**
## How it works?
The following is a demo where the three main operations are shown:
1. **Insertion** of cards;
2. **Update** of cards;
3. **Deletion** of cards.
![Demo image](docs/demo.gif)
## How to use it?
The wiki explains in detail [how to use it](https://github.com/reuseman/flashcards-obsidian/wiki).
## How to install
1. Install this plugin on Obsidian
From Obsidian v0.9.8+, you can activate this plugin within Obsidian by doing the following:
- Open Settings > Third-party plugin
- Make sure Safe mode is off
- Click Browse community plugins
- Search for "**Flashcards**"
- Click Install
- Once installed, close the community plugins window and activate the newly installed plugin
2. Install [AnkiConnect](https://ankiweb.net/shared/info/2055492159) on Anki
- Tools > Add-ons -> Get Add-ons...
- Paste the code **2055492159** > Ok
- Select the plugin > Config > Paste the configuration below
Configuration:
{
"apiKey": null,
"apiLogPath": null,
"webBindAddress": "127.0.0.1",
"webBindPort": 8765,
"webCorsOrigin": "http://localhost",
"webCorsOriginList": [
"http://localhost",
"app://obsidian.md"
]
}
# Flashcards
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/reuseman/flashcards-obsidian?style=for-the-badge&sort=semver)](https://github.com/reuseman/flashcards-obsidian/releases/latest)
![GitHub All Releases](https://img.shields.io/github/downloads/reuseman/flashcards-obsidian/total?style=for-the-badge)
Anki integration for [Obsidian](https://obsidian.md/).
## Features
🗃️ Simple flashcards with **#card**
🎴 Reversed flashcards with **#card-reverse**
📅 Spaced-only cards with **#card-spaced**
✍️ Inline style with **Question::Answer**
✍️ Inline style reversed with **Question:::Answer**
🧠 **Context-aware** mode
🏷️ Global and local **tags**
🔢 Support for **LaTeX**
🖼️ Support for **images**
🔗 Support for **Obsidian URI**
⚓ Support for **reference to note**
📟 Support for **code syntax highlight**
## How it works?
The following is a demo where the three main operations are shown:
1. **Insertion** of cards;
2. **Update** of cards;
3. **Deletion** of cards.
![Demo image](docs/demo.gif)
## How to use it?
The wiki explains in detail [how to use it](https://github.com/reuseman/flashcards-obsidian/wiki).
## How to install
1. Install this plugin on Obsidian
From Obsidian v0.9.8+, you can activate this plugin within Obsidian by doing the following:
- Open Settings > Third-party plugin
- Make sure Safe mode is off
- Click Browse community plugins
- Search for "**Flashcards**"
- Click Install
- Once installed, close the community plugins window and activate the newly installed plugin
2. Install [AnkiConnect](https://ankiweb.net/shared/info/2055492159) on Anki
- Tools > Add-ons -> Get Add-ons...
- Paste the code **2055492159** > Ok
- Select the plugin > Config > Paste the configuration below
Configuration:
{
"apiKey": null,
"apiLogPath": null,
"webBindAddress": "127.0.0.1",
"webBindPort": 8765,
"webCorsOrigin": "http://localhost",
"webCorsOriginList": [
"http://localhost",
"app://obsidian.md"
]
}

View File

@ -56,7 +56,7 @@ export default class ObsidianFlashcard extends Plugin {
}
private getDefaultSettings(): ISettings {
return { contextAwareMode: true, sourceSupport: false, codeHighlightSupport: false, contextSeparator: " > ", deck: "Default", flashcardsTag: "card", defaultAnkiTag: "obsidian" }
return { contextAwareMode: true, sourceSupport: false, codeHighlightSupport: false, inlineID: false, contextSeparator: " > ", deck: "Default", flashcardsTag: "card", defaultAnkiTag: "obsidian" }
}
private generateCards(activeFile: TFile) {

View File

@ -46,6 +46,6 @@ export class Inlinecard extends Card {
}
public getIdFormat(): string {
return "\n^" + this.id.toString()
return "^" + this.id.toString()
}
}

View File

@ -55,6 +55,17 @@ export class SettingsTab extends PluginSettingTab {
plugin.saveData(plugin.settings)
})
)
new Setting(containerEl)
.setName("Inline ID support")
.setDesc("Add ID to end of line for inline cards.")
.addToggle((toggle) =>
toggle
.setValue(plugin.settings.inlineID)
.onChange((value) => {
plugin.settings.inlineID = value
plugin.saveData(plugin.settings)
})
)
new Setting(containerEl)
.setName("Default deck")

View File

@ -38,7 +38,11 @@ export class Regex {
this.flashscardsWithTag = new RegExp(str, flags)
// https://regex101.com/r/Ixtzlv/1
str = "( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(:{2,3}) ?(.+?)((?: *#[\\p{Letter}-]+)+|$)(?:\\n\\^(\\d{13}))?"
if (settings.inlineID){
str = "( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(:{2,3}) ?(.+?)((?: *#[\\p{Letter}-]+)+)?(?:\\s+\\^(\\d{13})|$)"
} else {
str = "( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(:{2,3}) ?(.+?)((?: *#[\\p{Letter}-]+)+|$)(?:\\n\\^(\\d{13}))?"
}
this.cardsInlineStyle = new RegExp(str, flags)
// https://regex101.com/r/HOXF5E/1

View File

@ -3,9 +3,10 @@ import { App, FileSystemAdapter, FrontMatterCache, Notice, parseFrontMatterEntry
import { Parser } from 'src/services/parser'
import { ISettings } from 'src/settings'
import { Card } from 'src/entities/card'
import { arrayBufferToBase64 } from "src/utils"
import { arrayBufferToBase64} from "src/utils"
import { Regex } from 'src/regex'
import { noticeTimeout } from 'src/constants'
import { Inlinecard } from 'src/entities/inlinecard'
export class CardsService {
@ -43,6 +44,7 @@ export class CardsService {
this.totalOffset = 0
this.notifications = []
let filePath = activeFile.basename
let sourcePath = activeFile.path
let fileCachedMetadata = this.app.metadataCache.getFileCache(activeFile)
let vaultName = this.app.vault.getName()
let globalTags: string[] = undefined
@ -69,7 +71,7 @@ export class CardsService {
let cards: Card[] = this.parser.generateFlashcards(this.file, deckName, vaultName, filePath, globalTags)
let [cardsToCreate, cardsToUpdate] = this.filterByUpdate(ankiCards, cards)
let cardIds : number[] = this.getCardsIds(ankiCards, cards)
let cardIds: number[] = this.getCardsIds(ankiCards, cards)
let cardsToDelete: number[] = this.parser.getCardsToDelete(this.file)
console.info("Flashcards: Cards to create")
@ -79,7 +81,7 @@ export class CardsService {
console.info("Flashcards: Cards to delete")
console.info(cardsToDelete)
this.insertMedias(cards)
this.insertMedias(cards, sourcePath)
await this.deleteCardsOnAnki(cardsToDelete, ankiBlocks)
await this.updateCardsOnAnki(cardsToUpdate)
await this.insertCardsOnAnki(cardsToCreate, frontmatter, deckName)
@ -116,11 +118,11 @@ export class CardsService {
}
}
private async insertMedias(cards: Card[]) {
private async insertMedias(cards: Card[], sourcePath: string) {
try {
// Currently the media are created for every run, this is not a problem since Anki APIs overwrite the file
// A more efficient way would be to keep track of the medias saved
await this.generateMediaLinks(cards)
await this.generateMediaLinks(cards, sourcePath)
await this.anki.storeMediaFiles(cards)
} catch (err) {
console.error(err)
@ -128,16 +130,15 @@ export class CardsService {
}
}
private async generateMediaLinks(cards: Card[]) {
private async generateMediaLinks(cards: Card[], sourcePath: string) {
if (this.app.vault.adapter instanceof FileSystemAdapter) {
// @ts-ignore: Unreachable code error
let attachmentsPath = this.app.vault.config.attachmentFolderPath
for (let card of cards) {
for (let media of card.mediaNames) {
let file: TFile = this.app.vault.getAbstractFileByPath(attachmentsPath + "/" + media) as TFile
let image = this.app.metadataCache.getFirstLinkpathDest(decodeURIComponent(media), sourcePath);
try {
let binaryMedia = await this.app.vault.readBinary(file)
let binaryMedia = await this.app.vault.readBinary(image)
card.mediaBase64Encoded.push(arrayBufferToBase64(binaryMedia))
} catch (err) {
Error("Error: Could not read media")
@ -208,6 +209,13 @@ export class CardsService {
// if it has been inserted it has an ID too
if (card.id !== null && !card.inserted) {
let id = card.getIdFormat()
if (card instanceof Inlinecard) {
if (this.settings.inlineID) {
id = " " + id
} else {
id = "\n" + id
}
}
card.endOffset += this.totalOffset
let offset = card.endOffset
@ -296,7 +304,7 @@ export class CardsService {
return [cardsToCreate, cardsToUpdate]
}
public async deckNeedToBeChanged(cardsIds : number[], deckName: string) {
public async deckNeedToBeChanged(cardsIds: number[], deckName: string) {
let cardsInfo = await this.anki.cardsInfo(cardsIds)
console.log("Flashcards: Cards info")
console.log(cardsInfo)
@ -307,10 +315,10 @@ export class CardsService {
return false
}
public getCardsIds(ankiCards: any, generatedCards: Card[]) : number[] {
let ids : number[] = []
public getCardsIds(ankiCards: any, generatedCards: Card[]): number[] {
let ids: number[] = []
if (ankiCards) {
if (ankiCards) {
for (let flashcard of generatedCards) {
let ankiCard = undefined
if (flashcard.inserted) {
@ -323,7 +331,7 @@ export class CardsService {
return ids
}
public parseGlobalTags(file: String) : string[] {
public parseGlobalTags(file: String): string[] {
let globalTags: string[] = []
let tags = file.match(/(?:cards-)?tags: ?(.*)/im)

View File

@ -2,6 +2,7 @@ export interface ISettings {
contextAwareMode: boolean
sourceSupport: boolean
codeHighlightSupport: boolean
inlineID: boolean
contextSeparator: string
deck: string
flashcardsTag: string

View File

@ -1,3 +1,5 @@
import { Vault, TFile} from 'obsidian';
export function arrayBufferToBase64(buffer: ArrayBuffer): string {
var binary = "";
var bytes = new Uint8Array(buffer);
@ -43,4 +45,4 @@ export function escapeMarkdown(string: string, skips: string[] = []) {
? string
: string.replace(replacement[0], replacement[1]);
}, string);
}
}