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} + > + Avatar + {#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 = ''