diff --git a/.gitignore b/.gitignore index 4969a45a..42ca261d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# data.json need save +./data.json # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/Images/1cfcb1bb28a06e354b339691fe79a5d40c32ac3d8d74a0c1a5fd1b4f554e201a.png b/Images/1cfcb1bb28a06e354b339691fe79a5d40c32ac3d8d74a0c1a5fd1b4f554e201a.png new file mode 100644 index 00000000..a5017f48 Binary files /dev/null and b/Images/1cfcb1bb28a06e354b339691fe79a5d40c32ac3d8d74a0c1a5fd1b4f554e201a.png differ diff --git a/Images/5913712e835c128fdc7a12c0c0c1caa006ac7d47bf85a3a9f6c5970e37a0a948.png b/Images/5913712e835c128fdc7a12c0c0c1caa006ac7d47bf85a3a9f6c5970e37a0a948.png new file mode 100644 index 00000000..7c79e5e8 Binary files /dev/null and b/Images/5913712e835c128fdc7a12c0c0c1caa006ac7d47bf85a3a9f6c5970e37a0a948.png differ diff --git a/README.md b/README.md index 18b04483..a73f67de 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,53 @@ -# Obsidian_to_Anki -Plugin to add flashcards from a text or markdown file to Anki. Run in Obsidian as a plugin, or from the command-line as a python script. Built with [Obsidian](https://obsidian.md/) markdown syntax in mind. Supports **user-defined custom syntax for flashcards.** -See the [Trello](https://trello.com/b/6MXEizGg/obsidiantoanki) for planned features. - -## Getting started - -Check out the [Wiki](https://github.com/Pseudonium/Obsidian_to_Anki/wiki)! It has a ton of information, including setup instructions for new users. I will include a copy of the instructions here: - -## Setup - -### All users -1. Start up [Anki](https://apps.ankiweb.net/), and navigate to your desired profile. -2. Ensure that you've installed [AnkiConnect](https://git.foosoft.net/alex/anki-connect). - -### Obsidian plugin users -3. Have [Obsidian](https://obsidian.md/) downloaded -4. Search the 'Community plugins' list for this plugin -5. Install the plugin. -6. In Anki, navigate to Tools->Addons->AnkiConnect->Config, and change it to look like this: -
-{
-    "apiKey": null,
-    "apiLogPath": null,
-    "webBindAddress": "127.0.0.1",
-    "webBindPort": 8765,
-    "webCorsOrigin": "http://localhost",
-    "webCorsOriginList": [
-        "http://localhost",
-        "app://obsidian.md"
-    ]
-}
-
- -7. Restart Anki to apply the above changes -8. With Anki running in the background, load the plugin. This will generate the plugin settings. - - -You shouldn't need Anki running to load Obsidian in the future, though of course you will need it for using the plugin! - -To run the plugin, look for an Anki icon on your ribbon (the place where buttons such as 'open Graph view' and 'open Quick Switcher' are). -For more information on use, please check out the [Wiki](https://github.com/Pseudonium/Obsidian_to_Anki/wiki)! - -### Python script users -3. Install the latest version of [Python](https://www.python.org/downloads/). -4. If you are a new user, download `obstoanki_setup.py` from the [releases page](https://github.com/Pseudonium/Obsidian_to_Anki/releases), and place it in the folder you want the script installed (for example your notes folder). -5. Run `obstoanki_setup.py`, for example by double-clicking it in a file explorer. This will download the latest version of the script and required dependencies automatically. Existing users should be able to run their existing `obstoanki_setup.py` to get the latest version of the script. -6. Check the Permissions tab below to ensure the script is able to run. -7. Run `obsidian_to_anki.py`, for example by double-clicking it in a file explorer. This will generate a config file, `obsidian_to_anki_config.ini`. - -#### Permissions -The script needs to be able to: -* Make a config file in the directory the script is installed. -* Read the file in the directory the script is used. -* Make a backup file in the directory the script is used. -* Rename files in the directory the script is used. -* Remove a backup file in the directory the script is used. -* Change the current working directory temporarily (so that local image paths are resolved correctly). - -## Features - -Current features (check out the wiki for more details): -* **Custom note types** - You're not limited to the 6 built-in note types of Anki. -* **Custom scan directory** - * The plugin will scan the entire vault by default - * You can also set which directory (includes all sub-directories as well) to scan via plugin settings -* **Ignore Folders and Files** - * You can specify which files and folders to ignore - * This can be done in the settings of this plugin with [Glob syntax](https://en.wikipedia.org/wiki/Glob_(programming)#Syntax). - * If you're working on your own globs, you can test them out [here](https://globster.xyz/) - * Examples: - * `**/*.excalidraw.md` - Ignore all files that end in `.excalidraw.md` - * => avoids excalidraw files from being scanned which can be extremely slow - * `Template/**` - Ignore all files in the `Template` folder (including subfolders) - * `**/private/**` - Ignore all files in folders that are called `private` no matter where they are in the vault - * `[Pp]rivate*/**` - Ignore all files and folders in the root of the vault that start with `private` or with `Private` -* **Updating notes from file** - Your text files are the canonical source of the notes. -* **Tags**, including **tags for an entire file**. -* **Adding to user-specified deck** on a *per-file* basis. -* **Markdown formatting**. -* **Math formatting**. -* **Embedded images**. GIFs should work too. -* **Audio**. -* **Auto-deleting notes from the file**. -* **Reading from all files in a directory automatically** - recursively too! -* **Inline Notes** - Shorter syntax for typing out notes on a single line. -* **Easy cloze formatting** - A more compact syntax to do Cloze text -* **Frozen Fields** -* **Obsidian integration** - A link to the file that made the flashcard, full link and image embed support. -* **Custom syntax** - Using **regular expressions**, add custom syntax to generate **notes that make sense for you.** Some examples: - * RemNote single-line style. `This is how to use::Remnote single-line style` - ![Remnote 1](Images/Remnote_1.png) - * Header paragraph style. -
-  # Style
-  This style is suitable for having the header as the front, and the answer as the back
-  
- ![Header 1](Images/Header_1.png) - * Question answer style. -
-  Q: How do you use this style?
-  A: Just like this.
-  
- ![Question 1](Images/Question_1.png) - * Neuracache #flashcard style. -
-  In Neuracache style, to make a flashcard you do #flashcard
-  The next lines then become the back of the flashcard
-  
- ![Neuracache 1](Images/Neuracache_1.png) - * Ruled style -
-  How do you use ruled style?
-  ---
-  You need at least three '-' between the front and back of the card.
-  
- ![Ruled 1](Images/Ruled_1.png) - * Markdown table style -
-  | Why might this style be useful? |
-  | ------ |
-  | It looks nice when rendered as HTML in a markdown editor. |
-  
- ![Table 2](Images/Table_2.png) - * Cloze paragraph style -
-  The idea of {cloze paragraph style} is to be able to recognise any paragraphs that contain {cloze deletions}.
-  
- ![Cloze 1](Images/Cloze_1.png) - -Note that **all custom syntax is off by default**, and must be programmed into the script via the config file - see the Wiki for more details. - -Buy Me a Coffee at ko-fi.com +# 简介 +本项目由 [Obsidian_to_Anki](https://github.com/Pseudonium/Obsidian_to_Anki) fork而来 +在原来代码的基础上,添加一些功能 +1. 让obsidian markdown文件中的卡片在anki的deck的结构与文件夹结构保存一致 +例如:"/root/hello/world.md"中的卡片会自动同步到"root::hello::world"中 +2. 把id修改为blockid的形式,从而实现anki直接跳转到obsidian markdown文件对应的block +![图 1](images/5913712e835c128fdc7a12c0c0c1caa006ac7d47bf85a3a9f6c5970e37a0a948.png) +3. 优化了context的内容,路径和标题加在最前面,以换行分割,后面有一行分割线和卡片具体内容区分。标题中如果有链接或公式可以转化成功。 +4. 对于卡片中对blockid的引用链接,自动转化为anki link插件支持的格式`[link test|nid1714433449297]` +5. 优化了$的识别。现在\\\$不会识别为公式,会转化为\$。修改的格式化的顺序。现在先处理代码的部分。所以代码中出现\$不会识别为latex +# 本项目使用方法 +将main.js和manifes.json文件替换obsidian to anki中的main.js manifes.json +在插件设置中,开启设置,并重启obsidian +具体的使用方法可以参考[Obsidian_to_Anki](https://github.com/Pseudonium/Obsidian_to_Anki) + +原来的正则表达式`(?)": "", + "问答题(同时生成翻转的卡片)": "", + "问答题(输入答案)": "" + }, + "FILE_LINK_FIELDS": { + "Basic": "Front", + "Basic+": "Front", + "Basic++": "Front", + "Basic+++": "Front", + "Basic-73fde": "Front", + "Cloze ALL": "文字", + "Pot Card 2": "Front", + "Python - Basic": "Front", + "Python - Basic (and reversed card)": "Front", + "Python - Cloze": "Text", + "UVBasic-English": "英语单词", + "单词": "单词", + "图片遮盖": "遮盖", + "填空题": "文字", + "红宝书": "单词", + "问答题": "正面", + "问答题(同时生成翻转的卡片<可选>)": "正面", + "问答题(同时生成翻转的卡片)": "正面", + "问答题(输入答案)": "正面" + }, + "CONTEXT_FIELDS": { + "Cloze ALL": "文字", + "问答题": "正面" + }, + "FOLDER_DECKS": { + "未命名": "", + "第一个文件夹": "", + "第一个文件夹/第二个文件夹": "", + "norcx'anki'test": "", + "norcx'anki'test/asserts": "" + }, + "FOLDER_TAGS": { + "未命名": "", + "第一个文件夹": "", + "第一个文件夹/第二个文件夹": "", + "norcx'anki'test": "", + "norcx'anki'test/asserts": "" + }, + "Syntax": { + "Begin Note": "START", + "End Note": "END", + "Begin Inline Note": "STARTI", + "End Inline Note": "ENDI", + "Target Deck Line": "TARGET DECK", + "File Tags Line": "FILE TAGS", + "Delete Note Line": "DELETE", + "Frozen Fields Line": "FROZEN" + }, + "Defaults": { + "Scan Directory": "", + "Tag": "Obsidian_to_Anki", + "Deck": "Default", + "Scheduling Interval": 0, + "Add File Link": true, + "Add Context": true, + "CurlyCloze": false, + "CurlyCloze - Highlights to Clozes": false, + "ID Comments": true, + "Add Obsidian Tags": false, + "Use Path as Deck": true, + "Add Card link": true + }, + "IGNORED_FILE_GLOBS": [ + "**/*.excalidraw.md" + ] + }, + "Added Media": [ + "Pasted image 20240419143443.png", + "Pasted image 20240419143952.png", + "Pasted image 20240419152053.png", + "Pasted image 20240419152108.png", + "Pasted image 20240419152116.png", + "Pasted image 20240419152119.png", + "Pasted image 20240504143655.png" + ], + "File Hashes": { + "这个文件不会出现.md": "3bf0f4061ead725b9f00d10156e48c32", + "这次必须成功.md": "c8cb44b25bf0f5f3abdb489ce570dea7", + "第一个文件夹/读书.md": "d41d8cd98f00b204e9800998ecf8427e", + "第一个文件夹/读一本好书.md": "2bc012e3b6e73af53f1e2254e494d7d2", + "第一个文件夹/第二个文件夹/你好呀.md": "09639afc110da5e7033d4e6ce12147ff", + "第一个文件夹/第二个文件夹/未命名.md": "ef1e094f5b366fe563e71c1f6bcd141c", + "第一个文件夹/第二个文件夹/你好.md": "d41d8cd98f00b204e9800998ecf8427e", + "要成功了.md": "9ff4e52b269f2e12afbb019771ca51c7", + "破案吧.md": "03e25ab08d38fd6ea0c97b27ad4a4b4a", + "未命名 1.md": "4c811787fb4a175e57da923063c1e41c", + "有笔记但是没有内容.md": "284f5c9fb85e25bcd79b43b83b1c3e33", + "未命名 4.md": "295a6be3b2f9283e4d143f2dc27d7ff2", + "未命名 2.md": "30a44ea90cb104d9b16dda3826232784", + "成功.md": "bcab17d6d9aa24ec021d86219ba80275", + "你是谁.md": "7ce5d9b5499bf82ca585cd0556562db2", + "什么都没有.md": "2228e977ebea8966e27929f43e39cb67", + "README.md": "5844997ffd21e5020ecaa7670e5d942e", + "图片测试.md": "0e71ded111d1d619f9693a3b48e20cc3", + "测试文件.md": "f0ccad7d3b2091db20689782f5b34cc5", + "Welcome.md": "5dce4937496ad2d20178a7578f1a1368", + "norcx'anki'test/未命名.md": "68a672a3c453707a5db64bd4da5324c5", + "未命名.md": "63d5a30db0177e57313c15e5cb709767", + "norcx'anki'test/挖空测试.md": "08d3f20ea7d3869cf4e63b8ad32533ae", + "未命名 1 1.md": "166ac5eb16bb207e951f3c68aa1f4943", + "一次添加成功测试.md": "988168da191b9968488324f0f79360fb", + "anki测试2.md": "76a3ee4bba713fca92310ecfe0d57a30", + "$测试.md": "3bce4a5b61225248526ad4c60e1a3037" + }, + "fields_dict": { + "Basic": [ + "Front", + "Back" + ], + "Basic+": [ + "Front", + "Back" + ], + "Basic++": [ + "Front", + "Back" + ], + "Basic+++": [ + "Front", + "Back" + ], + "Basic-73fde": [ + "Front", + "Back" + ], + "Cloze ALL": [ + "文字", + "背面额外" + ], + "Pot Card 2": [ + "Front", + "Back", + "Symbol1", + "Voice1", + "Symbol2", + "Voice2" + ], + "Python - Basic": [ + "Front", + "Back - Basic", + "Back - Code", + "Additional Info", + "Example", + "Version" + ], + "Python - Basic (and reversed card)": [ + "Front", + "Back", + "Additional Info", + "With Options", + "Example", + "Version" + ], + "Python - Cloze": [ + "Text", + "Example", + "Version" + ], + "UVBasic-English": [ + "英语单词", + "英美音标", + "中文释义", + "vocab简明", + "vocab扩展", + "真题原句", + "柯林斯星级", + "柯林斯解释", + "来源", + "英语发音" + ], + "单词": [ + "单词", + "音标", + "助记词", + "线索", + "词性1", + "释义1", + "词性2", + "释义2", + "发音", + "拓展" + ], + "图片遮盖": [ + "遮盖", + "图片", + "标题", + "背面额外", + "注释" + ], + "填空题": [ + "文字", + "背面额外" + ], + "红宝书": [ + "单词", + "音标", + "解释", + "红宝书", + "字源", + "不择手段背单词", + "趣味全助记", + "Audio" + ], + "问答题": [ + "正面", + "背面" + ], + "问答题(同时生成翻转的卡片<可选>)": [ + "正面", + "背面", + "添加翻转的卡片" + ], + "问答题(同时生成翻转的卡片)": [ + "正面", + "背面" + ], + "问答题(输入答案)": [ + "正面", + "背面" + ] + } +} \ No newline at end of file diff --git a/image.png b/image.png new file mode 100644 index 00000000..44121bec Binary files /dev/null and b/image.png differ diff --git a/main.ts b/main.ts index 4b4c1d30..666c494d 100644 --- a/main.ts +++ b/main.ts @@ -42,6 +42,8 @@ export default class MyPlugin extends Plugin { "CurlyCloze - Highlights to Clozes": false, "ID Comments": true, "Add Obsidian Tags": false, + "Use Path as Deck" : false, + "Add Card link": false }, IGNORED_FILE_GLOBS: DEFAULT_IGNORED_FILE_GLOBS, } diff --git a/manifest.json b/manifest.json index e10e8a8e..99c90f74 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { - "id": "obsidian-to-anki-plugin", - "name": "Obsidian_to_Anki", - "version": "3.6.0", + "id": "norcx' obsidian-to-anki-plugin", + "name": "norcx' Obsidian_to_Anki", + "version": "0.2.0", "minAppVersion": "0.9.20", - "description": "This is an Anki integration plugin! Designed for efficient bulk exporting.", - "author": "Pseudonium", - "authorUrl": "https://github.com/Pseudonium/Obsidian_to_Anki", + "description": "More function for obsidian to anki", + "author": "Norcx", + "authorUrl": "https://github.com/norcx/new_obsidian_to_anki", "isDesktopOnly": true } diff --git a/src/constants.ts b/src/constants.ts index b0168b42..51ca8f4b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,6 @@ export const ANKI_ICON: string = `` -export const OBS_INLINE_MATH_REGEXP: RegExp = /(?" @@ -92,8 +95,10 @@ abstract class AbstractFile { tags: string[] formatter: FormatConverter - - constructor(file_contents: string, path:string, url: string, data: FileData, file_cache: CachedMetadata) { + fullpath: string + use_path_as_deck:boolean + add_card_link:boolean + constructor(file_contents: string, path:string, url: string, data: FileData, file_cache: CachedMetadata, fullpath: string,use_path_as_deck:boolean, add_card_link:boolean) { this.data = data this.file = file_contents this.path = path @@ -101,6 +106,9 @@ abstract class AbstractFile { this.original_file = this.file this.file_cache = file_cache this.formatter = new FormatConverter(file_cache, this.data.vault_name) + this.fullpath = fullpath + this.use_path_as_deck = use_path_as_deck + this.add_card_link = add_card_link } setup_frozen_fields_dict() { @@ -129,8 +137,14 @@ abstract class AbstractFile { } setup_target_deck() { + if(this.use_path_as_deck){ + this.target_deck = this.fullpath.replaceAll("/","::") + } + else{ const result = this.file.match(this.data.DECK_REGEXP) this.target_deck = result ? result[1] : this.data.template["deckName"] + } + } setup_global_tags() { @@ -154,7 +168,7 @@ abstract class AbstractFile { let result: string = this.path let currentContext: HeadingCache[] = [] if (!(this.file_cache.hasOwnProperty('headings'))) { - return result + return "
"+result+"
"+"
" } for (let currentHeading of this.file_cache.headings) { if (position < currentHeading.position.start.offset) { @@ -174,11 +188,17 @@ abstract class AbstractFile { } let heading_strs: string[] = [] for (let contextHeading of currentContext) { - heading_strs.push(contextHeading.heading) + let convertedHeading = contextHeading.heading + // 修改:添加Markdown链接转换为HTML链接的代码 + const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; + convertedHeading = contextHeading.heading.replace(markdownLinkRegex, '$1'); + const mathRegex = /\$(.+?)\$/g; + convertedHeading = convertedHeading.replace(mathRegex, '\\($1\\)'); + heading_strs.push(convertedHeading) } let result_arr: string[] = [result] result_arr.push(...heading_strs) - return result_arr.join(" > ") + return "
"+result_arr.join("
")+"
"+"
" } abstract writeIDs(): void @@ -259,8 +279,8 @@ export class AllFile extends AbstractFile { regex_notes_to_add: AnkiConnectNote[] regex_id_indexes: number[] - constructor(file_contents: string, path:string, url: string, data: FileData, file_cache: CachedMetadata) { - super(file_contents, path, url, data, file_cache) + constructor(file_contents: string, path:string, url: string, data: FileData, file_cache: CachedMetadata,fullpath:string,use_path_as_deck:boolean,add_card_link:boolean) { + super(file_contents, path, url, data, file_cache, fullpath,use_path_as_deck,add_card_link) this.custom_regexps = data.custom_regexps } @@ -417,6 +437,7 @@ export class AllFile extends AbstractFile { } } + scanFile() { this.setupScan() this.scanNotes() @@ -434,6 +455,31 @@ export class AllFile extends AbstractFile { fix_newline_ids() { this.file = this.file.replace(double_regexp, "$1") } + getAddNotesWithId(): AnkiConnect.AnkiConnectRequest { + let actions: AnkiConnect.AnkiConnectRequest[] = []; + this.all_notes_to_add.forEach((note, index) => { + // 使用this.note_ids数组中的相应ID + let id = this.note_ids[index]; + if (id !== null) { + let updated = false; // 标志是否成功替换 + // 遍历note.fields,查找"ID-null"的子字符串,替换为"ID-"+String(id) + for (let key in note.fields) { + let originalValue = note.fields[key]; + note.fields[key] = originalValue.replace(/ID-null/g, "ID-" + id); + // 如果字段被更新,则设置 updated 为 true + if (originalValue !== note.fields[key]) { + updated = true; + } + } + // 如果成功替换,则添加更新操作,并继续到下一个笔记 + if (updated) { + actions.push(AnkiConnect.updateNoteFields(id, note.fields)); + } + } + }); + return AnkiConnect.multi(actions); + } + writeIDs() { let normal_inserts: [number, string][] = [] @@ -441,7 +487,7 @@ export class AllFile extends AbstractFile { (id_position: number, index: number) => { const identifier: number | null = this.note_ids[index] if (identifier) { - normal_inserts.push([id_position, id_to_str(identifier, false, this.data.comment)]) + normal_inserts.push([id_position, id_to_str(identifier, false, this.data.comment,this.data.add_card_link)]) } } ) @@ -450,7 +496,7 @@ export class AllFile extends AbstractFile { (id_position: number, index: number) => { const identifier: number | null = this.note_ids[index + this.notes_to_add.length] //Since regular then inline if (identifier) { - inline_inserts.push([id_position, id_to_str(identifier, true, this.data.comment)]) + inline_inserts.push([id_position, id_to_str(identifier, true, this.data.comment,this.data.add_card_link)]) } } ) @@ -459,7 +505,7 @@ export class AllFile extends AbstractFile { (id_position: number, index: number) => { const identifier: number | null = this.note_ids[index + this.notes_to_add.length + this.inline_notes_to_add.length] // Since regular then inline then regex if (identifier) { - regex_inserts.push([id_position, "\n" + id_to_str(identifier, false, this.data.comment)]) + regex_inserts.push([id_position, "\n" + id_to_str(identifier, false, this.data.comment,this.data.add_card_link)]) } } ) diff --git a/src/files-manager.ts b/src/files-manager.ts index fb4d35f3..cf311a11 100644 --- a/src/files-manager.ts +++ b/src/files-manager.ts @@ -5,6 +5,7 @@ import { AllFile } from './file' import * as AnkiConnect from './anki' import { basename } from 'path' import multimatch from "multimatch" +import { AnkiConnectNote } from './interfaces/note-interface' interface addNoteResponse { result: number, error: string | null @@ -136,15 +137,19 @@ export class FileManager { async genAllFiles() { for (let file of this.files) { const content: string = await this.app.vault.read(file) + const fullpath: string = (file.path.slice(0, -file.extension.length - 1)) const cache: CachedMetadata = this.app.metadataCache.getCache(file.path) const file_data = this.dataToFileData(file) this.ownFiles.push( new AllFile( content, file.path, - this.data.add_file_link ? this.getUrl(file) : "", + this.data.add_file_link ? (this.data.add_card_link ? this.getUrl(file).slice(0,-3)+"%23%5E" :this.getUrl(file)) : "", file_data, - cache + cache, + fullpath, + this.data.use_path_as_deck, + this.data.add_card_link ) ) } @@ -279,6 +284,7 @@ export class FileManager { } file.card_ids = temp } + let temp: AnkiConnect.AnkiConnectRequest[] = [] for (let index in this.ownFiles) { let i: number = parseInt(index) let ownFile = this.ownFiles[i] @@ -286,10 +292,13 @@ export class FileManager { ownFile.tags = tag_list ownFile.writeIDs() ownFile.removeEmpties() + temp.push(ownFile.getAddNotesWithId()) if (ownFile.file !== ownFile.original_file) { await this.app.vault.modify(obFile, ownFile.file) } } + console.info("Requesting addition of id links...") + await AnkiConnect.invoke('multi', {actions: temp}) await this.requests_2() } @@ -306,6 +315,8 @@ export class FileManager { let temp: AnkiConnect.AnkiConnectRequest[] = [] console.info("Requesting cards to be moved to target deck...") for (let file of this.ownFiles) { + if (file.regex_id_indexes.length + file.inline_id_indexes.length === 0) + continue temp.push(file.getChangeDecks()) } requests.push(AnkiConnect.multi(temp)) diff --git a/src/format.ts b/src/format.ts index 0e81dfad..6eb7bb89 100644 --- a/src/format.ts +++ b/src/format.ts @@ -58,7 +58,7 @@ export class FormatConverter { } format_note_with_url(note: AnkiConnectNote, url: string, field: string): void { - note.fields[field] += '
Obsidian' + note.fields[field] += '
🔗' } format_note_with_frozen_fields(note: AnkiConnectNote, frozen_fields_dict: Record>): void { @@ -119,8 +119,16 @@ export class FormatConverter { if (!(this.file_cache.hasOwnProperty("links"))) { return note_text } + const regex = /\[([^\]]+)\]\(([^\)]+#\^ID-(\d{13}))\)/g; for (let link of this.file_cache.links) { + //regex.test(input) + if(link.link.includes("#^ID-")){ + const replacedString = link.original.replace(regex, '[$1|nid$3]'); + note_text = note_text.replace(new RegExp(c.escapeRegex(link.original), "g"),replacedString) + } + else{ note_text = note_text.replace(new RegExp(c.escapeRegex(link.original), "g"), '' + link.displayText + "") + } } return note_text } @@ -144,38 +152,43 @@ export class FormatConverter { } format(note_text: string, cloze: boolean, highlights_to_cloze: boolean): string { - note_text = this.obsidian_to_anki_math(note_text) - //Extract the parts that are anki math - let math_matches: string[] - let inline_code_matches: string[] - let display_code_matches: string[] + // First, isolate display and inline code blocks + let math_matches: string[]; + let inline_code_matches: string[]; + let display_code_matches: string[]; const add_highlight_css: boolean = note_text.match(c.OBS_DISPLAY_CODE_REGEXP) ? true : false; - [note_text, math_matches] = this.censor(note_text, ANKI_MATH_REGEXP, MATH_REPLACE); [note_text, display_code_matches] = this.censor(note_text, c.OBS_DISPLAY_CODE_REGEXP, DISPLAY_CODE_REPLACE); [note_text, inline_code_matches] = this.censor(note_text, c.OBS_CODE_REGEXP, INLINE_CODE_REPLACE); + + // Process other parts of the text + note_text = this.obsidian_to_anki_math(note_text); + [note_text, math_matches] = this.censor(note_text, ANKI_MATH_REGEXP, MATH_REPLACE); if (cloze) { if (highlights_to_cloze) { note_text = note_text.replace(HIGHLIGHT_REGEXP, "{$1}") } - note_text = this.curly_to_cloze(note_text) + note_text = this.curly_to_cloze(note_text); } - note_text = this.getAndFormatMedias(note_text) - note_text = this.formatLinks(note_text) - //Special for formatting highlights now, but want to avoid any == in code - note_text = note_text.replace(HIGHLIGHT_REGEXP, String.raw`$1`) - note_text = this.decensor(note_text, DISPLAY_CODE_REPLACE, display_code_matches, false) - note_text = this.decensor(note_text, INLINE_CODE_REPLACE, inline_code_matches, false) - note_text = converter.makeHtml(note_text) - note_text = this.decensor(note_text, MATH_REPLACE, math_matches, true).trim() - // Remove unnecessary paragraph tag + note_text = this.getAndFormatMedias(note_text); + note_text = this.formatLinks(note_text); + note_text = note_text.replace(HIGHLIGHT_REGEXP, String.raw`$1`); + + // Restore code blocks + note_text = this.decensor(note_text, DISPLAY_CODE_REPLACE, display_code_matches, false); + note_text = this.decensor(note_text, INLINE_CODE_REPLACE, inline_code_matches, false); + + // Final conversions + note_text = converter.makeHtml(note_text); + note_text = this.decensor(note_text, MATH_REPLACE, math_matches, true).trim(); if (note_text.startsWith(PARA_OPEN) && note_text.endsWith(PARA_CLOSE)) { - note_text = note_text.slice(PARA_OPEN.length, -1 * PARA_CLOSE.length) + note_text = note_text.slice(PARA_OPEN.length, -1 * PARA_CLOSE.length); } if (add_highlight_css) { - note_text = '' + note_text + note_text = '' + note_text; } - return note_text + return note_text.replace(/\\\$/g,"$"); } + diff --git a/src/interfaces/settings-interface.ts b/src/interfaces/settings-interface.ts index dd022b0f..0d6ae05e 100644 --- a/src/interfaces/settings-interface.ts +++ b/src/interfaces/settings-interface.ts @@ -27,7 +27,10 @@ export interface PluginSettings { "CurlyCloze": boolean, "CurlyCloze - Highlights to Clozes": boolean, "ID Comments": boolean, - "Add Obsidian Tags": boolean + "Add Obsidian Tags": boolean, + "Use Path as Deck" : boolean, + "Add Card link": boolean + }, IGNORED_FILE_GLOBS:string[] } @@ -54,6 +57,8 @@ export interface FileData { comment: boolean add_context: boolean add_obs_tags: boolean + use_path_as_deck: boolean + add_card_link: boolean } export interface ParsedSettings extends FileData { diff --git a/src/note.ts b/src/note.ts index 17330c31..c4278247 100644 --- a/src/note.ts +++ b/src/note.ts @@ -10,7 +10,7 @@ import { FileData } from './interfaces/settings-interface' const TAG_PREFIX:string = "Tags: " export const TAG_SEP:string = " " -export const ID_REGEXP_STR: string = String.raw`\n?(?: