diff --git a/esbuild.config.mjs b/esbuild.config.mjs
index b13282b..b65806e 100644
--- a/esbuild.config.mjs
+++ b/esbuild.config.mjs
@@ -1,6 +1,8 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
+import esbuildSvelte from "esbuild-svelte";
+import sveltePreprocess from "svelte-preprocess";
const banner =
`/*
@@ -17,6 +19,12 @@ const context = await esbuild.context({
},
entryPoints: ["main.ts"],
bundle: true,
+ plugins: [
+ esbuildSvelte({
+ compilerOptions: { css: true },
+ preprocess: sveltePreprocess(),
+ }),
+ ],
external: [
"obsidian",
"electron",
@@ -45,4 +53,4 @@ if (prod) {
process.exit(0);
} else {
await context.watch();
-}
\ No newline at end of file
+}
diff --git a/manifest.json b/manifest.json
index a46c983..3e4eba1 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "gamified-pkm",
"name": "Gamificate your PKM",
- "version": "0.0.89",
+ "version": "0.0.90",
"minAppVersion": "0.15.0",
"description": "Enhance your Personal Knowledge Management with gamification elements. Boost motivation and achieve growth as you engage with your PKM.",
"author": "Andreas Trebing",
diff --git a/package-lock.json b/package-lock.json
index 2c0354d..201abfe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "obsidian-gamified-pkm",
- "version": "0.0.89",
+ "version": "0.0.90",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "obsidian-gamified-pkm",
- "version": "0.0.89",
+ "version": "0.0.90",
"license": "MIT",
"dependencies": {
"crypto-js": "^4.1.1",
@@ -25,6 +25,7 @@
"@rollup/plugin-replace": "^5.0.5",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.2",
+ "@tsconfig/svelte": "^3.0.0",
"@types/crypto-js": "^4.1.1",
"@types/jest": "^29.5.4",
"@types/node": "^16.11.6",
@@ -34,6 +35,7 @@
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
+ "esbuild-svelte": "^0.7.3",
"eslint": "8.48.0",
"jest": "^29.6.3",
"jest-mock-extended": "^3.0.5",
@@ -47,6 +49,8 @@
"rollup-plugin-postprocess": "github:brettz9/rollup-plugin-postprocess#update",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.34.1",
+ "svelte": "^3.57.0",
+ "svelte-preprocess": "^5.0.3",
"ts-jest": "^29.1.1",
"tslib": "2.4.0",
"typescript": "4.7.4"
@@ -1976,6 +1980,12 @@
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
},
+ "node_modules/@tsconfig/svelte": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-3.0.0.tgz",
+ "integrity": "sha512-pYrtLtOwku/7r1i9AMONsJMVYAtk3hzOfiGNekhtq5tYBGA7unMve8RvUclKLMT3PrihvJqUmzsRGh0RP84hKg==",
+ "dev": true
+ },
"node_modules/@types/babel__core": {
"version": "7.20.1",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz",
@@ -2123,6 +2133,12 @@
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
},
+ "node_modules/@types/pug": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
+ "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
+ "dev": true
+ },
"node_modules/@types/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
@@ -2696,6 +2712,15 @@
"node-int64": "^0.4.0"
}
},
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -2967,6 +2992,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/detect-indent": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
+ "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -3060,6 +3094,12 @@
"is-arrayish": "^0.2.1"
}
},
+ "node_modules/es6-promise": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
+ "dev": true
+ },
"node_modules/esbuild": {
"version": "0.17.3",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz",
@@ -3097,6 +3137,19 @@
"@esbuild/win32-x64": "0.17.3"
}
},
+ "node_modules/esbuild-svelte": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.7.4.tgz",
+ "integrity": "sha512-d4Vafj5nFTmZPXznW6YL3ZHXiWwNiPLcE8yfq/5oE8nbyrZlIB92ZCVh3JMbMje+vCb4jnKdH+WoV2sLZRdOJA==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.9.6",
+ "svelte": ">=3.43.0 <5"
+ }
+ },
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -4896,6 +4949,15 @@
"node": ">=6"
}
},
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4908,6 +4970,27 @@
"node": "*"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
@@ -5732,6 +5815,30 @@
}
]
},
+ "node_modules/sander": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
+ "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
+ "dev": true,
+ "dependencies": {
+ "es6-promise": "^3.1.2",
+ "graceful-fs": "^4.1.3",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.2"
+ }
+ },
+ "node_modules/sander/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -5812,6 +5919,21 @@
"integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==",
"dev": true
},
+ "node_modules/sorcery": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
+ "integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.14",
+ "buffer-crc32": "^0.2.5",
+ "minimist": "^1.2.0",
+ "sander": "^0.5.0"
+ },
+ "bin": {
+ "sorcery": "bin/sorcery"
+ }
+ },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -5928,6 +6050,18 @@
"node": ">=6"
}
},
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+ "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+ "dev": true,
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -5971,6 +6105,90 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/svelte": {
+ "version": "3.59.2",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz",
+ "integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/svelte-preprocess": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz",
+ "integrity": "sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "@types/pug": "^2.0.6",
+ "detect-indent": "^6.1.0",
+ "magic-string": "^0.30.5",
+ "sorcery": "^0.11.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 16.0.0",
+ "pnpm": "^8.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.10.2",
+ "coffeescript": "^2.5.1",
+ "less": "^3.11.3 || ^4.0.0",
+ "postcss": "^7 || ^8",
+ "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
+ "pug": "^3.0.0",
+ "sass": "^1.26.8",
+ "stylus": "^0.55.0",
+ "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
+ "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
+ "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "coffeescript": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "postcss-load-config": {
+ "optional": true
+ },
+ "pug": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/svelte-preprocess/node_modules/magic-string": {
+ "version": "0.30.8",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
+ "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/terser": {
"version": "5.27.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz",
diff --git a/package.json b/package.json
index 670b59e..c9105f7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "obsidian-gamified-pkm",
- "version": "0.0.89",
+ "version": "0.0.90",
"description": "Enhance your Personal Knowledge Management with gamification elements. Boost motivation and achieve growth as you engage with your PKM.",
"main": "main.js",
"scripts": {
@@ -28,6 +28,7 @@
"ts-node": "^10.9.1"
},
"devDependencies": {
+ "@tsconfig/svelte": "^3.0.0",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
@@ -43,6 +44,7 @@
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
+ "esbuild-svelte": "^0.7.3",
"eslint": "8.48.0",
"jest": "^29.6.3",
"jest-mock-extended": "^3.0.5",
@@ -58,6 +60,8 @@
"rollup-plugin-typescript2": "^0.34.1",
"ts-jest": "^29.1.1",
"tslib": "2.4.0",
- "typescript": "4.7.4"
+ "typescript": "4.7.4",
+ "svelte": "^3.57.0",
+ "svelte-preprocess": "^5.0.3"
}
}
\ No newline at end of file
diff --git a/src/Messages.ts b/src/Messages.ts
index c8a467e..ae3eb58 100644
--- a/src/Messages.ts
+++ b/src/Messages.ts
@@ -12,6 +12,49 @@ export const RELEASE_NOTES: { [k: string]: string } = {
I develop this plugin as a hobby, spending my free time doing this. If you find it valuable, then please say THANK YOU or...
+`,
+"0.0.90": `
+## New
+- added an avatar picture field (contribution goes to the obsidian-avatar plugin from froehlichA). When creating the avatar page it will be there. Bellow the code how to exchange in existing profile pages.
+
+
+
+Replace the first table and bar graph with this and you get the new layout. (don't worry, the data will update with the next received poits):
+\`\`\`
+\`\`\`gamification-avatar
+image:
+description: |-2
+ | | |
+ | --------- | ------- |
+ | **Level** | **0** |
+ | Points | 0 |
+ ^levelAndPoints
+ \`\`\`chart
+ type: bar
+ labels: [Expririence]
+ series:
+ - title: points reached
+ data: [0]
+ - title: points to earn to level up
+ data: [1000]
+ xMin: 0
+ xMax: 1000
+ tension: 0.2
+ width: 70%
+ labelColors: false
+ fill: false
+ beginAtZero: false
+ bestFit: false
+ bestFitTitle: undefined
+ bestFitNumber: 0
+ stacked: true
+ indexAxis: y
+ xTitle: "progress"
+ legend: false
+\`\`\`
+
`,
"0.0.89": `
## New
diff --git a/src/avatar/AvatarView.svelte b/src/avatar/AvatarView.svelte
new file mode 100644
index 0000000..e60c256
--- /dev/null
+++ b/src/avatar/AvatarView.svelte
@@ -0,0 +1,177 @@
+
+
+
+
hoverOnImage = true}
+ on:mouseleave={() => hoverOnImage = false}
+ on:keydown={handleKeyDown}
+ >
+
+ {#if inSourceMode && hoverOnImage}
+
+
+
+ {/if}
+
+
+
+
+ {#if editMode}
+
+
+
+ {/if}
+
+
+
+
diff --git a/src/avatar/Fab.svelte b/src/avatar/Fab.svelte
new file mode 100644
index 0000000..302f999
--- /dev/null
+++ b/src/avatar/Fab.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/src/avatar/ObsidianIcon.svelte b/src/avatar/ObsidianIcon.svelte
new file mode 100644
index 0000000..b2ecb01
--- /dev/null
+++ b/src/avatar/ObsidianIcon.svelte
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/avatar/SelectImageModal.ts b/src/avatar/SelectImageModal.ts
new file mode 100644
index 0000000..a470568
--- /dev/null
+++ b/src/avatar/SelectImageModal.ts
@@ -0,0 +1,90 @@
+// This portion of code is adapted from the following source under the MIT License:
+// https://github.com/froehlichA/obsidian-avatar
+// Copyright (c) [2024], [froehlichA]
+// License: MIT
+import {App, SuggestModal, normalizePath, prepareFuzzySearch} from "obsidian";
+import type {SearchMatchPart} from "obsidian";
+
+export interface Image {
+ title: DocumentFragment;
+ desc?: string;
+ path: string;
+}
+
+export class SelectImageModal extends SuggestModal {
+ constructor(
+ app: App,
+ private readonly onSelect: (path: string) => void
+ ) {
+ super(app);
+ this.setPlaceholder("Select image / Paste URL...");
+ this.setInstructions([
+ { command: "↑↓", purpose: "to navigate" },
+ { command: "↵", purpose: "to select" }
+ ]);
+ }
+
+ override getSuggestions(query: string): Image[] {
+ const search = prepareFuzzySearch(query);
+ const files = this.app.vault.getFiles()
+ .filter(f => ["jpg", "jpeg", "png", "gif", "svg", "webp"].includes(f.extension))
+ .map(f => ({ title: f.name, path: f.path }));
+ const searchResults = files
+ .map(f => {
+ const result = search(f.title);
+ return {
+ title: result?.matches ? fuzzyStrToFragment(f.title, result?.matches) : strToFragment(f.title),
+ path: f.path,
+ score: result?.score ?? 1
+ }
+ })
+ .filter(f => f.score < 1)
+ .sort((a, b) => Math.abs(a.score) - Math.abs(b.score));
+
+ if (query.startsWith("http://") || query.startsWith("https://")) {
+ const shortenedQuery = query.substring(0, 30) + (query.length >= 30 ? "..." : "");
+ return [
+ ...searchResults,
+ { title: strToFragment(shortenedQuery), desc: "Use URL", path: query }
+ ];
+ } else {
+ return searchResults;
+ }
+ }
+
+ override renderSuggestion(item: Image, el: HTMLElement) {
+ el.createEl("span", { text: item.title });
+ el.createEl("small", { text: item.desc, cls: "avatar-plugin--float-right" });
+ }
+
+ override onChooseSuggestion(item: Image, evt: MouseEvent | KeyboardEvent): any {
+ this.onSelect(normalizePath(item.path));
+ }
+}
+
+function strToFragment(str: string): DocumentFragment {
+ const fragment = new DocumentFragment();
+ fragment.createEl("span", { text: str });
+ return fragment;
+}
+
+function fuzzyStrToFragment(str: string, matches: SearchMatchPart[]) : DocumentFragment {
+ const fragment = new DocumentFragment();
+
+ const highlightedIndices: number[] = [];
+ for(const match of matches) {
+ for(let i = match[0]; i < match[1]; i++) {
+ highlightedIndices.push(i);
+ }
+ }
+
+ for(let i = 0; i < str.length; i++) {
+ const char = str[i];
+ if(highlightedIndices.includes(i)) {
+ fragment.createEl("span", { text: char, cls: "suggestion-highlight" });
+ } else {
+ fragment.createEl("span", { text: char });
+ }
+ }
+ return fragment;
+}
diff --git a/src/avatar/renderCodeBlockProcessor.ts b/src/avatar/renderCodeBlockProcessor.ts
new file mode 100644
index 0000000..127577e
--- /dev/null
+++ b/src/avatar/renderCodeBlockProcessor.ts
@@ -0,0 +1,45 @@
+// This portion of code is adapted from the following source under the MIT License:
+// https://github.com/froehlichA/obsidian-avatar
+// Copyright (c) [2024], [froehlichA]
+// License: MIT
+import type {App, MarkdownPostProcessorContext, Plugin} from "obsidian";
+import type {SvelteComponent} from "svelte";
+import type {StateProvider} from "./stateProviders";
+import {MarkdownRenderChild} from "obsidian";
+
+export interface CodeBlockProcessorProps extends Record {
+ app: App;
+ plugin: Plugin;
+}
+
+/**
+ * Renders a svelte component as a code block processor.
+ * @param component the svelte component to render.
+ * @param props properties forwarded to the component.
+ * @param stateProvider an optional provider that handles state & state updates of the code block processor.
+ */
+export function renderCodeBlockProcessor(
+ component: typeof SvelteComponent,
+ props: CodeBlockProcessorProps,
+ stateProvider?: StateProvider
+) {
+ return (source: string, containerEl: HTMLElement, ctx: MarkdownPostProcessorContext) => {
+ const node = containerEl.createEl("div");
+ const svelteComponent = new component({
+ target: containerEl,
+ props: {
+ ...props,
+ ...stateProvider?.(props, source, node, ctx),
+ ctx
+ }
+ });
+
+ class UnloadSvelteComponent extends MarkdownRenderChild {
+ onunload() {
+ svelteComponent.$destroy();
+ }
+ }
+
+ ctx.addChild(new UnloadSvelteComponent(node));
+ }
+}
diff --git a/src/avatar/stateProviders.ts b/src/avatar/stateProviders.ts
new file mode 100644
index 0000000..8fa4673
--- /dev/null
+++ b/src/avatar/stateProviders.ts
@@ -0,0 +1,44 @@
+// This portion of code is adapted from the following source under the MIT License:
+// https://github.com/froehlichA/obsidian-avatar
+// Copyright (c) [2024], [froehlichA]
+// License: MIT
+import type {MarkdownPostProcessorContext} from "obsidian";
+import type {CodeBlockProcessorProps} from "./renderCodeBlockProcessor";
+import {parseYaml, stringifyYaml} from "obsidian";
+
+export type State = Partial;
+export type SetState = (setter: (state: Partial) => void) => void;
+
+export type StateProvider = (props: CodeBlockProcessorProps, source: string, node: HTMLElement, ctx: MarkdownPostProcessorContext) => {
+ state: State,
+ setState: SetState
+};
+
+export function withCodeblockState(): StateProvider {
+ return (props, source, node, ctx) => {
+ let state: State = {};
+ try {
+ state = parseYaml(source) ?? {};
+ } catch (_) {}
+
+ const setState: SetState = (stateSetter) => {
+ let newState = { ...state };
+ stateSetter(newState);
+ const newStateStr: string = stringifyYaml(newState);
+
+ const info = ctx.getSectionInfo(node);
+ if(info) {
+ app.workspace.activeEditor?.editor?.replaceRange(
+ newStateStr + "```",
+ { line: info.lineStart + 1, ch: 0 },
+ { line: info.lineEnd, ch: 3 }
+ );
+ }
+ };
+
+ return {
+ state,
+ setState
+ }
+ };
+}
diff --git a/src/constants.ts b/src/constants.ts
index 86292ea..16333b0 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,5 +1,5 @@
import { Badge } from './badges'
-export const PLUGIN_VERSION = '0.0.89';
+export const PLUGIN_VERSION = '0.0.90';
export const pointsNoteMajurity = 100;
export const pointsMajurity = 10;
export const pointsForDailyChallenge = 500;
@@ -13,33 +13,37 @@ export const secretKey = "2ZU^12y#QmNB5$yEin5^";
export const debugLogs = false;
export const avatarInitContent = `# Avatar
-| | |
-| --------- | ------- |
-| **Level** | **1** |
-| Points | 0 |
-^levelAndPoints
-\`\`\`chart
-type: bar
-labels: [Expririence]
-series:
- - title: points reached
- data: [0]
- - title: points to earn to level up
- data: [1000]
-xMin: 0
-xMax: 1000
-tension: 0.2
-width: 40%
-labelColors: false
-fill: false
-beginAtZero: false
-bestFit: false
-bestFitTitle: undefined
-bestFitNumber: 0
-stacked: true
-indexAxis: y
-xTitle: "progress"
-legend: false
+
+\`\`\`gamification-avatar
+image:
+description: |-2
+ | | |
+ | --------- | ------- |
+ | **Level** | **1** |
+ | Points | 0 |
+ ^levelAndPoints
+ \`\`\`chart
+ type: bar
+ labels: [Expririence]
+ series:
+ - title: points reached
+ data: [0]
+ - title: points to earn to level up
+ data: [1000]
+ xMin: 0
+ xMax: 1000
+ tension: 0.2
+ width: 40%
+ labelColors: false
+ fill: false
+ beginAtZero: false
+ bestFit: false
+ bestFitTitle: undefined
+ bestFitNumber: 0
+ stacked: true
+ indexAxis: y
+ xTitle: "progress"
+ legend: false
\`\`\`
| | |
diff --git a/src/main.ts b/src/main.ts
index b174053..f5d75c0 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -55,7 +55,10 @@ import {
} from './Utils'
import {ReleaseNotes} from "./ReleaseNotes";
import {GamificationMediator} from './GamificationMediator';
-
+import {renderCodeBlockProcessor} from "./avatar/renderCodeBlockProcessor";
+// @ts-ignore
+import AvatarView from "./avatar/AvatarView.svelte";
+import {withCodeblockState} from "./avatar/stateProviders";
let pointsToReceived = 0;
@@ -154,6 +157,10 @@ export default class gamification extends Plugin implements GamificationMediator
this.app.vault.on('rename', this.onFileRenamed.bind(this))
);
+ // This portion of code is adapted from the following source under the MIT License:
+ // https://github.com/zsviczian/obsidian-excalidraw-plugin
+ // Copyright (c) [2024], [zsviczian]
+ // License: MIT
let obsidianJustInstalled = false;
if (this.getSettingBoolean('showReleaseNotes')) {
@@ -171,7 +178,18 @@ export default class gamification extends Plugin implements GamificationMediator
).open();
}
}
+ // import ends here
+ // This portion of code is adapted from the following source under the MIT License:
+ // https://github.com/froehlichA/obsidian-avatar
+ // Copyright (c) [2024], [froehlichA]
+ // License: MIT
+ this.registerMarkdownCodeBlockProcessor("gamification-avatar", renderCodeBlockProcessor(
+ AvatarView,
+ { app: this.app, plugin: this },
+ withCodeblockState()
+ ));
+ // import ends here
this.registerCommands();
@@ -222,15 +240,15 @@ export default class gamification extends Plugin implements GamificationMediator
//const obsidianJustInstalled = this.settings.previousRelease === "0.0.0"
- // new ReleaseNotes(
- // this.app,
- // this,
- // //obsidianJustInstalled ? null :
- // PLUGIN_VERSION
- // ).open();
+ new ReleaseNotes(
+ this.app,
+ this,
+ //obsidianJustInstalled ? null :
+ PLUGIN_VERSION
+ ).open();
//await this.decreaseStreakbooster(50);
- await this.increaseStreakbooster(0.8);
+ //await this.increaseStreakbooster(0.8);
//this.setBadgeSave(getBadgeDetails('Brainiac Trailblazer'),'23-09-07', 'level 20');
//this.setBadgeSave(getBadgeDetails('Savvy Scholar'), '23-08-15', 'level 15');
@@ -1128,7 +1146,7 @@ export default class gamification extends Plugin implements GamificationMediator
}
const progressBarEnd = nextLevelAt - newPoints;
- const newPointsString = '| **Level** | **' + level.level + '** |\n| Points | ' + newPoints + ' |\n^levelAndPoints\n```chart\ntype: bar\nlabels: [Expririence]\nseries:\n - title: points reached\n data: [' + newPoints + ']\n - title: points to earn to level up\n data: [' + progressBarEnd + ']\nxMin: ' + level.points + '\nxMax: ' + level.pointsNext + '\ntension: 0.2\nwidth: 40%\nlabelColors: false\nfill: false\nbeginAtZero: false\nbestFit: false\nbestFitTitle: undefined\nbestFitNumber: 0\nstacked: true\nindexAxis: y\nxTitle: "progress"\nlegend: false\n```'
+ const newPointsString = ' | **Level** | **' + level.level + '** |\n | Points | ' + newPoints + ' |\n ^levelAndPoints\n ```chart\n type: bar\n labels: [Expririence]\n series:\n - title: points reached\n data: [' + newPoints + ']\n - title: points to earn to level up\n data: [' + progressBarEnd + ']\n xMin: ' + level.points + '\n xMax: ' + level.pointsNext + '\n tension: 0.2\n width: 70%\n labelColors: false\n fill: false\n beginAtZero: false\n bestFit: false\n bestFitTitle: undefined\n bestFitNumber: 0\n stacked: true\n indexAxis: y\n xTitle: "progress"\n legend: false\n```'
const dailyChallenge = '| **daily Notes** | *' + pointsForDailyChallenge * (this.getSettingNumber('badgeBoosterFactor') + this.getSettingNumber('streakbooster')) + 'EP* | **' + this.getSettingNumber('dailyNoteCreationTask') + '/2** |';
const daysLeftInWeeklyChain : number = 7 - this.getSettingNumber('weeklyNoteCreationTask');
let weeklyChallenge = ''