├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── release.yaml
│ └── test.yaml
├── .gitignore
├── .husky
└── pre-commit
├── .npmrc
├── LICENSE
├── README.md
├── esbuild.config.mjs
├── manifest.json
├── package-lock.json
├── package.json
├── src
├── InternalCounts.ts
├── __tests__
│ └── markdownHelpers.test.ts
├── achievements-view
│ ├── AchievementsView.svelte
│ └── AchievementsView.ts
├── array-utils
│ └── sort.ts
├── commands.ts
├── main.ts
├── markdownHelpers.ts
├── obsidian.d.ts
├── reset-progress-modal
│ ├── ResetProgressModal.svelte
│ └── ResetProgressModal.ts
├── seededAchievements.ts
├── settings-tab
│ ├── SettingsTab.svelte
│ └── SettingsTab.ts
└── settings.ts
├── styles.css
├── tsconfig.json
├── version-bump.mjs
├── versions.json
└── vitest.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | insert_final_newline = true
8 | indent_style = tab
9 | indent_size = 4
10 | tab_width = 4
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | npm node_modules
2 | build
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: "@typescript-eslint/parser",
4 | env: { node: true },
5 | plugins: ["@typescript-eslint", "svelte3"],
6 | overrides: [
7 | {
8 | files: ["*.svelte"],
9 | processor: "svelte3/svelte3",
10 | },
11 | ],
12 | extends: [
13 | "eslint:recommended",
14 | "plugin:@typescript-eslint/eslint-recommended",
15 | "plugin:@typescript-eslint/recommended",
16 | ],
17 | parserOptions: {
18 | sourceType: "module",
19 | },
20 | rules: {
21 | "no-unused-vars": "off",
22 | "@typescript-eslint/no-unused-vars": ["error", { args: "none" }],
23 | "@typescript-eslint/ban-ts-comment": "off",
24 | "no-prototype-builtins": "off",
25 | "@typescript-eslint/no-empty-function": "off",
26 | },
27 | settings: {
28 | "svelte3/typescript": () => require("typescript"), // pass the TypeScript package to the Svelte plugin
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: zachatoo # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | ko_fi: zachatoo # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release Obsidian Plugin
2 | on:
3 | push:
4 | tags:
5 | - "*"
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | with:
12 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
13 | - name: Use Node.js
14 | uses: actions/setup-node@v1
15 | with:
16 | node-version: "14.x"
17 | # Get the version number and put it in a variable
18 | - name: Get Version
19 | id: version
20 | run: |
21 | echo "::set-output name=tag::$(git describe --abbrev=0)"
22 | # Build the plugin
23 | - name: Build
24 | id: build
25 | run: |
26 | npm install
27 | npm run build --if-present
28 | # Package the required files into a zip
29 | - name: Package
30 | run: |
31 | mkdir ${{ github.event.repository.name }}
32 | cp main.js manifest.json styles.css README.md ${{ github.event.repository.name }}
33 | zip -r ${{ github.event.repository.name }}.zip ${{ github.event.repository.name }}
34 | # Create the release on github
35 | - name: Create Release
36 | id: create_release
37 | uses: actions/create-release@v1
38 | env:
39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40 | VERSION: ${{ github.ref }}
41 | with:
42 | tag_name: ${{ github.ref }}
43 | release_name: ${{ github.ref }}
44 | draft: false
45 | prerelease: false
46 | # Upload the packaged release file
47 | - name: Upload zip file
48 | id: upload-zip
49 | uses: actions/upload-release-asset@v1
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | with:
53 | upload_url: ${{ steps.create_release.outputs.upload_url }}
54 | asset_path: ./${{ github.event.repository.name }}.zip
55 | asset_name: ${{ github.event.repository.name }}-${{ steps.version.outputs.tag }}.zip
56 | asset_content_type: application/zip
57 | # Upload the main.js
58 | - name: Upload main.js
59 | id: upload-main
60 | uses: actions/upload-release-asset@v1
61 | env:
62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63 | with:
64 | upload_url: ${{ steps.create_release.outputs.upload_url }}
65 | asset_path: ./main.js
66 | asset_name: main.js
67 | asset_content_type: text/javascript
68 | # Upload the manifest.json
69 | - name: Upload manifest.json
70 | id: upload-manifest
71 | uses: actions/upload-release-asset@v1
72 | env:
73 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74 | with:
75 | upload_url: ${{ steps.create_release.outputs.upload_url }}
76 | asset_path: ./manifest.json
77 | asset_name: manifest.json
78 | asset_content_type: application/json
79 | # Upload the style.css
80 | - name: Upload styles.css
81 | id: upload-css
82 | uses: actions/upload-release-asset@v1
83 | env:
84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
85 | with:
86 | upload_url: ${{ steps.create_release.outputs.upload_url }}
87 | asset_path: ./styles.css
88 | asset_name: styles.css
89 | asset_content_type: text/css=
90 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | lint:
13 | name: Lint
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v3
18 |
19 | - name: Setup node
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 16
23 |
24 | - name: Install dependencies
25 | run: npm i
26 |
27 | - name: Lint
28 | run: npm run lint
29 |
30 | typecheck:
31 | name: Type Check
32 | runs-on: ubuntu-latest
33 | steps:
34 | - name: Checkout
35 | uses: actions/checkout@v3
36 |
37 | - name: Setup node
38 | uses: actions/setup-node@v3
39 | with:
40 | node-version: 16
41 |
42 | - name: Install dependencies
43 | run: npm i
44 |
45 | - name: Type check
46 | run: npm run typecheck
47 |
48 | test:
49 | name: Test
50 | runs-on: ubuntu-latest
51 | steps:
52 | - name: Checkout
53 | uses: actions/checkout@v3
54 |
55 | - name: Setup node
56 | uses: actions/setup-node@v3
57 | with:
58 | node-version: 16
59 |
60 | - name: Install dependencies
61 | run: npm i
62 |
63 | - name: Test
64 | run: npm run test:ci
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # vscode
2 | .vscode
3 |
4 | # Intellij
5 | *.iml
6 | .idea
7 |
8 | # npm
9 | node_modules
10 |
11 | # Don't include the compiled main.js file in the repo.
12 | # They should be uploaded to GitHub releases instead.
13 | main.js
14 |
15 | # Exclude sourcemaps
16 | *.map
17 |
18 | # obsidian
19 | data.json
20 |
21 | # Exclude macOS Finder (System Explorer) View States
22 | .DS_Store
23 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run typecheck
5 | npm run lint
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | tag-version-prefix=""
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Zachary Young
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Obsidian Achievements
4 |
5 | Gamify your note taking by working towards gaining achievements!
6 |
7 | ## Achievements
8 |
9 | ### Your first note
10 |
11 | Create a note.
12 |
13 | ### Note taker
14 |
15 | Create ten notes.
16 |
17 | ### Wordsmith
18 |
19 | Create one hundred notes.
20 |
21 | ### Storyteller
22 |
23 | Create one thousand notes.
24 |
25 | ### Taking out the trash
26 |
27 | Delete ten notes.
28 |
29 | ### Linking your thinking
30 |
31 | Create an internal link. You can type [[ to begin creating an internal link.
32 |
33 | ### Making connections
34 |
35 | Create ten internal links.
36 |
37 | ### Conspiracy theorist
38 |
39 | Create one hundred internal links.
40 |
41 | ### Air traffic controller
42 |
43 | Create one thousand internal links.
44 |
45 | ### Commander
46 |
47 | Open the command palette. You can find the hotkey to open the command palette in Settings - Hotkeys.
48 |
49 | ### Quickly now
50 |
51 | Open the quick switcher. You can find the hotkey to open the quick switcher in Settings - Hotkeys.
52 |
53 | ### Callouts
54 |
55 | Create a callout. You can find the hotkey to create a callout in Settings - Hotkeys.
56 |
57 | ### Headings
58 |
59 | Create a heading. You can create a heading by adding a new line to a note and typing # Heading.
60 |
61 | ### Nested headings
62 |
63 | Create at least three levels of headings in a single note.
64 |
65 | ### Your first tag
66 |
67 | Create a tag. You can create a tag by typing #tag.
68 |
69 | ### Tagging apprentice
70 |
71 | Create five unique tags.
72 |
73 | ### Tagging expert
74 |
75 | Create ten unique tags.
76 |
77 | ## Notice
78 |
79 | There's some known issues on mobile with achievements being achieved much quicker than expected, and some achievements not being achievable.
80 |
81 | ## Attributions
82 |
83 | - Thank you to pjeby for [monkey-around](https://github.com/pjeby/monkey-around) that I used to hook into events in Obsidian.
84 | - Thank you to marcusolsson for [obsidian-svelte](https://github.com/marcusolsson/obsidian-svelte) that I used for creating many of the UI elements.
85 |
--------------------------------------------------------------------------------
/esbuild.config.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from "esbuild";
2 | import process from "process";
3 | import builtins from "builtin-modules";
4 | import esbuildSvelte from "esbuild-svelte";
5 | import sveltePreprocess from "svelte-preprocess";
6 |
7 | const banner = `/*
8 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
9 | if you want to view the source, please visit the github repository of this plugin
10 | */
11 | `;
12 |
13 | const prod = process.argv[2] === "production";
14 |
15 | esbuild
16 | .build({
17 | plugins: [
18 | esbuildSvelte({
19 | compilerOptions: { css: true },
20 | preprocess: sveltePreprocess(),
21 | }),
22 | ],
23 | banner: {
24 | js: banner,
25 | },
26 | entryPoints: ["src/main.ts"],
27 | bundle: true,
28 | external: [
29 | "obsidian",
30 | "electron",
31 | "@codemirror/autocomplete",
32 | "@codemirror/collab",
33 | "@codemirror/commands",
34 | "@codemirror/language",
35 | "@codemirror/lint",
36 | "@codemirror/search",
37 | "@codemirror/state",
38 | "@codemirror/view",
39 | "@lezer/common",
40 | "@lezer/highlight",
41 | "@lezer/lr",
42 | ...builtins,
43 | ],
44 | format: "cjs",
45 | watch: !prod,
46 | target: "es2018",
47 | logLevel: "info",
48 | sourcemap: prod ? false : "inline",
49 | treeShaking: true,
50 | outfile: "main.js",
51 | })
52 | .catch(() => process.exit(1));
53 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-achievements",
3 | "name": "Achievements",
4 | "version": "0.0.10",
5 | "minAppVersion": "0.15.0",
6 | "description": "Add achievements to Obsidian.",
7 | "author": "Zachatoo",
8 | "authorUrl": "https://zachyoung.dev",
9 | "fundingUrl": "https://github.com/sponsors/Zachatoo",
10 | "isDesktopOnly": false
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obsidian-achievements",
3 | "version": "0.0.10",
4 | "description": "Add achievements to Obsidian.",
5 | "main": "src/main.js",
6 | "scripts": {
7 | "dev": "node esbuild.config.mjs",
8 | "test": "vitest",
9 | "test:ci": "vitest run",
10 | "typecheck": "tsc -noEmit -skipLibCheck",
11 | "lint": "npx eslint . --ext .ts,.svelte",
12 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
13 | "version": "node version-bump.mjs && git add manifest.json versions.json",
14 | "prepare": "husky install"
15 | },
16 | "keywords": [
17 | "obsidian-plugin"
18 | ],
19 | "author": "Zachatoo",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "@popperjs/core": "^2.11.6",
23 | "@tsconfig/svelte": "^3.0.0",
24 | "@types/node": "^16.11.6",
25 | "@typescript-eslint/eslint-plugin": "^5.40.1",
26 | "@typescript-eslint/parser": "^5.40.1",
27 | "builtin-modules": "3.3.0",
28 | "esbuild": "0.14.47",
29 | "esbuild-svelte": "^0.7.1",
30 | "eslint-plugin-svelte3": "^4.0.0",
31 | "husky": "^7.0.0",
32 | "obsidian": "latest",
33 | "obsidian-svelte": "^0.0.28",
34 | "svelte": "^3.52.0",
35 | "svelte-portal": "^2.2.0",
36 | "svelte-preprocess": "^4.10.7",
37 | "tslib": "2.4.0",
38 | "typescript": "4.7.4",
39 | "vitest": "^0.24.3"
40 | },
41 | "dependencies": {
42 | "monkey-around": "^2.3.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/InternalCounts.ts:
--------------------------------------------------------------------------------
1 | export interface InternalCounts {
2 | noteCount: number;
3 | internalLinkCount: number;
4 | tagCount: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/__tests__/markdownHelpers.test.ts:
--------------------------------------------------------------------------------
1 | import type { CachedMetadata } from "obsidian";
2 | import { fileHasCallout, getFileHeadingLevelsCount } from "src/markdownHelpers";
3 | import { describe, expect, it } from "vitest";
4 |
5 | describe("markdownHelpers", () => {
6 | describe("fileHasCallout", () => {
7 | it("returns true if there is a callout", () => {
8 | const mockCache: CachedMetadata = {
9 | sections: [
10 | {
11 | type: "callout",
12 | position: {
13 | end: { line: 2, col: 10, offset: 27 },
14 | start: { line: 1, col: 0, offset: 1 },
15 | },
16 | },
17 | ],
18 | };
19 |
20 | const result = fileHasCallout(mockCache);
21 |
22 | expect(result).toBe(true);
23 | });
24 |
25 | it("returns false for empty cache", () => {
26 | const mockCache: CachedMetadata = {};
27 |
28 | const result = fileHasCallout(mockCache);
29 |
30 | expect(result).toBe(false);
31 | });
32 |
33 | it("returns false if there are no callouts", () => {
34 | const mockCache: CachedMetadata = {
35 | sections: [
36 | {
37 | position: {
38 | end: { line: 0, col: 9, offset: 9 },
39 | start: { line: 0, col: 0, offset: 0 },
40 | },
41 | type: "heading",
42 | },
43 | {
44 | position: {
45 | end: { line: 2, col: 7, offset: 18 },
46 | start: { line: 2, col: 0, offset: 11 },
47 | },
48 | type: "paragraph",
49 | },
50 | ],
51 | };
52 |
53 | const result = fileHasCallout(mockCache);
54 |
55 | expect(result).toBe(false);
56 | });
57 | });
58 |
59 | describe("getFileHeadingLevelsCount", () => {
60 | it("returns 0 if no headings", () => {
61 | const mockCache: CachedMetadata = {
62 | sections: [
63 | {
64 | type: "callout",
65 | position: {
66 | end: { line: 2, col: 10, offset: 27 },
67 | start: { line: 1, col: 0, offset: 1 },
68 | },
69 | },
70 | ],
71 | };
72 |
73 | const result = getFileHeadingLevelsCount(mockCache);
74 |
75 | expect(result).toBe(0);
76 | });
77 |
78 | it("returns 0 if empty cache", () => {
79 | const mockCache: CachedMetadata = {};
80 |
81 | const result = getFileHeadingLevelsCount(mockCache);
82 |
83 | expect(result).toBe(0);
84 | });
85 |
86 | it("returns 1 if single heading", () => {
87 | const mockCache: CachedMetadata = {
88 | headings: [
89 | {
90 | heading: "Heading",
91 | level: 1,
92 | position: {
93 | end: { line: 0, col: 9, offset: 9 },
94 | start: { line: 0, col: 0, offset: 0 },
95 | },
96 | },
97 | ],
98 | };
99 |
100 | const result = getFileHeadingLevelsCount(mockCache);
101 |
102 | expect(result).toBe(1);
103 | });
104 |
105 | it("returns 1 if 2 headings same level", () => {
106 | const mockCache: CachedMetadata = {
107 | headings: [
108 | {
109 | heading: "Heading",
110 | level: 1,
111 | position: {
112 | end: { line: 0, col: 9, offset: 9 },
113 | start: { line: 0, col: 0, offset: 0 },
114 | },
115 | },
116 | {
117 | heading: "Another heading",
118 | level: 1,
119 | position: {
120 | end: { line: 4, col: 17, offset: 37 },
121 | start: { line: 4, col: 0, offset: 20 },
122 | },
123 | },
124 | ],
125 | };
126 |
127 | const result = getFileHeadingLevelsCount(mockCache);
128 |
129 | expect(result).toBe(1);
130 | });
131 |
132 | it("returns 2 if 2 headings different levels", () => {
133 | const mockCache: CachedMetadata = {
134 | headings: [
135 | {
136 | heading: "Heading",
137 | level: 1,
138 | position: {
139 | end: { line: 0, col: 9, offset: 9 },
140 | start: { line: 0, col: 0, offset: 0 },
141 | },
142 | },
143 | {
144 | heading: "Another heading",
145 | level: 2,
146 | position: {
147 | end: { line: 4, col: 18, offset: 38 },
148 | start: { line: 4, col: 0, offset: 20 },
149 | },
150 | },
151 | ],
152 | };
153 |
154 | const result = getFileHeadingLevelsCount(mockCache);
155 |
156 | expect(result).toBe(2);
157 | });
158 |
159 | it("returns 6 if 6 headings different levels", () => {
160 | const mockCache: CachedMetadata = {
161 | headings: [
162 | {
163 | position: {
164 | start: { line: 0, col: 0, offset: 0 },
165 | end: { line: 0, col: 13, offset: 13 },
166 | },
167 | heading: "First level",
168 | level: 1,
169 | },
170 | {
171 | position: {
172 | start: { line: 2, col: 0, offset: 15 },
173 | end: { line: 2, col: 15, offset: 30 },
174 | },
175 | heading: "Second level",
176 | level: 2,
177 | },
178 | {
179 | position: {
180 | start: { line: 4, col: 0, offset: 32 },
181 | end: { line: 4, col: 15, offset: 47 },
182 | },
183 | heading: "Third level",
184 | level: 3,
185 | },
186 | {
187 | position: {
188 | start: { line: 6, col: 0, offset: 49 },
189 | end: { line: 6, col: 17, offset: 66 },
190 | },
191 | heading: "Fourth level",
192 | level: 4,
193 | },
194 | {
195 | position: {
196 | start: { line: 8, col: 0, offset: 68 },
197 | end: { line: 8, col: 17, offset: 85 },
198 | },
199 | heading: "Fifth level",
200 | level: 5,
201 | },
202 | {
203 | position: {
204 | start: { line: 10, col: 0, offset: 87 },
205 | end: { line: 10, col: 18, offset: 105 },
206 | },
207 | heading: "Sixth level",
208 | level: 6,
209 | },
210 | ],
211 | };
212 |
213 | const result = getFileHeadingLevelsCount(mockCache);
214 |
215 | expect(result).toBe(6);
216 | });
217 | });
218 | });
219 |
--------------------------------------------------------------------------------
/src/achievements-view/AchievementsView.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 | {#each sortAchievements(SEEDED_ACHIEVEMENTS, $settingsStore) as achievement}
9 |
Resets all achievement progress. THIS CANNOT BE UNDONE.
11 |Are you sure you want to continue?
12 | 13 | -------------------------------------------------------------------------------- /src/reset-progress-modal/ResetProgressModal.ts: -------------------------------------------------------------------------------- 1 | import { App, Modal } from "obsidian"; 2 | import settingsStore from "src/settings"; 3 | import ResetProgressModalComponent from "./ResetProgressModal.svelte"; 4 | 5 | export class ResetProgressModal extends Modal { 6 | component: ResetProgressModalComponent; 7 | 8 | constructor(app: App) { 9 | super(app); 10 | } 11 | 12 | onOpen() { 13 | this.component = new ResetProgressModalComponent({ 14 | target: this.contentEl, 15 | props: { 16 | close: () => this.close(), 17 | closeAndReset: () => this.closeAndReset(), 18 | }, 19 | }); 20 | } 21 | 22 | async closeAndReset() { 23 | settingsStore.reset(); 24 | this.close(); 25 | } 26 | 27 | onClose() { 28 | const { contentEl } = this; 29 | contentEl.empty(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/seededAchievements.ts: -------------------------------------------------------------------------------- 1 | export type AchievementType = 2 | | "notesCreated" 3 | | "notesDeleted" 4 | | "internalLinksCreated" 5 | | "commandPaletteOpened" 6 | | "quickSwitcherOpened" 7 | | "calloutsCreated" 8 | | "headingLevelsCreated" 9 | | "tagsCreated"; 10 | 11 | export interface Achievement { 12 | id: string; 13 | type: AchievementType; 14 | name: string; 15 | description: string; 16 | popupMessage: string; // message that shows when achievement is achieved 17 | requiredOccurenceCount: number; 18 | } 19 | 20 | export const SEEDED_ACHIEVEMENTS: Achievement[] = [ 21 | { 22 | id: "notes-created:1", 23 | type: "notesCreated", 24 | name: "Your first note", 25 | description: "Create a note.", 26 | popupMessage: 27 | "You've created your first note. You're off to a great start!", 28 | requiredOccurenceCount: 1, 29 | }, 30 | { 31 | id: "notes-created:10", 32 | type: "notesCreated", 33 | name: "Note taker", 34 | description: "Create ten notes.", 35 | popupMessage: "You've created ten notes. Great job!", 36 | requiredOccurenceCount: 10, 37 | }, 38 | { 39 | id: "notes-created:100", 40 | type: "notesCreated", 41 | name: "Wordsmith", 42 | description: "Create one hundred notes.", 43 | popupMessage: 44 | "You've created one hundred notes! You're serious, aren't you?", 45 | requiredOccurenceCount: 100, 46 | }, 47 | { 48 | id: "notes-created:1000", 49 | type: "notesCreated", 50 | name: "Storyteller", 51 | description: "Create one thousand notes.", 52 | popupMessage: "You've created one thousand notes! That's impressive!", 53 | requiredOccurenceCount: 1000, 54 | }, 55 | { 56 | id: "notes-deleted:10", 57 | type: "notesDeleted", 58 | name: "Taking out the trash", 59 | description: "Delete ten notes.", 60 | popupMessage: "You've deleted 10 notes. Way to keep your vault tidy!", 61 | requiredOccurenceCount: 10, 62 | }, 63 | { 64 | id: "internal-links-created:1", 65 | type: "internalLinksCreated", 66 | name: "Linking your thinking", 67 | description: 68 | "Create an internal link. You can type [[ to begin creating a internal link.", 69 | popupMessage: 70 | "You've created your first internal link. It's all coming together!", 71 | requiredOccurenceCount: 1, 72 | }, 73 | { 74 | id: "internal-links-created:10", 75 | type: "internalLinksCreated", 76 | name: "Making connections", 77 | description: "Create ten internal links.", 78 | popupMessage: 79 | "You've created 10 internal links. Your graph is looking great!", 80 | requiredOccurenceCount: 10, 81 | }, 82 | { 83 | id: "internal-links-created:100", 84 | type: "internalLinksCreated", 85 | name: "Conspiracy theorist", 86 | description: "Create one hundred internal links", 87 | popupMessage: 88 | "You've created 100 internal links. Your graph is starting to look like a conspiracy board...", 89 | requiredOccurenceCount: 100, 90 | }, 91 | { 92 | id: "internal-links-created:1000", 93 | type: "internalLinksCreated", 94 | name: "Air traffic controller", 95 | description: "Create one thousand internal links", 96 | popupMessage: 97 | "You've created 1000 internal links. If you haven't already, you should post your graph on the official Obsidian discord.", 98 | requiredOccurenceCount: 1000, 99 | }, 100 | { 101 | id: "command-palette:open", 102 | type: "commandPaletteOpened", 103 | name: "Commander", 104 | description: 105 | "Open the command palette. You can find the hotkey to open the command palette in Settings - Hotkeys.", 106 | popupMessage: 107 | "You've opened the command palette. Way to take charge of your note taking!", 108 | requiredOccurenceCount: 1, 109 | }, 110 | { 111 | id: "switcher:open", 112 | type: "quickSwitcherOpened", 113 | name: "Quickly now", 114 | description: 115 | "Open the quick switcher. You can find the hotkey to open the quick switcher in Settings - Hotkeys.", 116 | popupMessage: "You've opened the quick switcher. Wow that was fast!", 117 | requiredOccurenceCount: 1, 118 | }, 119 | { 120 | id: "callouts:1", 121 | type: "calloutsCreated", 122 | name: "Callouts", 123 | description: 124 | "Create a callout. You can find the hotkey to create a callout in Settings - Hotkeys.", 125 | popupMessage: 126 | "You've created a callout. Just felt like that needed to be called out.", 127 | requiredOccurenceCount: 1, 128 | }, 129 | { 130 | id: "heading-levels:1", 131 | type: "headingLevelsCreated", 132 | name: "Headings", 133 | description: 134 | "Create a heading. You can create a heading by adding a new line to a note and typing # Heading.", 135 | popupMessage: 136 | "You've created a heading. Your notes are looking more organized already!", 137 | requiredOccurenceCount: 1, 138 | }, 139 | { 140 | id: "heading-levels:3", 141 | type: "headingLevelsCreated", 142 | name: "Nested headings", 143 | description: 144 | "Create at least three levels of headings in a single note.", 145 | popupMessage: 146 | "You've created at least 3 levels of nested headings. Your notes look so organized!", 147 | requiredOccurenceCount: 3, 148 | }, 149 | { 150 | id: "tags-created:1", 151 | type: "tagsCreated", 152 | name: "Your first tag", 153 | description: "Create a tag. You can create a tag by typing #tag.", 154 | popupMessage: "You've created your first tag!", 155 | requiredOccurenceCount: 1, 156 | }, 157 | { 158 | id: "tags-created:5", 159 | type: "tagsCreated", 160 | name: "Tagging apprentice", 161 | description: "Create five unique tags.", 162 | popupMessage: "You've created five unique tags!", 163 | requiredOccurenceCount: 5, 164 | }, 165 | { 166 | id: "tags-created:10", 167 | type: "tagsCreated", 168 | name: "Tagging expert", 169 | description: "Create ten unique tags.", 170 | popupMessage: "You've created ten unique tags!", 171 | requiredOccurenceCount: 10, 172 | }, 173 | ]; 174 | -------------------------------------------------------------------------------- /src/settings-tab/SettingsTab.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |