Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
quote_style = single
trim_trailing_whitespace = true

[{*.js, *.json, *.ts}]
quote_style = double
67 changes: 34 additions & 33 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
# Intellij
*.iml
.idea

# npm
node_modules
package-lock.json

# build
main.js
*.js.map
docs/test-vault/.obsidian/plugins/flashcards/main.js
docs/test-vault/.obsidian/plugins/flashcards/manifest.json

# scripts
move.sh

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

# Local History for Visual Studio Code
.history/

# Github
.DS_Store

# Test-vault
docs/test-vault/.obsidian/workspace
# Obsidian
data.json

# IntelliJ
.idea/
*.iml

# npm
node_modules/
package-lock.json

# Build
main.js

# Scripts
move.sh

# Visual Studio Code
.vscode/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/

# macOS
.DS_Store

# Test vault
docs/test-vault/.obsidian/*.json
!docs/test-vault/.obsidian/community-plugins.json
docs/test-vault/.obsidian/plugins/flashcards-obsidian/*
!docs/test-vault/.obsidian/plugins/flashcards-obsidian/.hotreload
!docs/test-vault/.obsidian/plugins/hot-reload/*
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"standard.enable": false
}
8 changes: 7 additions & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Contributing

Contributions via bug reports, bug fixes, are welcome. If you have ideas about features to be implemented, please open an issue so we can discuss the best way to implement it.

## How to build?

You need to pull the repository, install the dependencies with `node` and then build with the command `npm run dev`. It will automatically move the files into the `docs/test-vault` and hot reload the plugin.

$ git clone [email protected]:reuseman/flashcards-obsidian.git
$ cd flashcards-obsidian
~/flashcards-obsidian$ npm install
~/flashcards-obsidian$ npm run dev
~/flashcards-obsidian$ npm run dev

### Hot Reload

You need to download `main.js` and `manifest.json` from https://github.com/pjeby/hot-reload into `docs/test-vault/.obsidian/plugins/hot-reload` if you want to use the hot reload functionality mentioned above.
4 changes: 0 additions & 4 deletions docs/test-vault/.obsidian/app.json

This file was deleted.

3 changes: 0 additions & 3 deletions docs/test-vault/.obsidian/appearance.json

This file was deleted.

15 changes: 0 additions & 15 deletions docs/test-vault/.obsidian/core-plugins.json

This file was deleted.

1 change: 0 additions & 1 deletion docs/test-vault/.obsidian/hotkeys.json

This file was deleted.

13 changes: 0 additions & 13 deletions docs/test-vault/.obsidian/plugins/flashcards/data.json

This file was deleted.

115 changes: 115 additions & 0 deletions docs/test-vault/.obsidian/plugins/hot-reload/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
const {Plugin, Notice} = require("obsidian");
const fs = require("fs");

const watchNeeded = window.process.platform !== "darwin" && window.process.platform !== "win32";

module.exports = class HotReload extends Plugin {

statCache = new Map(); // path -> Stat

onload() { this.app.workspace.onLayoutReady( this._onload.bind(this) ); }

async _onload() {
this.pluginReloaders = {};
this.inProgress = null;
await this.getPluginNames();
this.reindexPlugins = this.debouncedMethod(500, this.getPluginNames);
this.registerEvent( this.app.vault.on("raw", this.onFileChange.bind(this)) );
this.watch(".obsidian/plugins");
await this.checkVersions();
this.addCommand({
id: "scan-for-changes",
name: "Check plugins for changes and reload them",
callback: () => this.checkVersions()
})
}

watch(path) {
if (this.app.vault.adapter.watchers.hasOwnProperty(path)) return;
const realPath = [this.app.vault.adapter.basePath, path].join("/");
const lstat = fs.lstatSync(realPath);
if (lstat && (watchNeeded || lstat.isSymbolicLink()) && fs.statSync(realPath).isDirectory()) {
this.app.vault.adapter.startWatchPath(path, false);
}
}

async checkVersions() {
const base = this.app.plugins.getPluginFolder();
for (const dir of Object.keys(this.pluginNames)) {
for (const file of ["manifest.json", "main.js", "styles.css", ".hotreload"]) {
const path = `${base}/${dir}/${file}`;
const stat = await app.vault.adapter.stat(path);
if (stat) {
if (this.statCache.has(path) && stat.mtime !== this.statCache.get(path).mtime) {
this.onFileChange(path);
}
this.statCache.set(path, stat);
}
}
}
}

async getPluginNames() {
const plugins = {}, enabled = new Set();
for (const {id, dir} of Object.values(app.plugins.manifests)) {
this.watch(dir);
plugins[dir.split("/").pop()] = id;
if (
await this.app.vault.exists(dir+"/.git") ||
await this.app.vault.exists(dir+"/.hotreload")
) enabled.add(id);
}
this.pluginNames = plugins;
this.enabledPlugins = enabled;
}

onFileChange(filename) {
if (!filename.startsWith(this.app.plugins.getPluginFolder()+"/")) return;
const path = filename.split("/");
const base = path.pop(), dir = path.pop();
if (path.length === 1 && dir === "plugins") return this.watch(filename);
if (path.length != 2) return;
const plugin = dir && this.pluginNames[dir];
if (base === "manifest.json" || base === ".hotreload" || base === ".git" || !plugin) return this.reindexPlugins();
if (base !== "main.js" && base !== "styles.css") return;
if (!this.enabledPlugins.has(plugin)) return;
const reloader = this.pluginReloaders[plugin] || (
this.pluginReloaders[plugin] = this.debouncedMethod(750, this.requestReload, plugin)
);
reloader();
}

requestReload(plugin) {
const next = this.inProgress = this.reload(plugin, this.inProgress);
next.finally(() => {if (this.inProgress === next) this.inProgress = null;})
}

async reload(plugin, previous) {
const plugins = this.app.plugins;
try {
// Wait for any other queued/in-progress reloads to finish
await previous;
await plugins.disablePlugin(plugin);
console.debug("disabled", plugin);
// Ensure sourcemaps are loaded (Obsidian 14+)
const oldDebug = localStorage.getItem("debug-plugin");
localStorage.setItem("debug-plugin", "1");
try {
await plugins.enablePlugin(plugin);
} finally {
// Restore previous setting
if (oldDebug === null) localStorage.removeItem("debug-plugin"); else localStorage.setItem("debug-plugin", oldDebug);
}
console.debug("enabled", plugin);
new Notice(`Plugin "${plugin}" has been reloaded`);
} catch(e) {}
}

debouncedMethod(ms, func, ...args) {
var timeout;
return () => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout( () => { timeout = null; func.apply(this, args); }, ms);
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "An Anki integration.",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w --environment BUILD:dev",
"dev": "cp manifest.json docs/test-vault/.obsidian/plugins/flashcards-obsidian/ && rollup --config rollup.config.js -w --environment BUILD:dev",
"build": "rollup --config rollup.config.js --environment BUILD:production",
"test": "jest"
},
Expand Down
8 changes: 6 additions & 2 deletions src/services/anki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,11 @@ export class Anki {
return await this.invoke("cardsInfo", 6, { cards: ids });
}

public async getCards(ids: number[]) {
public async findNotes(deckName: string) {
return await this.invoke("findNotes", 6, { query: `deck:"${deckName}"` });
}

public async notesInfo(ids: number[]) {
return await this.invoke("notesInfo", 6, { notes: ids });
}

Expand Down Expand Up @@ -304,7 +308,7 @@ export class Anki {
},
],
},

}

const obsidianSpaced = {
Expand Down
13 changes: 11 additions & 2 deletions src/services/cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ export class CardsService {
let deckName = "";
if (parseFrontMatterEntry(frontmatter, "cards-deck")) {
deckName = parseFrontMatterEntry(frontmatter, "cards-deck");
} else if (this.settings.folderBasedDeck && activeFile.parent.path !== "/") {
} else if (
this.settings.folderBasedDeck &&
activeFile.parent.path !== "/"
) {
// If the current file is in the path "programming/java/strings.md" then the deck name is "programming::java"
deckName = activeFile.parent.path.split("/").join("::");
} else {
Expand All @@ -81,8 +84,14 @@ export class CardsService {
globalTags = this.parseGlobalTags(this.file);
// TODO with empty check that does not call ankiCards line
const ankiBlocks = this.parser.getAnkiIDsBlocks(this.file);
const ankiIDs = this.getAnkiIDs(ankiBlocks);
const ankiNotes = await this.anki.findNotes(deckName);
// const inObsidianNotAnki = ankiIDs.filter((x) => !ankiNotes.includes(x));
// const inAnkiNotObsidian = ankiNotes.filter((x) => !ankiIDs.includes(x));
const inBoth = ankiIDs.filter((x) => ankiNotes.includes(x));

const ankiCards = ankiBlocks
? await this.anki.getCards(this.getAnkiIDs(ankiBlocks))
? await this.anki.notesInfo(inBoth)
: undefined;

const cards: Card[] = this.parser.generateFlashcards(
Expand Down