├── .npmrc ├── .tool-versions ├── .eslintignore ├── versions.json ├── commitlint.config.js ├── .husky ├── pre-commit └── commit-msg ├── assets └── demo.gif ├── test-vault ├── .obsidian │ ├── plugins │ │ ├── obsidian42-brat │ │ │ ├── styles.css │ │ │ └── manifest.json │ │ ├── task-status │ │ │ ├── styles.css │ │ │ ├── manifest.json │ │ │ └── main.js │ │ ├── obsidian-style-settings │ │ │ ├── manifest.json │ │ │ └── styles.css │ │ └── open-vscode │ │ │ ├── manifest.json │ │ │ └── main.js │ ├── community-plugins.json │ ├── themes │ │ ├── Border │ │ │ └── manifest.json │ │ ├── ITS Theme │ │ │ └── manifest.json │ │ ├── Things │ │ │ ├── manifest.json │ │ │ └── theme.css │ │ ├── AnuPpuccin │ │ │ └── manifest.json │ │ └── Minimal │ │ │ └── manifest.json │ ├── core-plugins.json │ ├── core-plugins-migration.json │ └── hotkeys.json ├── Checkboxes for Minimal Theme.md ├── Checkboxes for ITS Theme.md ├── Checkboxes for AnuPpuccin Theme.md ├── RegExr Test File.md ├── Test Note.md └── How Themes Target the Checkbox with CSS.md ├── src ├── themes │ ├── types.ts │ ├── index.ts │ ├── EbullientworksTheme.ts │ ├── BorderTheme.ts │ ├── AuraThemeTheme.ts │ ├── LYTModeTheme.ts │ ├── MinimalTheme.ts │ ├── ThingsTheme.ts │ ├── AnuPpuccinTheme.ts │ └── ITSTheme.ts ├── types.d.ts ├── commands │ └── open-task-quick-menu.ts ├── register-ribbon.ts ├── default-settings.ts ├── modals │ └── quick-action-modal.ts ├── settings.ts └── swap-checkbox-status.ts ├── styles.css ├── manifest.json ├── docs ├── Setup Border Theme.md ├── Setup Minimal Theme.md ├── Setup ITS Theme.md └── Setup AnuPpuccin Theme.md ├── tsconfig.json ├── .editorconfig ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── version-bump.mjs ├── .eslintrc ├── .commitlintrc.js ├── package.json ├── LICENSE ├── esbuild.config.mjs ├── main.ts ├── .prettierrc.yml ├── dev-log ├── Behavior.md ├── BDD.md └── Test Scenarios.md ├── TASKS.md ├── README.md └── code_of_conduct.md /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs lts 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | main.js 4 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.0": "0.15.0" 3 | } 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']} 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vburzynski/obsidian-task-status/HEAD/assets/demo.gif -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/obsidian42-brat/styles.css: -------------------------------------------------------------------------------- 1 | .brat-modal .modal-button-container { 2 | margin-top: 5px !important; 3 | } 4 | -------------------------------------------------------------------------------- /src/themes/types.ts: -------------------------------------------------------------------------------- 1 | import { CheckboxOption } from "src/types"; 2 | 3 | export type Theme = { 4 | name: string; 5 | statuses: CheckboxOption[]; 6 | }; 7 | -------------------------------------------------------------------------------- /test-vault/.obsidian/community-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "obsidian-task-status", 3 | "open-vscode", 4 | "obsidian42-brat", 5 | "task-status", 6 | "obsidian-style-settings" 7 | ] -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This CSS file will be included with your plugin, and 4 | available in the app when your plugin is enabled. 5 | 6 | If your plugin does not need CSS, delete this file. 7 | 8 | */ 9 | -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/Border/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Border", 3 | "version": "1.10.5", 4 | "minAppVersion": "0.16.0", 5 | "author": "Akifyss", 6 | "authorUrl": "https://github.com/Akifyss" 7 | } 8 | -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/ITS Theme/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ITS Theme", 3 | "version": "1.3.18", 4 | "minAppVersion": "0.16.0", 5 | "author": "SlRvb", 6 | "authorUrl": "https://github.com/SlRvb" 7 | } -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/Things/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Things", 3 | "version": "2.1.20", 4 | "minAppVersion": "1.0.0", 5 | "author": "@colineckert", 6 | "authorUrl": "https://twitter.com/colineckert" 7 | } 8 | -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/AnuPpuccin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AnuPpuccin", 3 | "version": "1.5.0", 4 | "minAppVersion": "1.6.0", 5 | "author": "Anubis", 6 | "authorUrl": "https://github.com/AnubisNekhet" 7 | } 8 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/task-status/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This CSS file will be included with your plugin, and 4 | available in the app when your plugin is enabled. 5 | 6 | If your plugin does not need CSS, delete this file. 7 | 8 | */ 9 | -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/Minimal/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Minimal", 3 | "version": "7.7.18", 4 | "minAppVersion": "1.6.1", 5 | "author": "@kepano", 6 | "authorUrl": "https://twitter.com/kepano", 7 | "fundingUrl": "https://www.buymeacoffee.com/kepano" 8 | } 9 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "task-status", 3 | "name": "Task Status", 4 | "version": "1.2.1", 5 | "minAppVersion": "0.15.0", 6 | "description": "Quickly select and apply custom task status markers", 7 | "author": "Valerie Burzynski", 8 | "authorUrl": "https://github.com/vburzynski/", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/task-status/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "task-status", 3 | "name": "Task Status", 4 | "version": "1.2.0", 5 | "minAppVersion": "0.15.0", 6 | "description": "Quickly select and apply custom task status markers", 7 | "author": "Valerie Burzynski", 8 | "authorUrl": "https://github.com/vburzynski/", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/obsidian-style-settings/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-style-settings", 3 | "name": "Style Settings", 4 | "version": "1.0.9", 5 | "minAppVersion": "0.11.5", 6 | "description": "Offers controls for adjusting theme, plugin, and snippet CSS variables.", 7 | "author": "mgmeyers", 8 | "authorUrl": "https://github.com/mgmeyers/obsidian-style-settings", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /test-vault/Checkboxes for Minimal Theme.md: -------------------------------------------------------------------------------- 1 | - [ ] to-do 2 | - [/] incomplete 3 | - [x] done 4 | - [-] canceled 5 | - [>] forwarded 6 | - [<] scheduling 7 | - [?] question 8 | - [!] important 9 | - [*] star 10 | - ["] quote 11 | - [l] location 12 | - [b] bookmark 13 | - [i] information 14 | - [S] savings 15 | - [I] idea 16 | - [p] pros 17 | - [c] cons 18 | - [f] fire 19 | - [k] key 20 | - [w] win 21 | - [u] up 22 | - [d] down 23 | 24 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/open-vscode/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "open-vscode", 3 | "name": "Open vault in VSCode", 4 | "version": "1.2.3", 5 | "minAppVersion": "1.6.6", 6 | "description": "Ribbon button and command to open the vault as a Visual Studio Code (VSCode) workspace", 7 | "author": "NomarCub", 8 | "authorUrl": "https://github.com/NomarCub", 9 | "fundingUrl": "https://ko-fi.com/nomarcub", 10 | "isDesktopOnly": true 11 | } -------------------------------------------------------------------------------- /docs/Setup Border Theme.md: -------------------------------------------------------------------------------- 1 | # Setup Border Theme 2 | 3 | ## Setup 4 | 5 | 1. Install and activate the **Border Theme** 6 | 2. Install the [Task Status](https://github.com/vburzynski/obsidian-task-status) plugin 7 | 3. Open the settings for the **Task Status** plugin 8 | 4. Click the **Clear List** button to clear the default configuration 9 | 5. Click the **Border Theme** button to add the configuration 10 | 11 | ## Resources 12 | 13 | - https://github.com/Akifyss/obsidian-border 14 | -------------------------------------------------------------------------------- /docs/Setup Minimal Theme.md: -------------------------------------------------------------------------------- 1 | # Setup Minimal Theme 2 | 3 | ## Setup 4 | 5 | 1. Install and activate the **Minimal Theme** 6 | 2. Install the [Task Status](https://github.com/vburzynski/obsidian-task-status) plugin 7 | 3. Open the settings for the **Task Status** plugin 8 | 4. Click the **Clear List** button to clear the default configuration 9 | 5. Click the **Minimal Theme** button to add the configuration 10 | 11 | ## Resources 12 | 13 | - https://github.com/kepano/obsidian-minimal 14 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | import { Command, Plugin } from "obsidian"; 2 | 3 | interface CommandCreator { 4 | (plugin: TaskStatusPluginInterface): Command; 5 | } 6 | 7 | interface TaskStatusPluginSettings { 8 | checkboxOptions: CheckboxOption[], 9 | } 10 | 11 | interface TaskStatusPluginInterface extends Plugin { 12 | settings: TaskStatusPluginSettings; 13 | loadSettings(): Promise; 14 | saveSettings(): Promise; 15 | } 16 | 17 | interface CheckboxOption { 18 | title: string; 19 | character: string; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7" 19 | ] 20 | }, 21 | "include": [ 22 | "**/*.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /docs/Setup ITS Theme.md: -------------------------------------------------------------------------------- 1 | # Setup ITS Theme 2 | 3 | ## Setup 4 | 5 | 1. Install and activate the **ITS Theme** 6 | 2. Install the [Task Status](https://github.com/vburzynski/obsidian-task-status) plugin 7 | 3. Open the settings for the **Task Status** plugin 8 | 4. Click the **Clear List** button to clear the default configuration 9 | 5. Click the **ITS Theme with SIRvb Checkboxes** button to add the configuration 10 | 11 | ## Resources 12 | 13 | - 14 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/obsidian42-brat/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian42-brat", 3 | "name": "BRAT", 4 | "version": "1.0.3", 5 | "minAppVersion": "1.4.16", 6 | "description": "Easily install a beta version of a plugin for testing.", 7 | "author": "TfTHacker", 8 | "authorUrl": "https://github.com/TfTHacker/obsidian42-brat", 9 | "helpUrl": "https://tfthacker.com/BRAT", 10 | "isDesktopOnly": false, 11 | "fundingUrl": { 12 | "Buy Me a Coffee": "https://bit.ly/o42-kofi", 13 | "Visit my site": "https://tfthacker.com" 14 | } 15 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | max_line_length = 100 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | # because whitespaces are important in diff files 14 | # because two or more spaces at the end of a markdown line is a newline 15 | [*.{diff,md,mdx}] 16 | trim_trailing_whitespace = false 17 | 18 | [{Makefile,**.mk}] 19 | indent_style = tab 20 | 21 | # Batch Files 22 | [*.{cmd,bat}] 23 | end_of_line = crlf 24 | -------------------------------------------------------------------------------- /.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 | 24 | # Exclude some test vault files 25 | test-vault/.obsidian/plugins/**/data.json 26 | test-vault/.obsidian/app.json 27 | test-vault/.obsidian/appearance.json 28 | test-vault/.obsidian/workspace.json 29 | -------------------------------------------------------------------------------- /src/themes/index.ts: -------------------------------------------------------------------------------- 1 | import { AnuPpuccinTheme } from './AnuPpuccinTheme'; 2 | import { AuraTheme } from './AuraThemeTheme'; 3 | import { EbullientworksTheme } from './EbullientworksTheme'; 4 | import { ITSTheme } from './ITSTheme'; 5 | import { LYTModeTheme } from './LYTModeTheme'; 6 | import { MinimalTheme } from './MinimalTheme'; 7 | import { ThingsTheme } from './ThingsTheme'; 8 | import { BorderTheme } from './BorderTheme'; 9 | 10 | export const themes = [ 11 | AnuPpuccinTheme, 12 | AuraTheme, 13 | EbullientworksTheme, 14 | ITSTheme, 15 | LYTModeTheme, 16 | MinimalTheme, 17 | ThingsTheme, 18 | BorderTheme, 19 | ]; 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 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 | -------------------------------------------------------------------------------- /src/themes/EbullientworksTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Status supported by the Ebullientworks theme. 5 | * {@link https://github.com/ebullient/obsidian-theme-ebullientworks} 6 | */ 7 | export const EbullientworksTheme: Theme = { 8 | name: 'Ebullientworks Theme', 9 | statuses: [ 10 | { character: ' ', title: 'Unchecked' }, 11 | { character: 'x', title: 'Checked' }, 12 | { character: '-', title: 'Cancelled' }, 13 | { character: '/', title: 'In Progress' }, 14 | { character: '>', title: 'Deferred' }, 15 | { character: '!', title: 'Important' }, 16 | { character: '?', title: 'Question' }, 17 | { character: 'r', title: 'Review' }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /version-bump.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | 3 | const targetVersion = process.env.npm_package_version; 4 | 5 | // read minAppVersion from manifest.json and bump version to target version 6 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); 7 | const { minAppVersion } = manifest; 8 | manifest.version = targetVersion; 9 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); 10 | 11 | // update versions.json with target version and minAppVersion from manifest.json 12 | let versions = JSON.parse(readFileSync("versions.json", "utf8")); 13 | versions[targetVersion] = minAppVersion; 14 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": [ 6 | "@typescript-eslint" 7 | ], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 19 | "@typescript-eslint/ban-ts-comment": "off", 20 | "no-prototype-builtins": "off", 21 | "@typescript-eslint/no-empty-function": "off" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test-vault/Checkboxes for ITS Theme.md: -------------------------------------------------------------------------------- 1 | 2 | - [ ] Unchecked 3 | - [x] Regular 4 | - [X] Checked 5 | - [-] Dropped 6 | - [>] Forward 7 | - [D] Date 8 | - [?] Question 9 | - [/] Half Done 10 | - [+] Add 11 | - [R] Research 12 | - [!] Important 13 | - [i] Idea 14 | - [B] Brainstorm 15 | - [P] Pro 16 | - [C] Con 17 | - [Q] Quote 18 | - [N] Note 19 | - [b] Bookmark 20 | - [I] Information 21 | - [p] Paraphrase 22 | - [L] Location 23 | - [E] Example 24 | - [A] Answer 25 | - [r] Reward 26 | - [c] Choice 27 | - [d] Doing 28 | - [T] Time 29 | - [@] Character / Person 30 | - [t] Talk 31 | - [O] Outline / Plot 32 | - [~] Conflict 33 | - [W] World 34 | - [f] Clue / Find 35 | - [F] Foreshadow 36 | - [H] Favorite / Health 37 | - [&] Symbolism 38 | - [s] Secret -------------------------------------------------------------------------------- /test-vault/Checkboxes for AnuPpuccin Theme.md: -------------------------------------------------------------------------------- 1 | - [ ] unchecked 2 | - [x] checked 3 | - [>] test 4 | - [<] test 5 | - [!] test 6 | - [-] test 7 | - [/] test 8 | - [?] test 9 | - [*] test 10 | - [n] test 11 | - [l] test 12 | - [i] test 13 | - [I] test 14 | - [S] test 15 | - [p] test 16 | - [c] test 17 | - [b] test 18 | - ["] test 19 | 20 | missing... 21 | 22 | - [u] test 23 | - [d] test 24 | - [f] test 25 | - [k] test 26 | - [w] test 27 | 28 | speech bubbles... 29 | 30 | - [0] test 31 | - [1] test 32 | - [2] test 33 | - [3] test 34 | - [4] test 35 | - [5] test 36 | - [6] test 37 | - [7] test 38 | - [8] test 39 | - [9] test 40 | 41 | 42 | ## Task-Status Plugin Bugs... 43 | 44 | - [ ] the speech bubbles don't render correctly inside the suggestion dialog -------------------------------------------------------------------------------- /docs/Setup AnuPpuccin Theme.md: -------------------------------------------------------------------------------- 1 | # Setup AnuPpuccin 2 | 3 | ## Setup 4 | 5 | 1. Install and activate the **AnuPpuccin** theme 6 | 2. Install the **Style Settings** Plugin 7 | 3. Open the settings for the Style Settings plugin 8 | 4. Locate the checkbox settings: `AnuPpuccin` ▶ `File Editor & Markdown Elements` ▶ `Checkboxes` 9 | 5. Toggle `Enable Custom Checkboxes` to active 10 | 6. Toggle `Enable Speech Bubbles` to active 11 | 7. Install the [Task Status](https://github.com/vburzynski/obsidian-task-status) plugin 12 | 8. Open the settings for the **Task Status** plugin 13 | 9. Click the **Clear List** button to clear the default configuration 14 | 10. Click the **AnuPpucin Theme** button to add the configuration 15 | 16 | ## Resources 17 | 18 | - https://github.com/AnubisNekhet/AnuPpuccin/ 19 | -------------------------------------------------------------------------------- /test-vault/.obsidian/core-plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "file-explorer": true, 3 | "global-search": true, 4 | "switcher": true, 5 | "graph": true, 6 | "backlink": true, 7 | "canvas": true, 8 | "outgoing-link": true, 9 | "tag-pane": true, 10 | "page-preview": true, 11 | "daily-notes": true, 12 | "templates": true, 13 | "note-composer": true, 14 | "command-palette": true, 15 | "slash-command": false, 16 | "editor-status": true, 17 | "bookmarks": true, 18 | "markdown-importer": false, 19 | "zk-prefixer": false, 20 | "random-note": false, 21 | "outline": true, 22 | "word-count": true, 23 | "slides": false, 24 | "audio-recorder": false, 25 | "workspaces": false, 26 | "file-recovery": true, 27 | "publish": false, 28 | "sync": false, 29 | "properties": false 30 | } -------------------------------------------------------------------------------- /test-vault/.obsidian/core-plugins-migration.json: -------------------------------------------------------------------------------- 1 | { 2 | "file-explorer": true, 3 | "global-search": true, 4 | "switcher": true, 5 | "graph": true, 6 | "backlink": true, 7 | "canvas": true, 8 | "outgoing-link": true, 9 | "tag-pane": true, 10 | "page-preview": true, 11 | "daily-notes": true, 12 | "templates": true, 13 | "note-composer": true, 14 | "command-palette": true, 15 | "slash-command": false, 16 | "editor-status": true, 17 | "bookmarks": true, 18 | "markdown-importer": false, 19 | "zk-prefixer": false, 20 | "random-note": false, 21 | "outline": true, 22 | "word-count": true, 23 | "slides": false, 24 | "audio-recorder": false, 25 | "workspaces": false, 26 | "file-recovery": true, 27 | "publish": false, 28 | "sync": false, 29 | "properties": false 30 | } -------------------------------------------------------------------------------- /src/commands/open-task-quick-menu.ts: -------------------------------------------------------------------------------- 1 | import { Command, MarkdownView, Editor } from "obsidian"; 2 | import { TaskStatusPluginInterface } from "src/types"; 3 | import QuickActionModal from '../modals/quick-action-modal'; 4 | 5 | /** 6 | * command creator which constructs a command object configured to open the quick action modal 7 | * @param plugin the plugin instance 8 | * @returns a command object 9 | */ 10 | const changeTaskStatus = (plugin: TaskStatusPluginInterface): Command => ({ 11 | id: 'change-task-status', 12 | name: 'change task status', 13 | // hotkeys: [{ modifiers: ["Mod", "Shift"], key: "l" }], 14 | editorCallback: (editor: Editor, view: MarkdownView) => { 15 | new QuickActionModal(plugin.app, plugin, editor).open(); 16 | } 17 | }); 18 | 19 | export default changeTaskStatus; 20 | 21 | -------------------------------------------------------------------------------- /test-vault/.obsidian/hotkeys.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor:cycle-list-checklist": [ 3 | { 4 | "modifiers": [ 5 | "Mod", 6 | "Shift" 7 | ], 8 | "key": "B" 9 | } 10 | ], 11 | "task-status:change-task-status": [ 12 | { 13 | "modifiers": [ 14 | "Mod", 15 | "Shift" 16 | ], 17 | "key": "L" 18 | } 19 | ], 20 | "editor:swap-line-down": [ 21 | { 22 | "modifiers": [ 23 | "Alt" 24 | ], 25 | "key": "ArrowDown" 26 | } 27 | ], 28 | "editor:swap-line-up": [ 29 | { 30 | "modifiers": [ 31 | "Alt" 32 | ], 33 | "key": "ArrowUp" 34 | } 35 | ], 36 | "editor:toggle-source": [ 37 | { 38 | "modifiers": [ 39 | "Mod", 40 | "Shift" 41 | ], 42 | "key": "E" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@commitlint/config-conventional', // scoped packages are not prefixed 4 | ], 5 | rules: { 6 | 'type-enum': [ 7 | 2, 8 | 'always', 9 | [ 10 | 'feat', // new feature 11 | 'test', // adding or correcting tests 12 | 'fix', // bug fix 13 | 'refactor', // changes that neither fixes a bug nor adds a feature 14 | 'perf', // changes that improve performance 15 | 'chore', // changes that don't modify src or test files 16 | 'style', // changes that do not affect the meaning of the code 17 | 'docs', // changes to documentation 18 | 'ci', // change to Continuous Integrations (CI) files and scripts 19 | 'build', // changes that affect the build system or external dependencies 20 | 'revert', // reverts a previous commit 21 | 'release', // version and other trivial changes preparing for a release 22 | ], 23 | ], 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 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 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-task-status", 3 | "version": "1.2.1", 4 | "description": "Quickly change any task status in your Obsidian notes", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json", 10 | "prepare": "husky install", 11 | "test": "echo 'no tests yet'" 12 | }, 13 | "keywords": [ 14 | "obsidian" 15 | ], 16 | "author": "Valerie Burzynski", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@commitlint/cli": "^18.4.4", 20 | "@commitlint/config-conventional": "^18.4.4", 21 | "@types/node": "^20.10.4", 22 | "@typescript-eslint/eslint-plugin": "6.14.0", 23 | "@typescript-eslint/parser": "6.14.0", 24 | "builtin-modules": "3.3.0", 25 | "esbuild": "0.19.9", 26 | "obsidian": "latest", 27 | "tslib": "2.6.2", 28 | "typescript": "5.3.3", 29 | "husky": "^8.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Valerie Burzynski 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 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === "production"); 13 | 14 | const context = await esbuild.context({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ["main.ts"], 19 | bundle: true, 20 | external: [ 21 | "obsidian", 22 | "electron", 23 | "@codemirror/autocomplete", 24 | "@codemirror/collab", 25 | "@codemirror/commands", 26 | "@codemirror/language", 27 | "@codemirror/lint", 28 | "@codemirror/search", 29 | "@codemirror/state", 30 | "@codemirror/view", 31 | "@lezer/common", 32 | "@lezer/highlight", 33 | "@lezer/lr", 34 | ...builtins], 35 | format: "cjs", 36 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: prod ? false : "inline", 39 | treeShaking: true, 40 | outfile: "main.js", 41 | }); 42 | 43 | if (prod) { 44 | await context.rebuild(); 45 | process.exit(0); 46 | } else { 47 | await context.watch(); 48 | } -------------------------------------------------------------------------------- /src/themes/BorderTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Statuses supported by the Border theme. 5 | * {@link https://github.com/Akifyss/obsidian-border} 6 | */ 7 | export const BorderTheme: Theme = { 8 | name: 'Border Theme', 9 | statuses: [ 10 | // Basic 11 | { character: ' ', title: 'To Do' }, 12 | { character: '/', title: 'In Progress' }, 13 | { character: 'x', title: 'Done' }, 14 | { character: '-', title: 'Canceled' }, 15 | { character: '>', title: 'Rescheduled' }, 16 | { character: '<', title: 'Scheduled' }, 17 | // Extras 18 | { character: '!', title: 'Important' }, 19 | { character: '?', title: 'Question' }, 20 | { character: 'i', title: 'Information' }, 21 | { character: 'S', title: 'Amount' }, 22 | { character: '*', title: 'Star' }, 23 | { character: 'b', title: 'Bookmark' }, 24 | { character: '"', title: 'Quote' }, 25 | { character: 'n', title: 'Note' }, 26 | { character: 'l', title: 'Location' }, 27 | { character: 'I', title: 'Idea' }, 28 | { character: 'p', title: 'Pro' }, 29 | { character: 'c', title: 'Con' }, 30 | { character: 'u', title: 'up' }, 31 | { character: 'd', title: 'down' }, 32 | ] 33 | }; 34 | -------------------------------------------------------------------------------- /src/themes/AuraThemeTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from './types'; 2 | 3 | /** 4 | * Status supported by the Aura theme. 5 | * {@link https://github.com/ashwinjadhav818/obsidian-aura} 6 | */ 7 | export const AuraTheme: Theme = { 8 | name: 'Aura Theme', 9 | statuses: [ 10 | { character: ' ', title: 'incomplete' }, 11 | { character: 'x', title: 'complete / done' }, 12 | { character: '-', title: 'cancelled' }, 13 | { character: '>', title: 'deferred' }, 14 | { character: '/', title: 'in progress, or half-done' }, 15 | { character: '!', title: 'Important' }, 16 | { character: '?', title: 'question' }, 17 | { character: 'R', title: 'review' }, 18 | { character: '+', title: 'Inbox / task that should be processed later' }, 19 | { character: 'b', title: 'bookmark' }, 20 | { character: 'B', title: 'brainstorm' }, 21 | { character: 'D', title: 'deferred or scheduled' }, 22 | { character: 'I', title: 'Info' }, 23 | { character: 'i', title: 'idea' }, 24 | { character: 'N', title: 'note' }, 25 | { character: 'Q', title: 'quote' }, 26 | { character: 'W', title: 'win / success / reward' }, 27 | { character: 'P', title: 'pro' }, 28 | { character: 'C', title: 'con' }, 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /src/themes/LYTModeTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Status supported by the LYT Mode theme. 5 | * {@link https://github.com/nickmilo/LYT-Mode} 6 | */ 7 | export const LYTModeTheme: Theme = { 8 | name: 'LYT Mode Theme', 9 | statuses: [ 10 | { character: ' ', title: 'Unchecked' }, 11 | { character: 'x', title: 'Checked' }, 12 | { character: '>', title: 'Rescheduled' }, 13 | { character: '<', title: 'Scheduled' }, 14 | { character: '!', title: 'Important' }, 15 | { character: '-', title: 'Cancelled' }, 16 | { character: '/', title: 'In Progress' }, 17 | { character: '?', title: 'Question' }, 18 | { character: '*', title: 'Star' }, 19 | { character: 'n', title: 'Note' }, 20 | { character: 'l', title: 'Location' }, 21 | { character: 'i', title: 'Information' }, 22 | { character: 'I', title: 'Idea' }, 23 | { character: 'S', title: 'Amount' }, 24 | { character: 'p', title: 'Pro' }, 25 | { character: 'c', title: 'Con' }, 26 | { character: 'b', title: 'Bookmark' }, 27 | { character: 'f', title: 'Fire' }, 28 | { character: 'k', title: 'Key' }, 29 | { character: 'w', title: 'Win' }, 30 | { character: 'u', title: 'Up' }, 31 | { character: 'd', title: 'Down' }, 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /src/themes/MinimalTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Statuses supported by the Minimal theme. 5 | * {@link https://github.com/kepano/obsidian-minimal} 6 | */ 7 | export const MinimalTheme: Theme = { 8 | name: 'Minimal Theme', 9 | statuses: [ 10 | { character: ' ', title: 'to-do' }, 11 | { character: '/', title: 'incomplete' }, 12 | { character: 'x', title: 'done' }, 13 | { character: '-', title: 'canceled' }, 14 | { character: '>', title: 'forwarded' }, 15 | { character: '<', title: 'scheduling' }, 16 | { character: '?', title: 'question' }, 17 | { character: '!', title: 'important' }, 18 | { character: '*', title: 'star' }, 19 | { character: '"', title: 'quote' }, 20 | { character: 'l', title: 'location' }, 21 | { character: 'b', title: 'bookmark' }, 22 | { character: 'i', title: 'information' }, 23 | { character: 'S', title: 'savings' }, 24 | { character: 'I', title: 'idea' }, 25 | { character: 'p', title: 'pros' }, 26 | { character: 'c', title: 'cons' }, 27 | { character: 'f', title: 'fire' }, 28 | { character: 'k', title: 'key' }, 29 | { character: 'w', title: 'win' }, 30 | { character: 'u', title: 'up' }, 31 | { character: 'd', title: 'down' }, 32 | ] 33 | }; 34 | -------------------------------------------------------------------------------- /src/themes/ThingsTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Statuses supported by the Things theme. 5 | * {@link https://github.com/colineckert/obsidian-things} 6 | */ 7 | export const ThingsTheme: Theme = { 8 | name: 'Things Theme', 9 | statuses: [ 10 | // Basic 11 | { character: ' ', title: 'to-do' }, 12 | { character: '/', title: 'incomplete' }, 13 | { character: 'x', title: 'done' }, 14 | { character: '-', title: 'canceled' }, 15 | { character: '>', title: 'forwarded' }, 16 | { character: '<', title: 'scheduling' }, 17 | // Extras 18 | { character: '?', title: 'question' }, 19 | { character: '!', title: 'important' }, 20 | { character: '*', title: 'star' }, 21 | { character: '"', title: 'quote' }, 22 | { character: 'l', title: 'location' }, 23 | { character: 'b', title: 'bookmark' }, 24 | { character: 'i', title: 'information' }, 25 | { character: 'S', title: 'savings' }, 26 | { character: 'I', title: 'idea' }, 27 | { character: 'p', title: 'pros' }, 28 | { character: 'c', title: 'cons' }, 29 | { character: 'f', title: 'fire' }, 30 | { character: 'k', title: 'key' }, 31 | { character: 'w', title: 'win' }, 32 | { character: 'u', title: 'up' }, 33 | { character: 'd', title: 'down' }, 34 | ] 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'obsidian'; 2 | import { TaskStatusPluginInterface, TaskStatusPluginSettings } from 'src/types'; 3 | import Settings from 'src/settings'; 4 | import registerRibbon from 'src/register-ribbon'; 5 | import changeTaskStatus from 'src/commands/open-task-quick-menu'; 6 | import DEFAULT_SETTINGS from 'src/default-settings'; 7 | 8 | export default class TaskStatusPlugin extends Plugin implements TaskStatusPluginInterface { 9 | settings: TaskStatusPluginSettings; 10 | 11 | /** 12 | * Setup the plugin when it loads in obsidian 13 | */ 14 | async onload() { 15 | console.log('loading Obsidian Task Status'); 16 | await this.loadSettings(); 17 | registerRibbon(this); 18 | this.addCommand(changeTaskStatus(this)); 19 | this.addSettingTab(new Settings(this.app, this)); 20 | } 21 | 22 | /** 23 | * Teardown the plugin when it gets unloaded 24 | */ 25 | onunload() { 26 | console.log('unloading Obsidian Task Status'); 27 | } 28 | 29 | /** 30 | * Trigger the rendering of the settings view 31 | */ 32 | async loadSettings() { 33 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 34 | } 35 | 36 | /** 37 | * persist/save the plugin settings 38 | */ 39 | async saveSettings() { 40 | await this.saveData(this.settings); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/register-ribbon.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownView, addIcon } from 'obsidian'; 2 | import { TaskStatusPluginInterface } from "./types"; 3 | import QuickActionModal from './modals/quick-action-modal'; 4 | 5 | /** 6 | * Registers a Ribbon Icon that can open the action modal 7 | */ 8 | export default (plugin: TaskStatusPluginInterface) => { 9 | // Add an icon (modified version of https://lucide.dev/icons/search-check) 10 | addIcon( 11 | 'search-check', 12 | ` 13 | 14 | 15 | 16 | 17 | 18 | ` 19 | ); 20 | 21 | // create an icon in the left ribbon. 22 | plugin.addRibbonIcon( 23 | 'search-check', 24 | 'Change Checkbox Status', 25 | (_event: MouseEvent) => { 26 | const activeView = plugin.app.workspace.getActiveViewOfType(MarkdownView); 27 | if (!activeView) return; 28 | 29 | const editor = activeView.editor; 30 | new QuickActionModal(plugin.app, plugin, editor).open(); 31 | } 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | endOfLine: lf 3 | 4 | # Specify the line length that the printer will wrap on. 5 | printWidth: 120 6 | 7 | # Use single quotes instead of double quotes in JSX. 8 | singleQuote: true 9 | 10 | # Specify the number of spaces per indentation-level. 11 | tabWidth: 2 12 | 13 | # Indent lines with tabs instead of spaces. 14 | useTabs: false 15 | 16 | # Enforce single attribute per line in HTML, Vue and JSX. 17 | # - false - Do not enforce single attribute per line. 18 | # - true - Enforce single attribute per line. 19 | singleAttributePerLine: false 20 | 21 | # Do not print semicolons, except at the beginning of lines which may need them. 22 | # - true - add a semicolon at the end of every line 23 | # - false - only add semicolons at the beginning of lines that may introduce ASI failures 24 | semi: true 25 | 26 | # Print trailing commas wherever possible. 27 | # - "none" - no trailing commas 28 | # - "es5" - trailing commas where valid in ES5 (objects, arrays, etc) 29 | # - "all" - trailing commas wherever possible (function arguments) 30 | trailingComma: es5 31 | 32 | # Do not print spaces between brackets. 33 | # If true, puts the > of a multi-line jsx element at the end of the last line instead of being 34 | # alone on the next line 35 | bracketSameLine: false 36 | 37 | # different configuration for certain file patterns 38 | overrides: 39 | - files: "*.scss" 40 | options: 41 | singleQuote: false 42 | -------------------------------------------------------------------------------- /dev-log/Behavior.md: -------------------------------------------------------------------------------- 1 | # Behavior 2 | 3 | This is a bunch of notes attempting to capture and define the rules and behavior of the plugin 4 | 5 | ## Current Behavior 6 | 7 | - the initial code processes most things as single lines of text 8 | - incidentally, most inline formatting or inline blocks are handled in a natural manner. 9 | - anything not identified as having a special purpose is just treated as if it's another line of text. 10 | - Lines of text and paragraphs 11 | - Each selected line of text will be transformed into a task item 12 | - Indentation 13 | - when applying a custom tasks status, indentation should be preserved 14 | - supports various kinds of whitespace (spaces and tabs) 15 | - Quote Blocks 16 | - Markers for quote blocks `>` are handled properly 17 | - Nested markers for quote blocks are handled properly 18 | - The custom task status will be applied inside the block 19 | - **example**: line nested inside two quote blocks 20 | - original text: `> > content` 21 | - transformed text: `> > -[x] content` 22 | - Lists 23 | - task list items, ordered list items, and unordered list items, are transformed into task list items with the selected custom task marker. 24 | - multi-line content isn't accounted for yet. 25 | - **examples**: setext headings, code blocks, and HTML 26 | - does not detect content that continues onto multiple lines 27 | - this includes both the lazy and non-lazy continuation syntax 28 | - Callouts 29 | - the first line of a callout is ignored (it contains the callout type and optional title) 30 | -------------------------------------------------------------------------------- /src/themes/AnuPpuccinTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Statuses supported by the AnuPpuccin theme. 5 | * {@link https://github.com/AnubisNekhet/AnuPpuccin} 6 | */ 7 | export const AnuPpuccinTheme: Theme = { 8 | name: 'AnuPpuccin Theme', 9 | statuses: [ 10 | { character: ' ', title: 'Unchecked' }, 11 | { character: 'x', title: 'Checked' }, 12 | { character: '>', title: 'Rescheduled' }, 13 | { character: '<', title: 'Scheduled' }, 14 | { character: '!', title: 'Important' }, 15 | { character: '-', title: 'Cancelled' }, 16 | { character: '/', title: 'In Progress' }, 17 | { character: '?', title: 'Question' }, 18 | { character: '*', title: 'Star' }, 19 | { character: 'n', title: 'Note' }, 20 | { character: 'l', title: 'Location' }, 21 | { character: 'i', title: 'Information' }, 22 | { character: 'I', title: 'Idea' }, 23 | { character: 'S', title: 'Amount' }, 24 | { character: 'p', title: 'Pro' }, 25 | { character: 'c', title: 'Con' }, 26 | { character: 'b', title: 'Bookmark' }, 27 | { character: '"', title: 'Quote' }, 28 | { character: '0', title: 'Speech bubble 0' }, 29 | { character: '1', title: 'Speech bubble 1' }, 30 | { character: '2', title: 'Speech bubble 2' }, 31 | { character: '3', title: 'Speech bubble 3' }, 32 | { character: '4', title: 'Speech bubble 4' }, 33 | { character: '5', title: 'Speech bubble 5' }, 34 | { character: '6', title: 'Speech bubble 6' }, 35 | { character: '7', title: 'Speech bubble 7' }, 36 | { character: '8', title: 'Speech bubble 8' }, 37 | { character: '9', title: 'Speech bubble 9' }, 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /src/default-settings.ts: -------------------------------------------------------------------------------- 1 | import { TaskStatusPluginSettings } from './types'; 2 | 3 | const DEFAULT_SETTINGS: TaskStatusPluginSettings = { 4 | // prettier-ignore 5 | checkboxOptions: [ 6 | { title: 'to-do', character: ' ' }, 7 | { title: 'incomplete', character: '/' }, 8 | { title: 'done', character: 'x' }, 9 | { title: 'canceled', character: '-' }, 10 | { title: 'forwarded', character: '>' }, 11 | { title: 'scheduling', character: '<' }, 12 | { title: 'question', character: '?' }, 13 | { title: 'important', character: '!' }, 14 | { title: 'star', character: '*' }, 15 | { title: 'quote', character: '"' }, 16 | { title: 'location', character: 'l' }, 17 | { title: 'bookmark', character: 'b' }, 18 | { title: 'information', character: 'i' }, 19 | { title: 'savings', character: 'S' }, 20 | { title: 'idea', character: 'I' }, 21 | { title: 'pros', character: 'p' }, 22 | { title: 'cons', character: 'c' }, 23 | { title: 'fire', character: 'f' }, 24 | { title: 'key', character: 'k' }, 25 | { title: 'win', character: 'w' }, 26 | { title: 'up', character: 'u' }, 27 | { title: 'down', character: 'd' }, 28 | { title: 'draft pull request', character: 'D' }, 29 | { title: 'open pull request', character: 'P' }, 30 | { title: 'merged pull request', character: 'M' }, 31 | ], 32 | }; 33 | 34 | export default DEFAULT_SETTINGS; 35 | -------------------------------------------------------------------------------- /dev-log/BDD.md: -------------------------------------------------------------------------------- 1 | # Behaviors 2 | 3 | This isn't a complete set of behaviors, just some that I started writing 4 | 5 | ```gherkin 6 | Feature: Single-Line Selections 7 | 8 | Scenario: blank line 9 | GIVEN a blank line is selected 10 | WHEN a selection is made with the checkbox status quick menu 11 | THEN insert a task item accented with the selected status marker 12 | 13 | Scenario: Ignore Headings 14 | GIVEN a heading is selected 15 | WHEN a custom task status is selected 16 | THEN the heading doesn't change 17 | 18 | Feature: Multi-Line Selections 19 | 20 | Scenario: blank lines 21 | GIVEN a selection range spanning multiple lines 22 | AND some of those lines are blank 23 | WHEN a custom task status is selected 24 | THEN the blank lines do not change 25 | 26 | Scenario: headings 27 | GIVEN a selection range spanning multiple lines 28 | AND some of those lines are headings 29 | WHEN a custom task status is selected 30 | THEN the headings do not change 31 | 32 | Scenario: Selection includes an Obsidian Callout 33 | GIVEN a user has selected multiple lines of text 34 | WHEN an obsidian callout is part of that selection 35 | THEN don't apply a task marker to the first line of any callout blocks 36 | 37 | Feature: Auto-correct mid-line selections 38 | 39 | Scenario: fragment of a single-line 40 | GIVEN a selection range spanning a fragment of a single line 41 | AND the start of the selection is midway through the line 42 | WHEN a custom task status is selected 43 | THEN the task marking is applied at the start of the line 44 | AND not in the middle of the line 45 | 46 | Scenario: mid-line selection 47 | GIVEN an editor selection 48 | AND the start of the selection is mid-line 49 | AND the first line contains plain text 50 | WHEN a custom task marker is applied 51 | THEN the marker should appear at the start of the content 52 | AND the marker should appear after any indentation 53 | AND the marker should appear after any callout or block quote markers 54 | ``` 55 | -------------------------------------------------------------------------------- /src/themes/ITSTheme.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./types"; 2 | 3 | /** 4 | * Statuses supported by the ITS theme. 5 | * {@link https://github.com/SlRvb/Obsidian--ITS-Theme} 6 | */ 7 | export const ITSTheme: Theme = { 8 | name: 'ITS Theme with SlRvb Checkboxes', 9 | statuses: [ 10 | { character: ' ', title: 'Unchecked' }, 11 | { character: 'x', title: 'Regular' }, 12 | { character: 'X', title: 'Checked' }, 13 | { character: '-', title: 'Dropped' }, 14 | { character: '>', title: 'Forward' }, 15 | { character: 'D', title: 'Date' }, 16 | { character: '?', title: 'Question' }, 17 | { character: '/', title: 'Half Done' }, 18 | { character: '+', title: 'Add' }, 19 | { character: 'R', title: 'Research' }, 20 | { character: '!', title: 'Important' }, 21 | { character: 'i', title: 'Idea' }, 22 | { character: 'B', title: 'Brainstorm' }, 23 | { character: 'P', title: 'Pro' }, 24 | { character: 'C', title: 'Con' }, 25 | { character: 'Q', title: 'Quote' }, 26 | { character: 'N', title: 'Note' }, 27 | { character: 'b', title: 'Bookmark' }, 28 | { character: 'I', title: 'Information' }, 29 | { character: 'p', title: 'Paraphrase' }, 30 | { character: 'L', title: 'Location' }, 31 | { character: 'E', title: 'Example' }, 32 | { character: 'A', title: 'Answer' }, 33 | { character: 'r', title: 'Reward' }, 34 | { character: 'c', title: 'Choice' }, 35 | { character: 'd', title: 'Doing' }, 36 | { character: 'T', title: 'Time' }, 37 | { character: '@', title: 'Character / Person' }, 38 | { character: 't', title: 'Talk' }, 39 | { character: 'O', title: 'Outline / Plot' }, 40 | { character: '~', title: 'Conflict' }, 41 | { character: 'W', title: 'World' }, 42 | { character: 'f', title: 'Clue / Find' }, 43 | { character: 'F', title: 'Foreshadow' }, 44 | { character: 'H', title: 'Favorite / Health' }, 45 | { character: '&', title: 'Symbolism' }, 46 | { character: 's', title: 'Secret' }, 47 | ] 48 | }; 49 | -------------------------------------------------------------------------------- /test-vault/RegExr Test File.md: -------------------------------------------------------------------------------- 1 | 2 | ## Content 3 | 4 | some arbitrary text content for use when testing Regular Expressions... 5 | 6 | ```text 7 | > [!NOTE] Title 8 | > - [*] Contents 9 | > - [*] a 10 | > - [*] b 11 | > - [*] c 12 | > > testing 13 | > > - task 14 | 15 | > [!NOTE] Title 16 | > - [*] Contents 17 | > - [*] a 18 | > - [*] b 19 | > - [*] c 20 | > > - [l] testing 21 | > > - [l] task 22 | 23 | - test text 24 | * test text 25 | - [ ] test text 26 | * [ ] test text 27 | 1. testing 28 | 2. testing 29 | 10. testing 30 | 31 | - test 32 | - test 33 | - test 34 | - test 35 | 36 | + test 37 | + test 38 | + test 39 | + test 40 | 41 | * test 42 | * test 43 | * test 44 | * test 45 | 46 | [!note] test 47 | > [!note] 48 | > [!test] test test 49 | > [!custom-test] 50 | > [!custom_test] 51 | > [!custom/test] 52 | > [!custom 53 | > [] 54 | > [!] 55 | > [other] test 56 | > Lorem ipsum dolor sit amet 57 | 58 | -- 59 | --- 60 | --- test 61 | -- test 62 | -- 63 | ---- 64 | --- 65 | -- 66 | --- 67 | ** 68 | *** 69 | **** 70 | *-* 71 | __ 72 | ___ 73 | ____ 74 | * * * 75 | * ** 76 | - - - 77 | ** * ** * ** * ** 78 | - - - - 79 | 80 | > [!NOTE] NOTE 81 | > content 82 | > - [ ] test text 83 | > * [ ] test text 84 | > - test text 85 | > * test text 86 | > testing 87 | > 1. testing 88 | > 2. Testing 89 | > 10. Testing 90 | > > - test test 91 | 92 | - test 93 | - test text 94 | * test text 95 | - [ ] test text 96 | * [ ] test text 97 | 98 | > [!NOTE] NOTE 99 | > - [ ] test text 100 | > * [ ] test text 101 | > - test text 102 | > * test text 103 | > testing 104 | 105 | testing 106 | 107 | # This is a heading 1 108 | ## This is a heading 2 109 | ### This is a heading 3 110 | #### This is a heading 4 111 | ##### This is a heading 5 112 | ###### This is a heading 6 113 | 114 | this line has a tag #tag 115 | 116 | this line has no tag 117 | 118 | # indented line starting with a hash for some reason 119 | ``` -------------------------------------------------------------------------------- /TASKS.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | ## Brainstorming 4 | 5 | ### Feature: Quickly apply previously selected status 6 | 7 | - [ ] auto memoize/cache/store/persist the previously used status 8 | - [ ] command to quickly re-apply last status 9 | - [ ] ability to toggle between no marker and last used marker 10 | - for now, you can use a combination of `Cycle bullet/checkbox`; `Toggle bullet list`; and the checkbox quick-menu 11 | - [ ] test dynamically changing the set of custom statuses and having commands for each one. 12 | - this would allow direct access to set a keyboard shortcut to a single item, also using the command pallette 13 | - `this.app.commands.removeCommand(ID)` -- this is unsafe and not part of the public API it seems. 14 | 15 | ### Commands and Hotkeys 16 | 17 | - [ ] implement individual command for each type of custom checkbox accent 18 | - this would allow users to directly apply a custom accent through either a hotkey or command palette. 19 | 20 | ### Multiple Line Selection Features 21 | 22 | ```text 23 | Feature: Ignore certain content blocks 24 | 25 | Scenario: Selection includes a Code Block 26 | GIVEN a user has selected multiple lines of text 27 | WHEN a code block is part of that selection 28 | THEN don't add check box markers to any lines of the code block 29 | 30 | Scenario: Selection includes a Single Line Obsidian Comment 31 | GIVEN a user has selected multiple lines of text 32 | AND some of those lines are single-line comments 33 | WHEN a custom task status is applied 34 | THEN don't modify any of the single-line comments 35 | 36 | Scenario: Selection includes a Multi-Line Obsidian Comment 37 | GIVEN a user has selected multiple lines of text 38 | AND some of those lines are multi-line comments 39 | WHEN a custom task status is applied 40 | THEN don't modify any lines of the comments 41 | 42 | Scenario: start of selection intersects with a multi-line comment block 43 | Scenario: end of selection intersects with a multi-line comment block 44 | Scenario: start of selection intersects with a code block 45 | Scenario: end of selection intersects with a code block 46 | Scenario: start of selection intersects with a callout block 47 | Scenario: end of selection intersects with a callout block 48 | ``` 49 | 50 | ## Release Workflow 51 | 52 | ```shell 53 | git tag -a 1.1.0 -m "1.1.0" 54 | git push origin 1.1.0 55 | gh release create "1.1.0" --title="1.1.0" --draft main.js manifest.json styles.css 56 | ``` 57 | -------------------------------------------------------------------------------- /src/modals/quick-action-modal.ts: -------------------------------------------------------------------------------- 1 | import { Editor, Notice, SuggestModal, App } from "obsidian"; 2 | import SwapCheckboxStatus from "src/swap-checkbox-status"; 3 | import { CheckboxOption, TaskStatusPluginInterface } from "src/types"; 4 | 5 | /** 6 | * A serchable modal that allows the user to select a checkbox status symbol 7 | */ 8 | export default class QuickActionModal extends SuggestModal { 9 | editor: Editor; 10 | plugin: TaskStatusPluginInterface; 11 | 12 | /** 13 | * 14 | * @param app Obsidian instance 15 | * @param plugin plugin instance 16 | * @param editor editor instance 17 | */ 18 | constructor(app: App, plugin: TaskStatusPluginInterface, editor: Editor) { 19 | super(app); 20 | this.plugin = plugin; 21 | this.editor = editor; 22 | } 23 | 24 | /** 25 | * filters the checkbox options; the results are used as suggestions 26 | * @param query the search string 27 | * @returns collection of options 28 | */ 29 | getSuggestions(query: string): CheckboxOption[] { 30 | return this.plugin.settings.checkboxOptions.filter((option) => 31 | option.title.toLowerCase().includes(query.toLowerCase()) 32 | ); 33 | } 34 | 35 | /** 36 | * renders each suggestion 37 | * @param option the checkbox option to display 38 | * @param el the suggestion HTML element 39 | */ 40 | renderSuggestion(option: CheckboxOption, el: HTMLElement) { 41 | el.setCssStyles({ 42 | display: 'flex', 43 | flexDirection: 'row', 44 | alignItems: 'center', 45 | textAlign: 'center', 46 | }); 47 | 48 | // set a bunch of attributes so that the preview will be targeted by various themes 49 | el.setAttribute('data-task', option.character); 50 | el.classList.add('task-list-item'); 51 | if (option.character !== ' ') { 52 | el.classList.add('is-checked'); 53 | } 54 | 55 | // show a preview of the checkbox 56 | const input = el.createEl('input', { 57 | attr: { 58 | 'type': 'checkbox', 59 | 'data-task': option.character, 60 | }, 61 | }); 62 | 63 | // set a bunch of attributes so that the preview will be targeted by various themes 64 | input.classList.add('task-list-item'); 65 | input.checked = option.character !== ' '; 66 | if (option.character !== ' ') { 67 | input.classList.add('is-checked'); 68 | } 69 | 70 | // show the name of the checkbox option 71 | const span = el.createEl("span", { text: option.title }); 72 | span.classList.add('cm-list-1') 73 | } 74 | 75 | /** 76 | * Handler for when the user chooses an option 77 | * @param option the option selected by the user 78 | * @param evt the triggering mouse or keyboard event 79 | */ 80 | onChooseSuggestion(option: CheckboxOption, evt: MouseEvent | KeyboardEvent) { 81 | new Notice(`Selected ${option.title}`); 82 | new SwapCheckboxStatus(this.editor).swap(option.character); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test-vault/Test Note.md: -------------------------------------------------------------------------------- 1 | sample text 2 | 3 | ## Bullets 4 | 5 | - [test](example.com) 6 | - bullet a 7 | - bullet b 8 | - bullet c 9 | 10 | ## Tasks 11 | 12 | - [ ] task 13 | - [ ] task 14 | - [ ] task 15 | 16 | ## Quote Blocks 17 | 18 | > Quote 19 | 20 | > Block Quote 21 | > more content 22 | 23 | ## Callouts 24 | 25 | > [!Example] My Example 26 | > - bullet point 27 | > - [ ] task 28 | > - [x] task 29 | 30 | > [!NOTE] Title 31 | > Contents 32 | 33 | > [!NOTE] varied contents 34 | > - list item using dash 35 | > * list item using asterix 36 | > - [ ] task using dash 37 | > * [ ] task using asterix 38 | 39 | ## Links 40 | 41 | - [link](https://example.com) 42 | - [example.com](https://example.com) 43 | - 44 | - [[Internal Link]] 45 | - [A](https://example.com) 46 | - [ ] task 47 | - [x] task 48 | - [b] task 49 | 50 | ## More Bullet Lists 51 | 52 | - task 53 | - task 2 54 | * task 55 | * task 3 56 | + task 57 | + task 4 58 | - + test 59 | - [x] done 60 | - [ ] not done 61 | - [ ] not done 62 | - [/] partially done 63 | * [x] done 64 | * [ ] not done 65 | * [ ] not done 66 | * [/] partially done 67 | + [x] done 68 | + [ ] not done 69 | + [ ] not done 70 | + [/] partially done 71 | 72 | > [!NOTE] Title 73 | > - [x] test 74 | 75 | + test 76 | - [<] testing 77 | - testing 78 | * testing 79 | 80 | ## Paragraph 81 | 82 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nec imperdiet urna, vel rhoncus ante. Nullam euismod orci a dictum congue. Nam luctus ipsum sed dui hendrerit, in facilisis eros tincidunt. Integer ante lectus, fermentum a rhoncus a, pretium id libero. Etiam finibus nibh pulvinar nunc iaculis aliquet. Aenean orci nisi, sagittis in vehicula ut, vestibulum vel lacus. Mauris est urna, tincidunt at sapien vitae, facilisis tempor lacus. Sed dignissim lorem sit amet semper vehicula. Sed lacinia ante ut pharetra molestie. Donec pharetra risus dictum libero vehicula, eget volutpat risus tincidunt. Etiam a massa at urna lacinia lobortis et eget enim. In eget hendrerit est, gravida gravida magna. Pellentesque lacinia euismod risus, congue pretium neque euismod quis. 83 | 84 | ## Code Blocks 85 | 86 | ``` 87 | code block 88 | ``` 89 | 90 | ```javascript 91 | // Typed code block 92 | ``` 93 | 94 | ## Unordered List 95 | 96 | - First list item 97 | - secondary indent 98 | - tertiary indent 99 | - Second list item 100 | - Third list item 101 | 102 | ## Ordered List 103 | 104 | 1. First list item 105 | 2. Second list item 106 | 3. Third list item 107 | 10. Tenth list item 108 | 100. One Hundredth list item 109 | 110 | ## Obsidian Comments 111 | 112 | %% single line comment %% 113 | 114 | %% 115 | multi-line comment 116 | %% 117 | 118 | %% SIDEBAR 119 | testing: [[Test Note]] 120 | %% 121 | # Heading 1 122 | ## Heading 2 123 | ### Heading 3 124 | #### Heading 4 125 | ##### Heading 5 126 | ###### Heading 6 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsidian Task Status 2 | 3 | Quickly change any task status in your Obsidian notes. The searchable modal enables a more dynamic and indiscriminate workflow when you need to change your checkbox markers. 4 | 5 | ![A demonstration of the plugin](assets/demo.gif) 6 | 7 | ## Instructions 8 | 9 | Select one or more lines of text using cursors or a selection range. Then open the command palette and select the 10 | `Task Status: change task status` command to display a semantically searchable quick menu to navigate 11 | and apply one of several custom task status markers. Applying a task status marker will both swap 12 | existing task markers and transform non-task content into tasks. The custom statues are configurable 13 | and customizable through the plugin settings. If you'd like to set a hotkey, you might use `⌘ + Shift + L` on a 14 | mac, or `CTRL + Shift + L` on Windows. I find this pairs well with the hotkey for "Toggle Checkbox Status". 15 | 16 | Please note, that this plugin assumes that you have either installed an Obsidian theme which includes 17 | custom task status styling (such as Things or Minimal), or that you've created and applied your own 18 | CSS snippet to style tasks. 19 | 20 | - [Setup AnuPpuccin Theme](./docs/Setup%20AnuPpuccin%20Theme.md) 21 | - [Setup ITS Theme](./docs/Setup%20ITS%20Theme.md) 22 | - [Setup Minimal Theme](./docs/Setup%20Minimal%20Theme.md) 23 | 24 | ## Why this plugin? 25 | 26 | - keyboard driven 27 | - the design strives to keep your hands on the keyboard 28 | - your hands shouldn't need to jump between keys and mouse/trackball/trackpad 29 | - semantically searchable quick menu 30 | - with the fuzzy finder, you won't have to remember which text character maps to which task status 31 | - facilitates quickly and indiscriminately jumping between any checkbox status 32 | - the presentation of the options enable you to perform a quick linear scan through the options list 33 | - minimal configuration 34 | - pre-configured with checkbox style markers commonly used in Obsidian themes 35 | - the settings menu allows you to configure your own status markers. 36 | - *(your obsidian theme or custom CSS will need to provide the styling)* 37 | - the goal is to complement other task related plugins 38 | 39 | ## Overview of Behavior 40 | 41 | - works with various types of text selection: 42 | - single cursor placement 43 | - a selection range covering one or more lines 44 | - multiple cursors 45 | - multiple selection ranges 46 | - general transformation behavior: 47 | - headings are ignored and will not be transformed 48 | - horizontal rules (thematic breaks) are ignored 49 | - indentation is preserved 50 | - block quotes are preserved 51 | - content within a quote block or nested quote block can be transformed 52 | - supports selecting text within an obsidian callout 53 | - blank lines... 54 | - a cursor selection on a blank line will transform it into a task 55 | - blank lines within a selection range are ignored 56 | 57 | ## Known Limitations 58 | 59 | - **Headings** 60 | - Plugin currently supports ATX Headings (with `#` prefix) 61 | - Setext headings are parsed as separate lines of text 62 | - **Code Blocks** 63 | - code blocks are not detected 64 | - any part of a code block that is selected will have the task markers appended to the front 65 | - For now, avoid including any part of a code block in your selection when applying task statuses 66 | - **Obsidian Comments** 67 | - If a comment is part of your selection range, those lines will also have the checkbox marker appended to the front of the line (as if they were just text content and not a comment). 68 | - For now, avoid selecting comments 69 | 70 | ## Contributing 71 | 72 | - please follow [conventional commit guidelines](https://www.conventionalcommits.org/) 73 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official email address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | 135 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | import { App, PluginSettingTab, Setting } from 'obsidian'; 2 | import { CheckboxOption, TaskStatusPluginInterface } from './types'; 3 | import { themes } from './themes'; 4 | import DEFAULT_SETTINGS from './default-settings'; 5 | 6 | /** 7 | * Swap two indexes in an array 8 | * @param arr 9 | * @param indexA 10 | * @param indexB 11 | * @returns 12 | */ 13 | function swap(arr: T[], indexA: number, indexB: number): void { 14 | if (indexB < 0 || indexB === arr.length) return; 15 | 16 | const temp = arr[indexA]; 17 | arr[indexA] = arr[indexB]; 18 | arr[indexB] = temp; 19 | } 20 | 21 | /** 22 | * move an array item to the top of the list 23 | * @param arr the list to modify 24 | * @param index the index of the item to move to the top 25 | */ 26 | function moveToTop(arr: T[], index: number): void { 27 | const item = arr.splice(index, 1); 28 | arr.unshift(item[0]); 29 | } 30 | 31 | export default class Settings extends PluginSettingTab { 32 | plugin: TaskStatusPluginInterface; 33 | 34 | /** 35 | * constructs the settings 36 | * @param app obsidian application instance 37 | * @param plugin plugin instance 38 | */ 39 | constructor(app: App, plugin: TaskStatusPluginInterface) { 40 | super(app, plugin); 41 | this.plugin = plugin; 42 | } 43 | 44 | /** 45 | * Renders the settings view 46 | */ 47 | display(): void { 48 | const { containerEl } = this; 49 | containerEl.empty(); 50 | this.displayTaskStatuses(); 51 | this.displayListActions(); 52 | this.displayImportOptions(); 53 | } 54 | 55 | /** 56 | * Render the custom task statuses editing section 57 | */ 58 | displayTaskStatuses(): void { 59 | // create a series of settings to edit the list of custom task statuses 60 | // the setting will have a name, two inputs (status name and marker), and buttons to move the 61 | // item or remove it 62 | this.plugin.settings.checkboxOptions.forEach((checkboxOption, index) => { 63 | new Setting(this.containerEl) 64 | .setName(`${(index + 1).toString().padStart(2, '0')}.`) 65 | .addText(async (text) => { 66 | text 67 | .setPlaceholder('name') 68 | .setValue(checkboxOption.title) 69 | .onChange(async (value) => { 70 | this.plugin.settings.checkboxOptions[index].title = value; 71 | this.plugin.saveSettings(); 72 | }); 73 | }) 74 | .addText(async (text) => { 75 | text 76 | .setPlaceholder('character') 77 | .setValue(checkboxOption.character) 78 | .onChange(async (value) => { 79 | this.plugin.settings.checkboxOptions[index].character = value; 80 | this.plugin.saveSettings(); 81 | }); 82 | }) 83 | .addExtraButton((button) => { 84 | button 85 | .setIcon('double-up-arrow-glyph') 86 | .setTooltip('Move to top') 87 | .onClick(() => { 88 | moveToTop(this.plugin.settings.checkboxOptions, index); 89 | this.plugin.saveSettings(); 90 | this.display(); 91 | }); 92 | }) 93 | .addExtraButton((button) => { 94 | button 95 | .setIcon('up-chevron-glyph') 96 | .setTooltip('Move up') 97 | .onClick(() => { 98 | swap(this.plugin.settings.checkboxOptions, index, index - 1); 99 | this.plugin.saveSettings(); 100 | this.display(); 101 | }); 102 | }) 103 | .addExtraButton((button) => { 104 | button 105 | .setIcon('down-chevron-glyph') 106 | .setTooltip('Move down') 107 | .onClick(() => { 108 | swap(this.plugin.settings.checkboxOptions, index, index + 1); 109 | this.plugin.saveSettings(); 110 | this.display(); 111 | }); 112 | }) 113 | .addExtraButton((button) => { 114 | button 115 | .setIcon('cross') 116 | .setTooltip('Delete') 117 | .onClick(() => { 118 | this.plugin.settings.checkboxOptions.splice(index, 1); 119 | this.plugin.saveSettings(); 120 | this.display(); 121 | }); 122 | }); 123 | }); 124 | } 125 | 126 | displayListActions() { 127 | new Setting(this.containerEl) 128 | .setName('Status List Actions') 129 | .addButton((button) => { 130 | button 131 | .setButtonText('Clear list') 132 | .setWarning() 133 | .onClick(() => { 134 | this.plugin.settings.checkboxOptions = []; 135 | this.plugin.saveSettings(); 136 | this.display(); 137 | }); 138 | }) 139 | .addButton((button) => { 140 | button 141 | .setButtonText('Reset to default') 142 | .onClick(() => { 143 | this.plugin.settings = structuredClone(DEFAULT_SETTINGS); 144 | this.plugin.saveSettings(); 145 | this.display(); 146 | }); 147 | }) 148 | .addButton((button) => { 149 | button 150 | .setButtonText('Add new status') 151 | .setCta() 152 | .onClick(() => { 153 | this.plugin.settings.checkboxOptions.push({ title: 'undefined', character: 'x' }); 154 | this.plugin.saveSettings(); 155 | this.display(); 156 | }); 157 | }); 158 | } 159 | 160 | displayImportOptions() { 161 | themes.forEach(({name, statuses}) => { 162 | new Setting(this.containerEl) 163 | .setName(name) 164 | .setDesc(`Add any missing custom checkbox statuses supported by the ${name}.`) 165 | .addButton((button) => { 166 | button.setButtonText(name).onClick(async () => { 167 | statuses.forEach((option: CheckboxOption) => { 168 | const found = this.plugin.settings.checkboxOptions.some((o) => o.character == option.character) 169 | if(!found) { 170 | this.plugin.settings.checkboxOptions.push({ ...option }); 171 | } 172 | }); 173 | this.plugin.saveSettings(); 174 | this.display(); 175 | }); 176 | }); 177 | }); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /dev-log/Test Scenarios.md: -------------------------------------------------------------------------------- 1 | # Test/Define Behavior on various content 2 | 3 | - This is the start of an attempt to enumerate the number of scenarios that might need to be handled and/or tested 4 | - There's probably opportunity to combine a lot of this, and focus on outliers and patterns instead. 5 | 6 | ## Other 7 | 8 | - transforming a single line 9 | - line is indented 10 | - line is not indented 11 | - line is a task 12 | - line is not a task 13 | - transforming a selection 14 | - containing indented lines 15 | 16 | ## Leaf Blocks 17 | 18 | - [ ] Paragraphs and lines of text 19 | - [ ] paragraphs separated by blank line 20 | - [ ] paragraph with double space line break 21 | - [ ] paragraph with HTML line break `
` 22 | - [ ] thematic breaks (horizontal rules) 23 | - [ ] ATX Headings 24 | - [ ] Setext Headings 25 | - [ ] Indented code blocks 26 | - [ ] Fenced code blocks 27 | - [ ] Including extended syntax: `mermaid`, `dataview`, etc. 28 | - [ ] Including syntax theme 29 | - [ ] Excluding syntax theme 30 | - [ ] with tildas or with backticks 31 | - [ ] HTML blocks 32 | - [ ] ``, ``, `
`, `` etc... 33 | - [ ] Links 34 | - [ ] wiki links `[[link]]` 35 | - [ ] link with label and url `[label](url)` 36 | - [ ] link with label, url, and title `[label](http://example.com "title")` 37 | - [ ] reference-style link `[label][ref]` 38 | - [ ] MD Link with only reference `` 39 | - [ ] Image Link - `![alt text](url-or-path)` 40 | - [ ] Paragraphs 41 | - [ ] Blank Lines 42 | 43 | ## Container Blocks 44 | 45 | - [ ] Block Quotes 46 | - [ ] lazy continuation lines (omitting `>`) 47 | - [ ] non-lazy continuation lines (with `>`) 48 | - [ ] lines including a space after the marker 49 | - [ ] lines exclusing a space after the marker 50 | - [ ] lazy lines that don't include the marker itself 51 | - [ ] Lists 52 | - [ ] lists included as part of a paragraph 53 | - [ ] lists included as part of a block (example: quote block) 54 | - [ ] list item with child list 55 | - [ ] List Items 56 | - [ ] ordered (`*`, `-`, `+`) 57 | - [ ] unordered 58 | - [ ] alternating / mixed 59 | - [ ] with indented paragraphs (spanning multiple lines) 60 | - [ ] with indendted code blocks 61 | 62 | ## Inline 63 | 64 | Note, for the most part all of these should just be incidentally handled already. There's little need to specifically test all scenarios here 65 | 66 | - [ ] code span (inline code) 67 | - [ ] emphasis / italics 68 | - [ ] strong emphasis / bold 69 | - [ ] inline links 70 | - [ ] inline markdown link 71 | - [ ] auto links 72 | - [ ] inline images 73 | - [ ] inline HTML 74 | - [ ] hard-line breaks 75 | - [ ] double spaces 76 | - [ ] `
` 77 | - [ ] soft-line breaks 78 | - [ ] strikethrough `~~text~~` 79 | - [ ] task lists `- [x]` 80 | - [ ] emoji `:emoji:` (not obsidian supported) 81 | - [ ] highlight `==text==` 82 | - [ ] subscript `~1~` (not obsidian supported) 83 | - [ ] superscript `^1^` (not obsidian supported) 84 | - [ ] inline HTML 85 | - [ ] subscript: `text` 86 | - [ ] superscript: `text` 87 | - [ ] hyperlinks: `label` 88 | - [ ] others: ``, ``, `` etc. 89 | 90 | ### Extended Syntax 91 | 92 | This list includes a mix of leaf, container, and inline syntax 93 | 94 | - [ ] Obsidian Links 95 | - [ ] internal links `[[link]]` (aka wikilinks) 96 | - [ ] internal links `[[path/link]]` 97 | - [ ] internal links `[[link|title]]` 98 | - [ ] internal links `[[path/link|title]]` 99 | - [ ] embedded files `![[path]]` 100 | - [ ] block reference `![[link#^id]]` 101 | - [ ] block anchor `^id` 102 | - [ ] Obsidian Callouts `> [!note] title` 103 | - [ ] Obsidian Comments 104 | - [ ] single line 105 | - [ ] inline 106 | - [ ] multi-line 107 | - [ ] (Obsidian) MathJax / LaTex 108 | - [ ] inline (wrapped with single dollar signs `$`) 109 | - [ ] block notation (wrapped with double dollar signs `$$`) 110 | - [ ] YAML Frontmatter 111 | - [ ] JSON Frontmatter 112 | - [ ] Definition Lists 113 | this is a line of text accompanied with a second line prefixed with a colon `:` marker. 114 | - [ ] Tables 115 | - [ ] with vertial bars on the outside 116 | - [ ] omitting outside vertical bars 117 | - [ ] aligned and unaligned columns 118 | - [ ] Footnotes 119 | - [ ] inline anchor `[^1]` 120 | - [ ] the reference at the bottom `[^1]: content` 121 | - [ ] MDX 122 | 123 | ### Content Continuation 124 | 125 | Some of these are captured above, but there's interesting ways to compose all the syntax situations. 126 | 127 | - paragraphs with soft line breaks 128 | - list items with paragraph continuation text 129 | - list items with code block continuation text 130 | - list items with quote block continuation text 131 | - consecutive blocks with blank lines in between 132 | - consecutive lists or blocks separated by thematic breaks (horizontal rules). 133 | - lazy continuation of blocks 134 | - non-lazy continuation of blocks 135 | - nested block quotes with various combinations of continuation texts 136 | 137 | A lot of continuation text lines may just need to not be transformed into task items; but I'm curious if there's situations where the indentation may need to be adjusted. 138 | 139 | ### List Considerations 140 | 141 | - list markers can have `1<=N<=4` spaces of width 142 | - continuations lines match that width in indentation 143 | - a list marker might start with a width of 4: `1. text` 144 | - then be changed to a width of 2 `- [ ] text` 145 | - the continuation content must also change. 146 | 147 | ```markdown 148 | 1. list item 149 | continuation paragraph 150 | ``` 151 | 152 | I believe you would have this: 153 | 154 | ```md 155 | - [ ] list item 156 | continuation paragraph 157 | ``` 158 | 159 | ```markdown 160 | 1. list item 161 | continuation paragraph 162 | 2. text 163 | 3. text 164 | 4. text 165 | 5. text 166 | 6. text 167 | 7. text 168 | 8. text 169 | 9. text 170 | 10. text 171 | 11. text 172 | text 173 | ``` 174 | 175 | ## References for the specifications and syntax 176 | 177 | - 178 | - 179 | - 180 | - 181 | - 182 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/open-vscode/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 3 | if you want to view the source, please visit the github repository of this plugin 4 | */ 5 | 6 | "use strict";var v=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var D=(d,t)=>{for(var s in t)v(d,s,{get:t[s],enumerable:!0})},U=(d,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of P(t))!E.call(d,i)&&i!==s&&v(d,i,{get:()=>t[i],enumerable:!(o=k(t,i))||o.enumerable});return d};var R=d=>U(v({},"__esModule",{value:!0}),d);var O={};D(O,{default:()=>b});module.exports=R(O);var p=require("obsidian");var l=require("obsidian"),a={ribbonIcon:!0,ribbonCommandUsesCode:!0,executeTemplate:'code "{{vaultpath}}" "{{vaultpath}}/{{filepath}}"',openFile:!0,workspacePath:"{{vaultpath}}",useUrlInsiders:!1},m=class extends l.PluginSettingTab{constructor(t,s){super(t,s),this.plugin=s}display(){let{containerEl:t}=this;t.empty(),t.createEl("h3",{text:"General settings"}),new l.Setting(t).setName("Display Ribbon Icon").setDesc("Toggle this OFF if you want to hide the Ribbon Icon.").addToggle(n=>n.setValue(this.plugin.settings.ribbonIcon).onChange(e=>{this.plugin.settings.ribbonIcon=e,this.plugin.saveSettings(),this.plugin.refreshIconRibbon()})),new l.Setting(t).setName("Ribbon opens via `code` command").setDesc("Toggle this OFF if you'd prefer that the Ribbon Icon opens VSCode via URL.").addToggle(n=>n.setValue(this.plugin.settings.ribbonCommandUsesCode).onChange(e=>{this.plugin.settings.ribbonCommandUsesCode=e,this.plugin.saveSettings()})),t.createEl("h3",{text:"Open via `code` CLI settings"}),new l.Setting(t).setName("Template for executing the `code` command").setDesc('You can use the following variables: `{{vaultpath}}` (absolute), `{{filepath}}` (relative), `{{folderpath}}` (relative), `{{line}}` and `{{ch}}`. Note that on MacOS, a full path to the VSCode executable is required (generally "/usr/local/bin/code"). Example: `/usr/local/bin/code "{{vaultpath}}" "{{vaultpath}}/{{filepath}}"`').addText(n=>n.setPlaceholder(a.executeTemplate).setValue(this.plugin.settings.executeTemplate||a.executeTemplate).onChange(e=>{e=e.trim(),e===""&&(e=a.executeTemplate),this.plugin.settings.executeTemplate=e,this.plugin.saveData(this.plugin.settings)})),t.createEl("h3",{text:"Open via `vscode://` URL settings"});let o=t.createEl("p").createEl("em",{text:`\u26A0\uFE0F This setting is not recommended for Windows users due to 7 | UX issues caused by security enhancements in VSCode on Windows. More information: `});o.appendChild(createEl("a",{text:"Open in VSCode Readme",href:"https://github.com/NomarCub/obsidian-open-vscode/blob/master/README.md#caveats-regarding-the-url-command-for-windows-users"})),o.appendText("."),new l.Setting(t).setName("Open current file").setDesc("Open the current file rather than the root of the vault.").addToggle(n=>n.setValue(this.plugin.settings.openFile||a.openFile).onChange(e=>{this.plugin.settings.openFile=e,this.plugin.saveData(this.plugin.settings)}));let i=new l.Setting(t).setName("Path to VSCode Workspace").setDesc('Defaults to the {{vaultpath}} template variable. You can set this to an absolute path to a ".code-workspace" file if you prefer to use a Multi Root workspace file: ').addText(n=>n.setPlaceholder(a.workspacePath).setValue(this.plugin.settings.workspacePath||a.workspacePath).onChange(e=>{e=e.trim(),e===""&&(e=a.workspacePath),this.plugin.settings.workspacePath=e,this.plugin.saveData(this.plugin.settings)})),r="https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces";i.descEl.appendChild(createEl("a",{href:r,text:r})).appendText("."),new l.Setting(t).setName("Open VSCode using a `vscode-insiders://` URL").addToggle(n=>n.setValue(this.plugin.settings.useUrlInsiders).onChange(e=>{this.plugin.settings.useUrlInsiders=e,this.plugin.saveSettings()}))}};var I=require("child_process"),h=class h extends p.Plugin{constructor(){super(...arguments);this.DEV=!1;this.refreshIconRibbon=()=>{var s;(s=this.ribbonIcon)==null||s.remove(),this.settings.ribbonIcon&&(this.ribbonIcon=this.addRibbonIcon(h.iconId,"VSCode",()=>{let o=this.settings.ribbonCommandUsesCode?"openVSCode":"openVSCodeUrl";this[o]()}))}}async onload(){var o;console.log("Loading "+this.manifest.name+" plugin"),(0,p.addIcon)(h.iconId,h.iconSvgContent),await this.loadSettings(),this.refreshIconRibbon(),this.addSettingTab(new m(this.app,this)),this.addCommand({id:"open-vscode",name:"Open as Visual Studio Code workspace",callback:this.openVSCode.bind(this)}),this.addCommand({id:"open-vscode-via-url",name:"Open as Visual Studio Code workspace using a vscode:// URL",callback:this.openVSCodeUrl.bind(this)});let s=this.app.plugins.getPlugin("hot-reload");this.DEV=(o=s==null?void 0:s.enabledPlugins.has(this.manifest.id))!=null?o:!1,this.DEV&&(this.addCommand({id:"open-vscode-reload",name:"Reload the plugin in dev",callback:this.reload.bind(this)}),this.addCommand({id:"open-vscode-reset-settings",name:"Reset plugins settings to default in dev",callback:this.resetSettings.bind(this)}))}openVSCode(){var f,C,S,w,V,x;if(!(this.app.vault.adapter instanceof p.FileSystemAdapter))return;let{executeTemplate:s}=this.settings,o=this.app.vault.adapter.getBasePath(),i=this.app.workspace.getActiveFile(),r=(f=i==null?void 0:i.path)!=null?f:"",n=(S=(C=i==null?void 0:i.parent)==null?void 0:C.path)!=null?S:"",e=(w=this.app.workspace.getActiveViewOfType(p.MarkdownView))==null?void 0:w.editor.getCursor(),c=((V=e==null?void 0:e.line)!=null?V:0)+1,u=((x=e==null?void 0:e.ch)!=null?x:0)+1,g=s.trim()===""?a.executeTemplate:s;g=g.replaceAll("{{vaultpath}}",o).replaceAll("{{filepath}}",r).replaceAll("{{folderpath}}",n).replaceAll("{{line}}",c.toString()).replaceAll("{{ch}}",u.toString()),this.DEV&&console.log("[openVSCode]",{command:g}),(0,I.exec)(g,T=>{T&&console.error(`[openVSCode] exec error: ${T.message}`)})}openVSCodeUrl(){var u;if(!(this.app.vault.adapter instanceof p.FileSystemAdapter))return;let{openFile:s,useUrlInsiders:o}=this.settings,i=this.app.vault.adapter.getBasePath(),r=this.app.workspace.getActiveFile(),n=(u=r==null?void 0:r.path)!=null?u:"";this.DEV&&console.log("[open-vscode]",{settings:this.settings,path:i,filePath:n});let c=`${o?"vscode-insiders://":"vscode://"}file/${i}`;if(s){c+=`/${n}`;let g=this.settings.workspacePath.replaceAll("{{vaultpath}}",i);window.open(`vscode://file/${g}`),setTimeout(()=>{this.DEV&&console.log("[openVSCode]",{url:c}),window.open(c)},200)}else this.DEV&&console.log("[openVSCode]",{url:c}),window.open(c)}async loadSettings(){this.settings=Object.assign({},a,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}async reload(){let s=this.manifest.id,o=this.app.plugins;await o.disablePlugin(s),await o.enablePlugin(s),console.log(`[${this.manifest.id}] reloaded`,this)}async resetSettings(){console.log(`[${this.manifest.id}]`,{old:this.settings,default:a}),this.settings=a,await this.saveData(this.settings)}};h.iconId="vscode-logo",h.iconSvgContent=` 8 | 10 | Visual Studio Code 11 | 15 | 16 | `;var b=h; 17 | 18 | /* nosourcemap */ -------------------------------------------------------------------------------- /test-vault/How Themes Target the Checkbox with CSS.md: -------------------------------------------------------------------------------- 1 | ## AnuPpuccin 2 | 3 | ```css 4 | .anp-custom-checkboxes [data-task=">"] > input[type=checkbox]:checked, 5 | .anp-custom-checkboxes [data-task=">"] > p > input[type=checkbox]:checked, .anp-custom-checkboxes [data-task=">"][type=checkbox]:checked { 6 | --checkbox-color: transparent; 7 | --checkbox-color-hover: transparent; 8 | border-width: 0; 9 | } 10 | .anp-custom-checkboxes [data-task=">"] > input[type=checkbox]:checked:after, 11 | .anp-custom-checkboxes [data-task=">"] > p > input[type=checkbox]:checked:after, 12 | .anp-custom-checkboxes [data-task=">"][type=checkbox]:checked:after { 13 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3C!--! --%3E%3Cpath d='M307 34.8c-11.5 5.1-19 16.6-19 29.2v64H176C78.8 128 0 206.8 0 304C0 417.3 81.5 467.9 100.2 478.1c2.5 1.4 5.3 1.9 8.1 1.9c10.9 0 19.7-8.9 19.7-19.7c0-7.5-4.3-14.4-9.8-19.5C108.8 431.9 96 414.4 96 384c0-53 43-96 96-96h96v64c0 12.6 7.4 24.1 19 29.2s25 3 34.4-5.4l160-144c6.7-6.1 10.6-14.7 10.6-23.8s-3.8-17.7-10.6-23.8l-160-144c-9.4-8.5-22.9-10.6-34.4-5.4z'/%3E%3C/svg%3E"); 14 | -webkit-mask-size: contain; 15 | background-color: rgb(var(--ctp-sapphire)); 16 | left: 0px; 17 | } 18 | .anp-custom-checkboxes [data-task=">"] > input[type=checkbox]:checked:before, 19 | .anp-custom-checkboxes [data-task=">"] > p > input[type=checkbox]:checked:before, 20 | .anp-custom-checkboxes [data-task=">"][type=checkbox]:checked:before { 21 | color: rgb(var(--ctp-sapphire)); 22 | margin: 0 3px; 23 | position: absolute; 24 | left: calc(var(--checkbox-size) * 1); 25 | font-weight: bold; 26 | } 27 | .anp-custom-checkboxes-labels [data-task=">"] input[type=checkbox]:checked:before, 28 | .anp-custom-checkboxes-labels [data-task=">"][type=checkbox]:checked:before { 29 | content: "RSCH"; 30 | } 31 | ``` 32 | 33 | 34 | 35 | - `anp-custom-checkboxes-labels` goes into the `` styles 36 | - `.anp-custom-checkboxes [data-task=">"] > input[type=checkbox]:checked` has a specificity off **`0,4,1`** 37 | 38 | ## Minimal Theme 39 | 40 | ```css 41 | /* [>] Forwarded */ 42 | input[data-task=">"], 43 | li[data-task=">"] > input, 44 | li[data-task=">"] > p > input, { 45 | &:checked { 46 | color:var(--text-faint); 47 | transform:rotate(90deg); 48 | -webkit-mask-position:50% 100%; 49 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z' /%3E%3C/svg%3E"); 50 | } 51 | } 52 | ``` 53 | 54 | 55 | 56 | - `input[data-task=">"]` has a specificity of **`0,1,1`** 57 | - `li[data-task=">"] > input` has a specificity off **`0,1,2`** 58 | 59 | ## Things Theme 60 | 61 | ```css 62 | input[data-task='>']:checked, 63 | li[data-task='>'] > input:checked, 64 | li[data-task='>'] > p > input:checked { 65 | color: var(--text-faint); 66 | transform: rotate(90deg); 67 | -webkit-mask-position: 50% 100%; 68 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z' /%3E%3C/svg%3E"); 69 | } 70 | ``` 71 | 72 | 73 | 74 | - `input[data-task='>']:checked` has a specificity of **`0,2,1`** 75 | - `li[data-task='>'] > input:checked` has a specificity of **`0,2,2`** 76 | 77 | ## ITS Theme 78 | 79 | ```css 80 | body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task]:is([data-task=X], 81 | [data-task=">"], 82 | [data-task="<"]) :is(.task-list-label, p) > input:is([type=checkbox], [type=checkbox i]):checked, body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task]:is([data-task=X], 83 | [data-task=">"], 84 | [data-task="<"]) > input:is([type=checkbox], [type=checkbox i]):checked, body:not(.alt-chkbx-off) .task-list-item.is-checked:is([data-task=X], 85 | [data-task=">"], 86 | [data-task="<"]) :is(.task-list-label, p) > input:is([type=checkbox], [type=checkbox i]):checked, body:not(.alt-chkbx-off) .task-list-item.is-checked:is([data-task=X], 87 | [data-task=">"], 88 | [data-task="<"]) > input:is([type=checkbox], [type=checkbox i]):checked { 89 | background-color: transparent; 90 | font: var(--its); 91 | font-family: var(--its); 92 | font-size: inherit; 93 | font-weight: 10; 94 | text-align: center; 95 | border: 0; 96 | cursor: pointer; 97 | -webkit-mask-image: unset; 98 | } 99 | 100 | body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task]:is([data-task=X], 101 | [data-task=">"], 102 | [data-task="<"]) :is(.task-list-label, p) > input:is([type=checkbox], [type=checkbox i]):checked::after, body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task]:is([data-task=X], 103 | [data-task=">"], 104 | [data-task="<"]) > input:is([type=checkbox], [type=checkbox i]):checked::after, body:not(.alt-chkbx-off) .task-list-item.is-checked:is([data-task=X], 105 | [data-task=">"], 106 | [data-task="<"]) :is(.task-list-label, p) > input:is([type=checkbox], [type=checkbox i]):checked::after, body:not(.alt-chkbx-off) .task-list-item.is-checked:is([data-task=X], 107 | [data-task=">"], 108 | [data-task="<"]) > input:is([type=checkbox], [type=checkbox i]):checked::after { 109 | background-color: transparent; 110 | top: -4px; 111 | left: 0px; 112 | -webkit-mask-image: unset; 113 | } 114 | 115 | body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .task-list-item-checkbox[data-task=">"]::after, 116 | body:not(.alt-chkbx-off) .task-list-item.is-checked[data-task=">"] > input[type=checkbox]:checked::after, 117 | body:not(.alt-chkbx-off) .task-list-item.is-checked[data-task=">"] p > input[type=checkbox]:checked::after { 118 | content: "\ec03"; 119 | color: var(--text-normal); 120 | } 121 | ``` 122 | 123 | 124 | 125 | - `body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task]:is([data-task=X], [data-task=">"], [data-task="<"]) :is(.task-list-label, p) > input:is([type=checkbox], [type=checkbox i]):checked` has a specificity of `0,9,2` 126 | - `body:not(.alt-chkbx-off) .markdown-source-view.mod-cm6 .task-list-item-checkbox[data-task=">"]::after` has a specificity of **`0,5,2`** 127 | - `body:not(.alt-chkbx-off) .task-list-item.is-checked[data-task=">"] > input[type=checkbox]:checked::after` has a specificity of **`0,6,3`** 128 | 129 | ## Border 130 | 131 | ```css 132 | body:not(.disable-alternative-checkboxes) input[data-task="!"]:checked, 133 | body:not(.disable-alternative-checkboxes) li[data-task="!"]>input:checked, 134 | body:not(.disable-alternative-checkboxes) li[data-task="!"]>p>input:checked { 135 | --checkbox-color: var(--color-orange); 136 | --checkbox-color-hover: var(--color-orange); 137 | } 138 | ``` 139 | 140 | 141 | 142 | - `body:not(.disable-alternative-checkboxes) input[data-task="!"]:checked` has a specificity of **`0,3,2`** 143 | -------------------------------------------------------------------------------- /src/swap-checkbox-status.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorPosition, EditorSelection } from 'obsidian'; 2 | 3 | // TODO: ignore certain types of blocks -- like comment blocks and code blocks 4 | 5 | class SwapCheckboxStatus { 6 | editor: Editor; 7 | 8 | /** 9 | * When true, the class will output debug console messages 10 | */ 11 | DEBUG = true; 12 | 13 | // matches a checklist item: 14 | // start of a line; any amount of whitespace or `>` characters (for callouts); 15 | // then a dash or asterix for a bullet point, a single whitespace, then square bracket syntax 16 | // for the checkbox; followed by any remaining characters til the end of the line. 17 | // Capturing Groups: 18 | // $1 - everything before the checkbox 19 | // $2 - everything after the checkbox 20 | public static readonly taskRegex = /^([\s>]*[-*+]\s)\[[^\]]\](?!\()(.*)$/gm; 21 | 22 | /* 23 | (?!^#+) Ignore Heading (negative lookahead) 24 | (?!^\s*$) Ignore blank lines 25 | (?!^\s{0,3}([-_*]) *(?:\1 *){2,}$) Ignore horizontal rules and thematic breaks 26 | (?!^[\s>]* \[![\w-]+\]) Ignore first line of Obsidian Callouts 27 | ^ start of a line 28 | (?!(?:\s*>?)*\s*[-*+]\s+\[[^\]]\](?!\()) Ignore lines that are tasks (they may or may not be inside block quotes) 29 | ([>\s]*)? capture any amount of indentation and nested blockquote markers 30 | (?:(?:[-*+]|[0-9]+\.)\s)? don't capture any ordered or unordered list markings 31 | (.*) capture the rest of the text 32 | */ 33 | 34 | // matches a line that is not a checklist 35 | // Capturing Groups: 36 | // $1 - used internally to detect horizontal rules 37 | // $2 - indentation 38 | // $3 - bullet character 39 | // $4 - content 40 | public static readonly nonTaskRegex = 41 | /(?!^#+)(?!^\s*$)(?!^\s{0,3}([-_*])\s*(?:\1 *){2,}$)(?!^[\s>]* \[![\w-]+\])^(?!(?:\s*>?)*\s*[-*+]\s+\[[^\]]\](?!\())([>\s]*)?(?:(?:([-*+])|[0-9]+\.)\s)?(.*)/gm; 42 | 43 | // matches a blank line 44 | public static readonly blankLineRegex = /^(\s*)$/gm; 45 | 46 | constructor(editor: Editor) { 47 | this.editor = editor; 48 | } 49 | 50 | /** 51 | * Initiates the transformation of editor selected lines 52 | * @param marker the status marker string to put inside the checkbox 53 | */ 54 | swap(marker: string) { 55 | const selections = this.editor.listSelections(); 56 | selections.forEach((selection) => { 57 | this.transformSelectionOrLine(selection, marker); 58 | }); 59 | } 60 | 61 | /** 62 | * execute the appropriate transform depending on whether a single line or multiple are selected 63 | * @param selection an editor selection 64 | * @param marker the status marker to put inside the checkbox 65 | */ 66 | transformSelectionOrLine(selection: EditorSelection, marker: string) { 67 | this.log(`selection:\n${selection}`); 68 | if (selection.anchor.line === selection.head.line && selection.anchor.ch === selection.head.ch) { 69 | this.transformLine(selection.anchor, marker); 70 | } else { 71 | this.transformSelection(selection, marker); 72 | } 73 | } 74 | 75 | /** 76 | * transform a single line by it's line number 77 | * @param line the line number 78 | * @param target the status marker to put inside the checkbox 79 | */ 80 | transformLine(anchor: EditorPosition, target: string) { 81 | const line: number = anchor.line; 82 | this.log('start transformLine'); 83 | const original = this.editor.getLine(line); 84 | const replacement = this.getLineReplacement(original, target); 85 | 86 | this.log(`original:\n${original}`); 87 | this.log(`replacement:\n${replacement}`); 88 | 89 | this.editor.setLine(line, replacement); 90 | 91 | let ch = replacement.length; 92 | if (original.length === replacement.length) { 93 | ch = anchor.ch; 94 | } else { 95 | ch = anchor.ch + replacement.length - original.length; 96 | } 97 | this.editor.setSelection({ line, ch }); 98 | } 99 | 100 | getLineReplacement(original: string, target: string): string { 101 | SwapCheckboxStatus.taskRegex.lastIndex = 0; 102 | SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 103 | SwapCheckboxStatus.blankLineRegex.lastIndex = 0; 104 | switch (true) { 105 | // when the line is a task, replace the task marker 106 | case SwapCheckboxStatus.taskRegex.test(original): 107 | return this.transformTasks(original, target); 108 | // when the line is not a task, transform it into a task 109 | case SwapCheckboxStatus.nonTaskRegex.test(original): 110 | return this.transformNonTasks(original, target); 111 | // when the line is blank, start a blank task 112 | case SwapCheckboxStatus.blankLineRegex.test(original): 113 | return this.transformBlankLines(original, target); 114 | // otherwise don't apply any transformation 115 | default: 116 | this.log('detected other'); 117 | return original; 118 | } 119 | } 120 | 121 | transformTasks(original: string, target: string) { 122 | this.log('transforming task(s)'); 123 | SwapCheckboxStatus.taskRegex.lastIndex = 0; 124 | return original.replace(SwapCheckboxStatus.taskRegex, `$1[${target}]$2`); 125 | } 126 | 127 | transformNonTasks(original: string, target: string) { 128 | this.log('transforming non-task(s)'); 129 | 130 | SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 131 | const parts = SwapCheckboxStatus.nonTaskRegex.exec(original); 132 | if (parts === null) return original; 133 | 134 | // match 1 is a used inside the regex to detect horizontal rules 135 | // use matches 2 (indentation) and 3 (the content); 136 | const bullet = parts[3] || '-'; 137 | return original.replace(SwapCheckboxStatus.nonTaskRegex, `$2${bullet} [${target}] $4`); 138 | } 139 | 140 | transformBlankLines(original: string, target: string) { 141 | this.log('transforming blank line(s)'); 142 | SwapCheckboxStatus.blankLineRegex.lastIndex = 0; 143 | return original.replace(SwapCheckboxStatus.blankLineRegex, `$1- [${target}] `); 144 | } 145 | 146 | /** 147 | * transform a selection of text 148 | * @param selection 149 | * @param target 150 | */ 151 | transformSelection(selection: EditorSelection, target: string) { 152 | this.log('start transformSelection'); 153 | this.log('selection', selection); 154 | 155 | const { anchor, head } = selection; 156 | 157 | // determine the start and end 158 | const anchorIsStart = anchor.ch <= head.ch && anchor.line <= head.line; 159 | const cursorStart = anchorIsStart ? anchor : head; 160 | const cursorEnd = anchorIsStart ? head : anchor; 161 | 162 | // expand the selection to cover entire lines 163 | cursorStart.ch = 0; 164 | cursorEnd.ch = 0; 165 | cursorEnd.line += 1; 166 | 167 | // get the existing original text 168 | let replacement = this.editor.getRange(cursorStart, cursorEnd); 169 | 170 | // transform selected lines that contain tasks 171 | SwapCheckboxStatus.taskRegex.lastIndex = 0; 172 | if (SwapCheckboxStatus.taskRegex.test(replacement)) { 173 | replacement = this.transformTasks(replacement, target); 174 | } 175 | 176 | // all selected non-blank lines are transformed into tasks as well 177 | SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 178 | if (SwapCheckboxStatus.nonTaskRegex.test(replacement)) { 179 | replacement = this.transformNonTasks(replacement, target); 180 | } 181 | 182 | // NOTE: non-blank lines are left blank when muli-line selection is used 183 | 184 | this.log('replacement', replacement); 185 | this.editor.replaceRange(replacement, cursorStart, cursorEnd); 186 | } 187 | 188 | /** 189 | * conditional log output 190 | * @param args arguments to pass on to console.log 191 | */ 192 | log(...args: unknown[]) { 193 | this.DEBUG && console.log(...args); 194 | } 195 | } 196 | 197 | export default SwapCheckboxStatus; 198 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/obsidian-style-settings/styles.css: -------------------------------------------------------------------------------- 1 | .style-settings-heading { 2 | cursor: pointer; 3 | margin-bottom: 18px; 4 | padding-bottom: 6px; 5 | border-bottom: 1px solid var(--background-modifier-border); 6 | } 7 | 8 | .style-settings-heading[data-level="0"] { 9 | margin-bottom: 26px; 10 | } 11 | 12 | .style-settings-container { 13 | padding-bottom: 16px; 14 | } 15 | 16 | .style-settings-heading[data-level="0"] + .style-settings-container { 17 | padding-left: 34px; 18 | } 19 | 20 | .style-settings-heading.is-collapsed { 21 | margin-bottom: 0; 22 | } 23 | 24 | .style-settings-heading.is-collapsed + .style-settings-container { 25 | display: none; 26 | } 27 | 28 | .style-settings-collapse-indicator { 29 | color: var(--text-faint); 30 | display: inline-block; 31 | margin-right: 8px; 32 | position: relative; 33 | top: -1px; 34 | } 35 | 36 | .style-settings-heading[data-level="0"] 37 | + .style-settings-container 38 | .style-settings-collapse-indicator { 39 | margin-left: -17px; 40 | } 41 | 42 | .style-settings-collapse-indicator > svg { 43 | height: 9px; 44 | width: 9px; 45 | } 46 | 47 | .style-settings-heading.is-collapsed .style-settings-collapse-indicator > svg { 48 | transform: rotate(-90deg); 49 | } 50 | 51 | .style-settings-filter-result-count { 52 | color: var(--text-faint); 53 | line-height: var(--line-height-tight); 54 | margin-inline: var(--size-4-2); 55 | } 56 | 57 | .style-settings-error { 58 | font-size: 14px; 59 | border-radius: 6px; 60 | background: rgba(var(--background-modifier-error-rgb), 0.2); 61 | color: var(--text-error); 62 | padding: 10px; 63 | margin-bottom: 1rem; 64 | } 65 | 66 | .style-settings-error-name { 67 | font-weight: bold; 68 | margin-bottom: 5px; 69 | } 70 | 71 | .style-settings-error-desc { 72 | white-space: pre; 73 | } 74 | 75 | .style-settings-empty { 76 | font-size: 14px; 77 | background: var(--background-secondary); 78 | padding: 10px; 79 | } 80 | 81 | .style-settings-empty-name { 82 | font-weight: bold; 83 | margin-bottom: 5px; 84 | } 85 | 86 | .style-settings-import-input { 87 | width: 0.1px; 88 | height: 0.1px; 89 | opacity: 0; 90 | overflow: hidden; 91 | position: absolute; 92 | z-index: -1; 93 | } 94 | 95 | .style-settings-import-label { 96 | cursor: pointer; 97 | color: var(--text-accent); 98 | text-decoration: underline; 99 | } 100 | 101 | .style-settings-import-label:hover { 102 | color: var(--text-accent-hover); 103 | } 104 | 105 | .style-settings-export, 106 | .style-settings-import { 107 | display: inline-block; 108 | margin-right: 10px; 109 | } 110 | 111 | .style-settings-copy, 112 | .style-settings-download { 113 | position: relative; 114 | display: inline-block; 115 | margin-left: 10px; 116 | } 117 | 118 | .style-settings-copy:before { 119 | color: var(--interactive-success); 120 | content: "✓"; 121 | position: absolute; 122 | left: -18px; 123 | font-weight: bold; 124 | opacity: 0; 125 | transition: 150ms opacity ease-in-out; 126 | } 127 | 128 | .style-settings-copy.success:before { 129 | opacity: 1; 130 | } 131 | 132 | .modal-style-settings { 133 | height: 70vh; 134 | display: flex; 135 | flex-direction: column; 136 | } 137 | 138 | .modal-style-settings .modal-content { 139 | flex-grow: 1; 140 | margin: 0; 141 | display: flex; 142 | flex-direction: column; 143 | } 144 | 145 | .modal-style-settings textarea { 146 | display: block; 147 | width: 100%; 148 | height: 100%; 149 | font-family: var(--font-monospace) !important; 150 | font-size: 12px; 151 | white-space: pre; 152 | overflow-wrap: normal; 153 | overflow-x: scroll; 154 | margin-bottom: 5px; 155 | } 156 | 157 | .modal-style-settings .setting-item { 158 | align-items: flex-start; 159 | } 160 | 161 | .modal-style-settings button { 162 | margin: 10px 0 0; 163 | } 164 | 165 | .style-settings-import-error { 166 | display: none; 167 | color: var(--text-error); 168 | } 169 | 170 | .style-settings-import-error.active { 171 | display: block; 172 | } 173 | 174 | .view-content .style-settings-container .setting-item:not(.setting-item-heading) { 175 | flex-direction: column; 176 | align-items: flex-start; 177 | } 178 | 179 | .view-content .style-settings-container .setting-item:not(.setting-item-heading) .setting-item-control { 180 | padding-top: 0.5em; 181 | } 182 | 183 | .view-content .style-settings-container .setting-item:not(.setting-item-heading) .themed-color-wrapper { 184 | display: flex; 185 | } 186 | 187 | .style-settings-ref { 188 | position: absolute; 189 | width: 0 !important; 190 | height: 0 !important; 191 | pointer-events: none; 192 | } 193 | 194 | .style-settings-info-text .style-settings-markdown :first-child { 195 | margin-top: 0; 196 | } 197 | 198 | .style-settings-info-text .style-settings-markdown :last-child { 199 | margin-bottom: 0; 200 | }.style-settings-container .pcr-app { 201 | display: none; 202 | } 203 | 204 | .style-settings-container .pcr-app.visible { 205 | display: flex; 206 | } 207 | 208 | .pcr-app .pcr-swatches > button { 209 | padding: 0; 210 | } 211 | 212 | .pickr .pcr-button { 213 | margin-right: 0; 214 | } 215 | 216 | .themed-color-wrapper > div { 217 | background: var(--background-primary); 218 | padding: 10px; 219 | display: flex; 220 | align-items: center; 221 | border-radius: 4px; 222 | } 223 | 224 | .themed-color-wrapper > div + div { 225 | margin-top: 6px; 226 | } 227 | 228 | .themed-color-wrapper button { 229 | display: block; 230 | } 231 | 232 | .themed-color-wrapper .pickr-reset > button { 233 | margin: 0 0 0 10px; 234 | padding: 9px; 235 | line-height: 1; 236 | } 237 | 238 | .themed-color-wrapper .pickr-reset > button > svg { 239 | display: block; 240 | } 241 | /*! Pickr 1.8.4 MIT | https://github.com/Simonwep/pickr */ 242 | .pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:0.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;border-radius:.15em;background:url('data:image/svg+xml;utf8, ') no-repeat center;background-size:0;transition:all 0.3s}.pickr .pcr-button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:'';top:0;left:0;height:100%;width:100%;transition:background 0.3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow 0.3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(0,0,0,0.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:0.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity 0.3s, visibility 0s 0.3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 0.15em 1.5em 0 rgba(0,0,0,0.1),0 0 1em 0 rgba(0,0,0,0.03);left:0;top:0}.pcr-app.visible{transition:opacity 0.3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:0.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:0.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all 0.15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:'';position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,0.05);border-radius:0.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 0.2em}.pcr-app .pcr-interaction input{letter-spacing:0.07em;font-size:0.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all 0.15s;padding:0.45em 0.5em;margin-top:0.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(66,133,244,0.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all 0.2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(244,66,80,0.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme='nano']{width:14.25em;max-width:95vw}.pcr-app[data-theme='nano'] .pcr-swatches{margin-top:.6em;padding:0 .6em}.pcr-app[data-theme='nano'] .pcr-interaction{padding:0 .6em .6em .6em}.pcr-app[data-theme='nano'] .pcr-selection{display:grid;grid-gap:.6em;grid-template-columns:1fr 4fr;grid-template-rows:5fr auto auto;align-items:center;height:10.5em;width:100%;align-self:flex-start}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview{grid-area:2 / 1 / 4 / 1;height:100%;width:100%;display:flex;flex-direction:row;justify-content:center;margin-left:.6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-last-color{display:none}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color{position:relative;background:var(--pcr-color);width:2em;height:2em;border-radius:50em;overflow:hidden}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-preview .pcr-current-color::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette{grid-area:1 / 1 / 2 / 3;width:100%;height:100%;z-index:1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette{border-radius:.15em;width:100%;height:100%}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:'';top:0;left:0;width:100%;height:100%;background:url('data:image/svg+xml;utf8, ');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser{grid-area:2 / 2 / 2 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{grid-area:3 / 2 / 3 / 2}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity{height:0.5em;margin:0 .6em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-picker{top:50%;transform:translateY(-50%)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{flex-grow:1;border-radius:50em}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to right, red, #ff0, lime, cyan, blue, #f0f, red)}.pcr-app[data-theme='nano'] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to right, transparent, black),url('data:image/svg+xml;utf8, ');background-size:100%, 0.25em} 243 | 244 | -------------------------------------------------------------------------------- /test-vault/.obsidian/plugins/task-status/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 3 | if you want to view the source, please visit the github repository of this plugin 4 | */ 5 | 6 | var __defProp = Object.defineProperty; 7 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor; 8 | var __getOwnPropNames = Object.getOwnPropertyNames; 9 | var __hasOwnProp = Object.prototype.hasOwnProperty; 10 | var __export = (target, all) => { 11 | for (var name in all) 12 | __defProp(target, name, { get: all[name], enumerable: true }); 13 | }; 14 | var __copyProps = (to, from, except, desc) => { 15 | if (from && typeof from === "object" || typeof from === "function") { 16 | for (let key of __getOwnPropNames(from)) 17 | if (!__hasOwnProp.call(to, key) && key !== except) 18 | __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); 19 | } 20 | return to; 21 | }; 22 | var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 23 | 24 | // main.ts 25 | var main_exports = {}; 26 | __export(main_exports, { 27 | default: () => TaskStatusPlugin 28 | }); 29 | module.exports = __toCommonJS(main_exports); 30 | var import_obsidian4 = require("obsidian"); 31 | 32 | // src/settings.ts 33 | var import_obsidian = require("obsidian"); 34 | 35 | // src/themes/AnuPpuccinTheme.ts 36 | var AnuPpuccinTheme = { 37 | name: "AnuPpuccin Theme", 38 | statuses: [ 39 | { character: " ", title: "Unchecked" }, 40 | { character: "x", title: "Checked" }, 41 | { character: ">", title: "Rescheduled" }, 42 | { character: "<", title: "Scheduled" }, 43 | { character: "!", title: "Important" }, 44 | { character: "-", title: "Cancelled" }, 45 | { character: "/", title: "In Progress" }, 46 | { character: "?", title: "Question" }, 47 | { character: "*", title: "Star" }, 48 | { character: "n", title: "Note" }, 49 | { character: "l", title: "Location" }, 50 | { character: "i", title: "Information" }, 51 | { character: "I", title: "Idea" }, 52 | { character: "S", title: "Amount" }, 53 | { character: "p", title: "Pro" }, 54 | { character: "c", title: "Con" }, 55 | { character: "b", title: "Bookmark" }, 56 | { character: '"', title: "Quote" }, 57 | { character: "0", title: "Speech bubble 0" }, 58 | { character: "1", title: "Speech bubble 1" }, 59 | { character: "2", title: "Speech bubble 2" }, 60 | { character: "3", title: "Speech bubble 3" }, 61 | { character: "4", title: "Speech bubble 4" }, 62 | { character: "5", title: "Speech bubble 5" }, 63 | { character: "6", title: "Speech bubble 6" }, 64 | { character: "7", title: "Speech bubble 7" }, 65 | { character: "8", title: "Speech bubble 8" }, 66 | { character: "9", title: "Speech bubble 9" } 67 | ] 68 | }; 69 | 70 | // src/themes/AuraThemeTheme.ts 71 | var AuraTheme = { 72 | name: "Aura Theme", 73 | statuses: [ 74 | { character: " ", title: "incomplete" }, 75 | { character: "x", title: "complete / done" }, 76 | { character: "-", title: "cancelled" }, 77 | { character: ">", title: "deferred" }, 78 | { character: "/", title: "in progress, or half-done" }, 79 | { character: "!", title: "Important" }, 80 | { character: "?", title: "question" }, 81 | { character: "R", title: "review" }, 82 | { character: "+", title: "Inbox / task that should be processed later" }, 83 | { character: "b", title: "bookmark" }, 84 | { character: "B", title: "brainstorm" }, 85 | { character: "D", title: "deferred or scheduled" }, 86 | { character: "I", title: "Info" }, 87 | { character: "i", title: "idea" }, 88 | { character: "N", title: "note" }, 89 | { character: "Q", title: "quote" }, 90 | { character: "W", title: "win / success / reward" }, 91 | { character: "P", title: "pro" }, 92 | { character: "C", title: "con" } 93 | ] 94 | }; 95 | 96 | // src/themes/EbullientworksTheme.ts 97 | var EbullientworksTheme = { 98 | name: "Ebullientworks Theme", 99 | statuses: [ 100 | { character: " ", title: "Unchecked" }, 101 | { character: "x", title: "Checked" }, 102 | { character: "-", title: "Cancelled" }, 103 | { character: "/", title: "In Progress" }, 104 | { character: ">", title: "Deferred" }, 105 | { character: "!", title: "Important" }, 106 | { character: "?", title: "Question" }, 107 | { character: "r", title: "Review" } 108 | ] 109 | }; 110 | 111 | // src/themes/ITSTheme.ts 112 | var ITSTheme = { 113 | name: "ITS Theme with SlRvb Checkboxes", 114 | statuses: [ 115 | { character: " ", title: "Unchecked" }, 116 | { character: "x", title: "Regular" }, 117 | { character: "X", title: "Checked" }, 118 | { character: "-", title: "Dropped" }, 119 | { character: ">", title: "Forward" }, 120 | { character: "D", title: "Date" }, 121 | { character: "?", title: "Question" }, 122 | { character: "/", title: "Half Done" }, 123 | { character: "+", title: "Add" }, 124 | { character: "R", title: "Research" }, 125 | { character: "!", title: "Important" }, 126 | { character: "i", title: "Idea" }, 127 | { character: "B", title: "Brainstorm" }, 128 | { character: "P", title: "Pro" }, 129 | { character: "C", title: "Con" }, 130 | { character: "Q", title: "Quote" }, 131 | { character: "N", title: "Note" }, 132 | { character: "b", title: "Bookmark" }, 133 | { character: "I", title: "Information" }, 134 | { character: "p", title: "Paraphrase" }, 135 | { character: "L", title: "Location" }, 136 | { character: "E", title: "Example" }, 137 | { character: "A", title: "Answer" }, 138 | { character: "r", title: "Reward" }, 139 | { character: "c", title: "Choice" }, 140 | { character: "d", title: "Doing" }, 141 | { character: "T", title: "Time" }, 142 | { character: "@", title: "Character / Person" }, 143 | { character: "t", title: "Talk" }, 144 | { character: "O", title: "Outline / Plot" }, 145 | { character: "~", title: "Conflict" }, 146 | { character: "W", title: "World" }, 147 | { character: "f", title: "Clue / Find" }, 148 | { character: "F", title: "Foreshadow" }, 149 | { character: "H", title: "Favorite / Health" }, 150 | { character: "&", title: "Symbolism" }, 151 | { character: "s", title: "Secret" } 152 | ] 153 | }; 154 | 155 | // src/themes/LYTModeTheme.ts 156 | var LYTModeTheme = { 157 | name: "LYT Mode Theme", 158 | statuses: [ 159 | { character: " ", title: "Unchecked" }, 160 | { character: "x", title: "Checked" }, 161 | { character: ">", title: "Rescheduled" }, 162 | { character: "<", title: "Scheduled" }, 163 | { character: "!", title: "Important" }, 164 | { character: "-", title: "Cancelled" }, 165 | { character: "/", title: "In Progress" }, 166 | { character: "?", title: "Question" }, 167 | { character: "*", title: "Star" }, 168 | { character: "n", title: "Note" }, 169 | { character: "l", title: "Location" }, 170 | { character: "i", title: "Information" }, 171 | { character: "I", title: "Idea" }, 172 | { character: "S", title: "Amount" }, 173 | { character: "p", title: "Pro" }, 174 | { character: "c", title: "Con" }, 175 | { character: "b", title: "Bookmark" }, 176 | { character: "f", title: "Fire" }, 177 | { character: "k", title: "Key" }, 178 | { character: "w", title: "Win" }, 179 | { character: "u", title: "Up" }, 180 | { character: "d", title: "Down" } 181 | ] 182 | }; 183 | 184 | // src/themes/MinimalTheme.ts 185 | var MinimalTheme = { 186 | name: "Minimal Theme", 187 | statuses: [ 188 | { character: " ", title: "to-do" }, 189 | { character: "/", title: "incomplete" }, 190 | { character: "x", title: "done" }, 191 | { character: "-", title: "canceled" }, 192 | { character: ">", title: "forwarded" }, 193 | { character: "<", title: "scheduling" }, 194 | { character: "?", title: "question" }, 195 | { character: "!", title: "important" }, 196 | { character: "*", title: "star" }, 197 | { character: '"', title: "quote" }, 198 | { character: "l", title: "location" }, 199 | { character: "b", title: "bookmark" }, 200 | { character: "i", title: "information" }, 201 | { character: "S", title: "savings" }, 202 | { character: "I", title: "idea" }, 203 | { character: "p", title: "pros" }, 204 | { character: "c", title: "cons" }, 205 | { character: "f", title: "fire" }, 206 | { character: "k", title: "key" }, 207 | { character: "w", title: "win" }, 208 | { character: "u", title: "up" }, 209 | { character: "d", title: "down" } 210 | ] 211 | }; 212 | 213 | // src/themes/ThingsTheme.ts 214 | var ThingsTheme = { 215 | name: "Things Theme", 216 | statuses: [ 217 | // Basic 218 | { character: " ", title: "to-do" }, 219 | { character: "/", title: "incomplete" }, 220 | { character: "x", title: "done" }, 221 | { character: "-", title: "canceled" }, 222 | { character: ">", title: "forwarded" }, 223 | { character: "<", title: "scheduling" }, 224 | // Extras 225 | { character: "?", title: "question" }, 226 | { character: "!", title: "important" }, 227 | { character: "*", title: "star" }, 228 | { character: '"', title: "quote" }, 229 | { character: "l", title: "location" }, 230 | { character: "b", title: "bookmark" }, 231 | { character: "i", title: "information" }, 232 | { character: "S", title: "savings" }, 233 | { character: "I", title: "idea" }, 234 | { character: "p", title: "pros" }, 235 | { character: "c", title: "cons" }, 236 | { character: "f", title: "fire" }, 237 | { character: "k", title: "key" }, 238 | { character: "w", title: "win" }, 239 | { character: "u", title: "up" }, 240 | { character: "d", title: "down" } 241 | ] 242 | }; 243 | 244 | // src/themes/BorderTheme.ts 245 | var BorderTheme = { 246 | name: "Border Theme", 247 | statuses: [ 248 | // Basic 249 | { character: " ", title: "To Do" }, 250 | { character: "/", title: "In Progress" }, 251 | { character: "x", title: "Done" }, 252 | { character: "-", title: "Canceled" }, 253 | { character: ">", title: "Rescheduled" }, 254 | { character: "<", title: "Scheduled" }, 255 | // Extras 256 | { character: "!", title: "Important" }, 257 | { character: "?", title: "Question" }, 258 | { character: "i", title: "Information" }, 259 | { character: "S", title: "Amount" }, 260 | { character: "*", title: "Star" }, 261 | { character: "b", title: "Bookmark" }, 262 | { character: '"', title: "Quote" }, 263 | { character: "n", title: "Note" }, 264 | { character: "l", title: "Location" }, 265 | { character: "I", title: "Idea" }, 266 | { character: "p", title: "Pro" }, 267 | { character: "c", title: "Con" }, 268 | { character: "u", title: "up" }, 269 | { character: "d", title: "down" } 270 | ] 271 | }; 272 | 273 | // src/themes/index.ts 274 | var themes = [ 275 | AnuPpuccinTheme, 276 | AuraTheme, 277 | EbullientworksTheme, 278 | ITSTheme, 279 | LYTModeTheme, 280 | MinimalTheme, 281 | ThingsTheme, 282 | BorderTheme 283 | ]; 284 | 285 | // src/default-settings.ts 286 | var DEFAULT_SETTINGS = { 287 | // prettier-ignore 288 | checkboxOptions: [ 289 | { title: "to-do", character: " " }, 290 | { title: "incomplete", character: "/" }, 291 | { title: "done", character: "x" }, 292 | { title: "canceled", character: "-" }, 293 | { title: "forwarded", character: ">" }, 294 | { title: "scheduling", character: "<" }, 295 | { title: "question", character: "?" }, 296 | { title: "important", character: "!" }, 297 | { title: "star", character: "*" }, 298 | { title: "quote", character: '"' }, 299 | { title: "location", character: "l" }, 300 | { title: "bookmark", character: "b" }, 301 | { title: "information", character: "i" }, 302 | { title: "savings", character: "S" }, 303 | { title: "idea", character: "I" }, 304 | { title: "pros", character: "p" }, 305 | { title: "cons", character: "c" }, 306 | { title: "fire", character: "f" }, 307 | { title: "key", character: "k" }, 308 | { title: "win", character: "w" }, 309 | { title: "up", character: "u" }, 310 | { title: "down", character: "d" }, 311 | { title: "draft pull request", character: "D" }, 312 | { title: "open pull request", character: "P" }, 313 | { title: "merged pull request", character: "M" } 314 | ] 315 | }; 316 | var default_settings_default = DEFAULT_SETTINGS; 317 | 318 | // src/settings.ts 319 | function swap(arr, indexA, indexB) { 320 | if (indexB < 0 || indexB === arr.length) 321 | return; 322 | const temp = arr[indexA]; 323 | arr[indexA] = arr[indexB]; 324 | arr[indexB] = temp; 325 | } 326 | function moveToTop(arr, index) { 327 | const item = arr.splice(index, 1); 328 | arr.unshift(item[0]); 329 | } 330 | var Settings = class extends import_obsidian.PluginSettingTab { 331 | /** 332 | * constructs the settings 333 | * @param app obsidian application instance 334 | * @param plugin plugin instance 335 | */ 336 | constructor(app, plugin) { 337 | super(app, plugin); 338 | this.plugin = plugin; 339 | } 340 | /** 341 | * Renders the settings view 342 | */ 343 | display() { 344 | const { containerEl } = this; 345 | containerEl.empty(); 346 | this.displayTaskStatuses(); 347 | this.displayListActions(); 348 | this.displayImportOptions(); 349 | } 350 | /** 351 | * Render the custom task statuses editing section 352 | */ 353 | displayTaskStatuses() { 354 | this.plugin.settings.checkboxOptions.forEach((checkboxOption, index) => { 355 | new import_obsidian.Setting(this.containerEl).setName(`${(index + 1).toString().padStart(2, "0")}.`).addText(async (text) => { 356 | text.setPlaceholder("name").setValue(checkboxOption.title).onChange(async (value) => { 357 | this.plugin.settings.checkboxOptions[index].title = value; 358 | this.plugin.saveSettings(); 359 | }); 360 | }).addText(async (text) => { 361 | text.setPlaceholder("character").setValue(checkboxOption.character).onChange(async (value) => { 362 | this.plugin.settings.checkboxOptions[index].character = value; 363 | this.plugin.saveSettings(); 364 | }); 365 | }).addExtraButton((button) => { 366 | button.setIcon("double-up-arrow-glyph").setTooltip("Move to top").onClick(() => { 367 | moveToTop(this.plugin.settings.checkboxOptions, index); 368 | this.plugin.saveSettings(); 369 | this.display(); 370 | }); 371 | }).addExtraButton((button) => { 372 | button.setIcon("up-chevron-glyph").setTooltip("Move up").onClick(() => { 373 | swap(this.plugin.settings.checkboxOptions, index, index - 1); 374 | this.plugin.saveSettings(); 375 | this.display(); 376 | }); 377 | }).addExtraButton((button) => { 378 | button.setIcon("down-chevron-glyph").setTooltip("Move down").onClick(() => { 379 | swap(this.plugin.settings.checkboxOptions, index, index + 1); 380 | this.plugin.saveSettings(); 381 | this.display(); 382 | }); 383 | }).addExtraButton((button) => { 384 | button.setIcon("cross").setTooltip("Delete").onClick(() => { 385 | this.plugin.settings.checkboxOptions.splice(index, 1); 386 | this.plugin.saveSettings(); 387 | this.display(); 388 | }); 389 | }); 390 | }); 391 | } 392 | displayListActions() { 393 | new import_obsidian.Setting(this.containerEl).setName("Status List Actions").addButton((button) => { 394 | button.setButtonText("Clear list").setWarning().onClick(() => { 395 | this.plugin.settings.checkboxOptions = []; 396 | this.plugin.saveSettings(); 397 | this.display(); 398 | }); 399 | }).addButton((button) => { 400 | button.setButtonText("Reset to default").onClick(() => { 401 | this.plugin.settings = structuredClone(default_settings_default); 402 | this.plugin.saveSettings(); 403 | this.display(); 404 | }); 405 | }).addButton((button) => { 406 | button.setButtonText("Add new status").setCta().onClick(() => { 407 | this.plugin.settings.checkboxOptions.push({ title: "undefined", character: "x" }); 408 | this.plugin.saveSettings(); 409 | this.display(); 410 | }); 411 | }); 412 | } 413 | displayImportOptions() { 414 | themes.forEach(({ name, statuses }) => { 415 | new import_obsidian.Setting(this.containerEl).setName(name).setDesc(`Add any missing custom checkbox statuses supported by the ${name}.`).addButton((button) => { 416 | button.setButtonText(name).onClick(async () => { 417 | statuses.forEach((option) => { 418 | const found = this.plugin.settings.checkboxOptions.some((o) => o.character == option.character); 419 | if (!found) { 420 | this.plugin.settings.checkboxOptions.push({ ...option }); 421 | } 422 | }); 423 | this.plugin.saveSettings(); 424 | this.display(); 425 | }); 426 | }); 427 | }); 428 | } 429 | }; 430 | 431 | // src/register-ribbon.ts 432 | var import_obsidian3 = require("obsidian"); 433 | 434 | // src/modals/quick-action-modal.ts 435 | var import_obsidian2 = require("obsidian"); 436 | 437 | // src/swap-checkbox-status.ts 438 | var _SwapCheckboxStatus = class _SwapCheckboxStatus { 439 | constructor(editor) { 440 | /** 441 | * When true, the class will output debug console messages 442 | */ 443 | this.DEBUG = true; 444 | this.editor = editor; 445 | } 446 | /** 447 | * Initiates the transformation of editor selected lines 448 | * @param marker the status marker string to put inside the checkbox 449 | */ 450 | swap(marker) { 451 | const selections = this.editor.listSelections(); 452 | selections.forEach((selection) => { 453 | this.transformSelectionOrLine(selection, marker); 454 | }); 455 | } 456 | /** 457 | * execute the appropriate transform depending on whether a single line or multiple are selected 458 | * @param selection an editor selection 459 | * @param marker the status marker to put inside the checkbox 460 | */ 461 | transformSelectionOrLine(selection, marker) { 462 | this.log(`selection: 463 | ${selection}`); 464 | if (selection.anchor.line === selection.head.line && selection.anchor.ch === selection.head.ch) { 465 | this.transformLine(selection.anchor, marker); 466 | } else { 467 | this.transformSelection(selection, marker); 468 | } 469 | } 470 | /** 471 | * transform a single line by it's line number 472 | * @param line the line number 473 | * @param target the status marker to put inside the checkbox 474 | */ 475 | transformLine(anchor, target) { 476 | const line = anchor.line; 477 | this.log("start transformLine"); 478 | const original = this.editor.getLine(line); 479 | const replacement = this.getLineReplacement(original, target); 480 | this.log(`original: 481 | ${original}`); 482 | this.log(`replacement: 483 | ${replacement}`); 484 | this.editor.setLine(line, replacement); 485 | let ch = replacement.length; 486 | if (original.length === replacement.length) { 487 | ch = anchor.ch; 488 | } else { 489 | ch = anchor.ch + replacement.length - original.length; 490 | } 491 | this.editor.setSelection({ line, ch }); 492 | } 493 | getLineReplacement(original, target) { 494 | _SwapCheckboxStatus.taskRegex.lastIndex = 0; 495 | _SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 496 | _SwapCheckboxStatus.blankLineRegex.lastIndex = 0; 497 | switch (true) { 498 | case _SwapCheckboxStatus.taskRegex.test(original): 499 | return this.transformTasks(original, target); 500 | case _SwapCheckboxStatus.nonTaskRegex.test(original): 501 | return this.transformNonTasks(original, target); 502 | case _SwapCheckboxStatus.blankLineRegex.test(original): 503 | return this.transformBlankLines(original, target); 504 | default: 505 | this.log("detected other"); 506 | return original; 507 | } 508 | } 509 | transformTasks(original, target) { 510 | this.log("transforming task(s)"); 511 | _SwapCheckboxStatus.taskRegex.lastIndex = 0; 512 | return original.replace(_SwapCheckboxStatus.taskRegex, `$1[${target}]$2`); 513 | } 514 | transformNonTasks(original, target) { 515 | this.log("transforming non-task(s)"); 516 | _SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 517 | const parts = _SwapCheckboxStatus.nonTaskRegex.exec(original); 518 | if (parts === null) 519 | return original; 520 | const bullet = parts[3] || "-"; 521 | return original.replace(_SwapCheckboxStatus.nonTaskRegex, `$2${bullet} [${target}] $4`); 522 | } 523 | transformBlankLines(original, target) { 524 | this.log("transforming blank line(s)"); 525 | _SwapCheckboxStatus.blankLineRegex.lastIndex = 0; 526 | return original.replace(_SwapCheckboxStatus.blankLineRegex, `$1- [${target}] `); 527 | } 528 | /** 529 | * transform a selection of text 530 | * @param selection 531 | * @param target 532 | */ 533 | transformSelection(selection, target) { 534 | this.log("start transformSelection"); 535 | this.log("selection", selection); 536 | const { anchor, head } = selection; 537 | const anchorIsStart = anchor.ch <= head.ch && anchor.line <= head.line; 538 | const cursorStart = anchorIsStart ? anchor : head; 539 | const cursorEnd = anchorIsStart ? head : anchor; 540 | cursorStart.ch = 0; 541 | cursorEnd.ch = 0; 542 | cursorEnd.line += 1; 543 | let replacement = this.editor.getRange(cursorStart, cursorEnd); 544 | _SwapCheckboxStatus.taskRegex.lastIndex = 0; 545 | if (_SwapCheckboxStatus.taskRegex.test(replacement)) { 546 | replacement = this.transformTasks(replacement, target); 547 | } 548 | _SwapCheckboxStatus.nonTaskRegex.lastIndex = 0; 549 | if (_SwapCheckboxStatus.nonTaskRegex.test(replacement)) { 550 | replacement = this.transformNonTasks(replacement, target); 551 | } 552 | this.log("replacement", replacement); 553 | this.editor.replaceRange(replacement, cursorStart, cursorEnd); 554 | } 555 | /** 556 | * conditional log output 557 | * @param args arguments to pass on to console.log 558 | */ 559 | log(...args) { 560 | this.DEBUG && console.log(...args); 561 | } 562 | }; 563 | // matches a checklist item: 564 | // start of a line; any amount of whitespace or `>` characters (for callouts); 565 | // then a dash or asterix for a bullet point, a single whitespace, then square bracket syntax 566 | // for the checkbox; followed by any remaining characters til the end of the line. 567 | // Capturing Groups: 568 | // $1 - everything before the checkbox 569 | // $2 - everything after the checkbox 570 | _SwapCheckboxStatus.taskRegex = /^([\s>]*[-*+]\s)\[[^\]]\](?!\()(.*)$/gm; 571 | /* 572 | (?!^#+) Ignore Heading (negative lookahead) 573 | (?!^\s*$) Ignore blank lines 574 | (?!^\s{0,3}([-_*]) *(?:\1 *){2,}$) Ignore horizontal rules and thematic breaks 575 | (?!^[\s>]* \[![\w-]+\]) Ignore first line of Obsidian Callouts 576 | ^ start of a line 577 | (?!(?:\s*>?)*\s*[-*+]\s+\[[^\]]\](?!\()) Ignore lines that are tasks (they may or may not be inside block quotes) 578 | ([>\s]*)? capture any amount of indentation and nested blockquote markers 579 | (?:(?:[-*+]|[0-9]+\.)\s)? don't capture any ordered or unordered list markings 580 | (.*) capture the rest of the text 581 | */ 582 | // matches a line that is not a checklist 583 | // Capturing Groups: 584 | // $1 - used internally to detect horizontal rules 585 | // $2 - indentation 586 | // $3 - bullet character 587 | // $4 - content 588 | _SwapCheckboxStatus.nonTaskRegex = /(?!^#+)(?!^\s*$)(?!^\s{0,3}([-_*])\s*(?:\1 *){2,}$)(?!^[\s>]* \[![\w-]+\])^(?!(?:\s*>?)*\s*[-*+]\s+\[[^\]]\](?!\())([>\s]*)?(?:(?:([-*+])|[0-9]+\.)\s)?(.*)/gm; 589 | // matches a blank line 590 | _SwapCheckboxStatus.blankLineRegex = /^(\s*)$/gm; 591 | var SwapCheckboxStatus = _SwapCheckboxStatus; 592 | var swap_checkbox_status_default = SwapCheckboxStatus; 593 | 594 | // src/modals/quick-action-modal.ts 595 | var QuickActionModal = class extends import_obsidian2.SuggestModal { 596 | /** 597 | * 598 | * @param app Obsidian instance 599 | * @param plugin plugin instance 600 | * @param editor editor instance 601 | */ 602 | constructor(app, plugin, editor) { 603 | super(app); 604 | this.plugin = plugin; 605 | this.editor = editor; 606 | } 607 | /** 608 | * filters the checkbox options; the results are used as suggestions 609 | * @param query the search string 610 | * @returns collection of options 611 | */ 612 | getSuggestions(query) { 613 | return this.plugin.settings.checkboxOptions.filter( 614 | (option) => option.title.toLowerCase().includes(query.toLowerCase()) 615 | ); 616 | } 617 | /** 618 | * renders each suggestion 619 | * @param option the checkbox option to display 620 | * @param el the suggestion HTML element 621 | */ 622 | renderSuggestion(option, el) { 623 | el.setCssStyles({ 624 | display: "flex", 625 | flexDirection: "row", 626 | alignItems: "center", 627 | textAlign: "center" 628 | }); 629 | el.setAttribute("data-task", option.character); 630 | el.classList.add("task-list-item"); 631 | if (option.character !== " ") { 632 | el.classList.add("is-checked"); 633 | } 634 | const input = el.createEl("input", { 635 | attr: { 636 | "type": "checkbox", 637 | "data-task": option.character 638 | } 639 | }); 640 | if (option.character !== " ") { 641 | input.classList.add("is-checked"); 642 | } 643 | input.classList.add("task-list-item"); 644 | input.checked = option.character !== " "; 645 | const span = el.createEl("span", { text: option.title }); 646 | span.classList.add("cm-list-1"); 647 | } 648 | /** 649 | * Handler for when the user chooses an option 650 | * @param option the option selected by the user 651 | * @param evt the triggering mouse or keyboard event 652 | */ 653 | onChooseSuggestion(option, evt) { 654 | new import_obsidian2.Notice(`Selected ${option.title}`); 655 | new swap_checkbox_status_default(this.editor).swap(option.character); 656 | } 657 | }; 658 | 659 | // src/register-ribbon.ts 660 | var register_ribbon_default = (plugin) => { 661 | (0, import_obsidian3.addIcon)( 662 | "search-check", 663 | ` 664 | 665 | 666 | 667 | 668 | 669 | ` 670 | ); 671 | plugin.addRibbonIcon( 672 | "search-check", 673 | "Change Checkbox Status", 674 | (_event) => { 675 | const activeView = plugin.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView); 676 | if (!activeView) 677 | return; 678 | const editor = activeView.editor; 679 | new QuickActionModal(plugin.app, plugin, editor).open(); 680 | } 681 | ); 682 | }; 683 | 684 | // src/commands/open-task-quick-menu.ts 685 | var changeTaskStatus = (plugin) => ({ 686 | id: "change-task-status", 687 | name: "change task status", 688 | // hotkeys: [{ modifiers: ["Mod", "Shift"], key: "l" }], 689 | editorCallback: (editor, view) => { 690 | new QuickActionModal(plugin.app, plugin, editor).open(); 691 | } 692 | }); 693 | var open_task_quick_menu_default = changeTaskStatus; 694 | 695 | // main.ts 696 | var TaskStatusPlugin = class extends import_obsidian4.Plugin { 697 | /** 698 | * Setup the plugin when it loads in obsidian 699 | */ 700 | async onload() { 701 | console.log("loading Obsidian Task Status"); 702 | await this.loadSettings(); 703 | register_ribbon_default(this); 704 | this.addCommand(open_task_quick_menu_default(this)); 705 | this.addSettingTab(new Settings(this.app, this)); 706 | } 707 | /** 708 | * Teardown the plugin when it gets unloaded 709 | */ 710 | onunload() { 711 | console.log("unloading Obsidian Task Status"); 712 | } 713 | /** 714 | * Trigger the rendering of the settings view 715 | */ 716 | async loadSettings() { 717 | this.settings = Object.assign({}, default_settings_default, await this.loadData()); 718 | } 719 | /** 720 | * persist/save the plugin settings 721 | */ 722 | async saveSettings() { 723 | await this.saveData(this.settings); 724 | } 725 | }; 726 | -------------------------------------------------------------------------------- /test-vault/.obsidian/themes/Things/theme.css: -------------------------------------------------------------------------------- 1 | /*─────────────────────────────────────────────────────── 2 | THINGS 3 | Version 2.1.20 4 | Created by @colineckert 5 | 6 | Readme: 7 | https://github.com/colineckert/obsidian-things 8 | 9 | Support my work: 10 | https://www.buymeacoffee.com/colineckert 11 | ────────────────────────────────────────────────────── */ 12 | 13 | /* ─────────────────────────────────────────────────── */ 14 | /* Main Theme Styling */ 15 | /* ─────────────────────────────────────────────────── */ 16 | 17 | @charset "UTF-8"; 18 | body { 19 | /* Colors */ 20 | --base-h: 212; /* Base hue */ 21 | --base-s: 15%; /* Base saturation */ 22 | --base-d: 13%; /* Base lightness Dark Mode - 0 is black */ 23 | --base-l: 97%; /* Base lightness Light Mode - 100 is white */ 24 | --accent-h: 215; /* Accent hue */ 25 | --accent-s: 75%; /* Accent saturation */ 26 | --accent-d: 70%; /* Accent lightness Dark Mode */ 27 | --accent-l: 60%; /* Accent lightness Light Mode */ 28 | 29 | --blue: #2e80f2; 30 | --pink: #ff82b2; 31 | --green: #3eb4bf; 32 | --yellow: #e5b567; 33 | --orange: #e87d3e; 34 | --red: #e83e3e; 35 | --purple: #9e86c8; 36 | 37 | --h1-color: var(--text-normal); 38 | --h2-color: var(--text-normal); 39 | --h3-color: var(--blue); 40 | --h4-color: var(--yellow); 41 | --h5-color: var(--red); 42 | --h6-color: var(--text-muted); 43 | 44 | --strong-color: var(--pink); 45 | --em-color: var(--pink); 46 | --quote-color: var(--green); 47 | 48 | --tag-background-color-l: #bde1d3; 49 | --tag-font-color-l: #1d694b; 50 | --tag-background-color-d: #1d694b; 51 | --tag-font-color-d: #ffffff; 52 | 53 | --highlight-background-color--normal: hsl(50deg 100% 50% / 15%) !important; 54 | --highlight-background-color-underline: hsl(50deg 100% 50% / 100%) !important; 55 | --highlight-background-color--active: hsl(50deg 100% 50% / 20%) !important; 56 | 57 | --progress-color-1: #ad5758; 58 | --progress-color-2: #b87f4c; 59 | --progress-color-3: #d2b874; 60 | --progress-color-4: #b0c07e; 61 | --progress-color-5: #768399; 62 | 63 | /* Font families */ 64 | --font-text-theme: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 65 | Inter, Ubuntu, sans-serif; 66 | --font-editor-theme: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 67 | Inter, Ubuntu, sans-serif; 68 | --font-monospace-theme: 'JetBrains Mono', 'Fira Code', Menlo, SFMono-Regular, 69 | Consolas, 'Roboto Mono', monospace; 70 | --font-interface-theme: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 71 | Inter, Ubuntu, sans-serif; 72 | --font-editor: var(--font-editor-override), var(--font-text-override), 73 | var(--font-editor-theme); 74 | 75 | --h1-size: 1.7rem; 76 | --h2-size: 1.5rem; 77 | --h3-size: 1.2rem; 78 | --h4-size: 1.1rem; 79 | --h5-size: 1rem; 80 | --h6-size: 0.9rem; 81 | 82 | /* Misc */ 83 | --checkbox-radius: 30%; 84 | --link-external-decoration: underline; 85 | --link-decoration: underline; 86 | 87 | /* -------- */ 88 | --radius-s: 4px; 89 | --radius-m: 8px; 90 | --radius-l: 10px; 91 | --radius-xl: 16px; 92 | 93 | --line-width: 40rem; 94 | --line-height: 1.5; 95 | --max-width: 90%; 96 | --max-col-width: 18em; 97 | --icon-muted: 0.5; 98 | --nested-padding: 1.1em; 99 | --folding-offset: 10px; 100 | 101 | --line-width-adaptive: var(--line-width); 102 | --line-width-wide: calc(var(--line-width) + 12.5%); 103 | } 104 | 105 | /* COLOR SCHEMES 106 | ────────────────────────────────────────────────────── */ 107 | 108 | .theme-light, 109 | body.theme-light.is-mobile { 110 | color-scheme: light; 111 | --highlight-mix-blend-mode: darken; 112 | --mono-rgb-0: 255, 255, 255; 113 | --mono-rgb-100: 0, 0, 0; 114 | --color-red-rgb: 228, 55, 75; 115 | --color-red: #e4374b; 116 | --color-green-rgb: 12, 181, 79; 117 | --color-green: #0cb54f; 118 | --color-orange: #d96c00; 119 | --color-yellow: #bd8e37; 120 | --color-cyan: #2db7b5; 121 | --color-blue: #086ddd; 122 | --color-purple: #876be0; 123 | --color-pink: #c32b74; 124 | --color-base-00: #ffffff; 125 | --color-base-05: #fcfcfc; 126 | --color-base-10: #f6f7f8; /* code blocks */ 127 | --color-base-20: #f6f7f8; 128 | --color-base-25: #f0f0f0; 129 | --color-base-30: #ebedf0; /* soften dividing lines */ 130 | --color-base-35: #d4d4d4; 131 | --color-base-40: #bdbdbd; 132 | --color-base-50: #ababab; 133 | --color-base-60: #707070; 134 | --color-base-70: #5a5a5a; 135 | --color-base-100: #222222; 136 | --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); 137 | --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); 138 | --color-accent-1: hsl( 139 | var(--accent-h), 140 | var(--accent-s), 141 | calc(var(--accent-l) + 2.5%) 142 | ); 143 | --color-accent-2: hsl( 144 | var(--accent-h), 145 | var(--accent-s), 146 | calc(var(--accent-l) + 5%) 147 | ); 148 | --background-modifier-hover: #e2e5e9; 149 | --background-secondary-alt: var(--color-base-05); 150 | --background-modifier-box-shadow: rgba(0, 0, 0, 0.1); 151 | --background-modifier-cover: rgba(220, 220, 220, 0.4); 152 | --text-highlight-bg-l: rgba(255, 208, 0, 0.4); 153 | --text-highlight-bg: var(--text-highlight-bg-l); 154 | --text-highlight-bg-active: rgba(255, 128, 0, 0.4); 155 | --input-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 156 | 0 2px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 1.5px 0 rgba(0, 0, 0, 0.03), 157 | 0 1px 2px 0 rgba(0, 0, 0, 0.04), 0 0 0 0 transparent; 158 | --input-shadow-hover: inset 0 0 0 1px rgba(0, 0, 0, 0.17), 159 | 0 2px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 1.5px 0 rgba(0, 0, 0, 0.03), 160 | 0 1px 2px 0 rgba(0, 0, 0, 0.04), 0 0 0 0 transparent; 161 | --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.028), 162 | 0px 3.4px 6.7px rgba(0, 0, 0, 0.042), 0px 15px 30px rgba(0, 0, 0, 0.07); 163 | --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 164 | 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), 0px 30px 90px rgba(0, 0, 0, 0.2); 165 | 166 | --tag-background: var(--tag-background-color-l); 167 | --tag-color: var(--tag-font-color-l); 168 | 169 | /* --text-normal: hsl(var(--base-h), var(--base-s), calc(var(--base-l) - 80%)); */ 170 | --text-muted: hsl( 171 | var(--base-h), 172 | calc(var(--base-s) - 5%), 173 | calc(var(--base-l) - 60%) 174 | ); 175 | --text-faint: hsl( 176 | var(--base-h), 177 | calc(var(--base-s) - 5%), 178 | calc(var(--base-l) - 30%) 179 | ); 180 | --text-formatted: hsl( 181 | var(--base-h), 182 | calc(var(--base-s) - 5%), 183 | calc(var(--base-l) - 35%) 184 | ); 185 | --text-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); 186 | --text-accent-hover: hsl( 187 | var(--accent-h), 188 | var(--accent-s), 189 | calc(var(--accent-l) - 10%) 190 | ); 191 | 192 | --atom-gray-1: #383a42; 193 | --atom-gray-2: #383a42; 194 | --atom-red: #e75545; 195 | --atom-green: #4ea24c; 196 | --atom-blue: #3d74f6; 197 | --atom-purple: #a625a4; 198 | --atom-aqua: #0084bc; 199 | --atom-yellow: #e35649; 200 | --atom-orange: #986800; 201 | } 202 | 203 | .theme-dark, 204 | body.theme-dark.is-mobile { 205 | color-scheme: dark; 206 | --highlight-mix-blend-mode: lighten; 207 | --mono-rgb-0: 0, 0, 0; 208 | --mono-rgb-100: 255, 255, 255; 209 | --color-red-rgb: 251, 70, 76; 210 | --color-red: #fb464c; 211 | --color-green-rgb: 68, 207, 110; 212 | --color-green: #44cf6e; 213 | --color-orange: #e9973f; 214 | --color-yellow: #e0de71; 215 | --color-cyan: #53dfdd; 216 | --color-blue: #027aff; 217 | --color-purple: #a882ff; 218 | --color-pink: #fa99cd; 219 | --color-base-00: #1c2127; /* main editor window */ 220 | --color-base-10: #282c34; /* code blocks */ 221 | --color-base-20: #181c20; /* top unselected unfocused nav */ 222 | --color-base-25: #2c313c; 223 | --color-base-30: #35393e; 224 | --color-base-35: #3f3f3f; 225 | --color-base-40: #555; 226 | --color-base-50: #666; /* unfocused text */ 227 | --color-base-60: #999; 228 | --color-base-70: #bababa; 229 | --color-base-100: #dadada; 230 | --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); 231 | --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); 232 | --color-accent-1: hsl( 233 | var(--accent-h), 234 | var(--accent-s), 235 | calc(var(--accent-l) - 3.8%) 236 | ); 237 | --color-accent-2: hsl( 238 | var(--accent-h), 239 | var(--accent-s), 240 | calc(var(--accent-l) + 3.8%) 241 | ); 242 | --titlebar-background-focused: var(--color-base-10); 243 | --background-modifier-form-field: var(--color-base-25); 244 | --background-secondary-alt: var(--color-base-30); 245 | --interactive-normal: var(--color-base-30); 246 | --interactive-hover: var(--color-base-35); 247 | --text-highlight-bg-d: rgba(255, 208, 0, 0.4); 248 | --text-highlight-bg: var(--text-highlight-bg-d); 249 | --text-highlight-bg-active: rgba(255, 128, 0, 0.4); 250 | --text-selection: hsla(var(--interactive-accent-hsl), 0.25); 251 | --input-shadow: inset 0 0.5px 0.5px 0.5px rgba(255, 255, 255, 0.09), 252 | 0 2px 4px 0 rgba(0, 0, 0, 0.15), 0 1px 1.5px 0 rgba(0, 0, 0, 0.1), 253 | 0 1px 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 0 transparent; 254 | --input-shadow-hover: inset 0 0.5px 1px 0.5px rgba(255, 255, 255, 0.16), 255 | 0 2px 3px 0 rgba(0, 0, 0, 0.3), 0 1px 1.5px 0 rgba(0, 0, 0, 0.2), 256 | 0 1px 2px 0 rgba(0, 0, 0, 0.4), 0 0 0 0 transparent; 257 | --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.121), 258 | 0px 3.4px 6.7px rgba(0, 0, 0, 0.179), 0px 15px 30px rgba(0, 0, 0, 0.3); 259 | --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 260 | 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), 0px 30px 90px rgba(0, 0, 0, 0.2); 261 | 262 | --tag-background: var(--tag-background-color-d); 263 | --tag-color: var(--tag-font-color-d); 264 | 265 | --text-muted: hsl(var(--base-h), var(--base-s), calc(var(--base-d) + 65%)); 266 | --text-faint: hsl(var(--base-h), var(--base-s), calc(var(--base-d) + 30%)); 267 | --text-formatted: hsl( 268 | var(--base-h), 269 | var(--base-s), 270 | calc(var(--base-d) + 50%) 271 | ); 272 | 273 | --text-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-d)); 274 | --text-accent-hover: hsl( 275 | var(--accent-h), 276 | var(--accent-s), 277 | calc(var(--accent-d) + 12%) 278 | ); 279 | 280 | --atom-gray-1: #5c6370; 281 | --atom-gray-2: #abb2bf; 282 | --atom-red: #e06c75; 283 | --atom-orange: #d19a66; 284 | --atom-green: #98c379; 285 | --atom-aqua: #56b6c2; 286 | --atom-purple: #c678dd; 287 | --atom-blue: #61afef; 288 | --atom-yellow: #e5c07b; 289 | } 290 | 291 | body.is-mobile.theme-dark.mobile-black-background { 292 | --color-base-00: black; 293 | } 294 | 295 | /* Make exported PDFs render correctly */ 296 | @media print { 297 | .theme-dark { 298 | --highlight-mix-blend-mode: darken; 299 | --color-base-30: #ebedf0; 300 | --h1-color: var(--color-base-00); 301 | } 302 | } 303 | 304 | /* H2 styling */ 305 | body.h2-underline h2, 306 | body.h2-underline .HyperMD-header.HyperMD-header-2.cm-line { 307 | border-bottom: 2px solid var(--background-modifier-border); 308 | width: 100%; 309 | padding-bottom: 2px; 310 | } 311 | 312 | /* Hashtags font */ 313 | .markdown-source-view.mod-cm6.is-live-preview .cm-hashtag.cm-meta, 314 | .markdown-source-view.mod-cm5 .cm-hashtag.cm-meta { 315 | font-family: var(--font-text-theme); 316 | } 317 | 318 | /* Blockquote */ 319 | body:not(.default-font-color) .markdown-preview-view blockquote, 320 | body:not(.default-font-color) span.cm-quote.cm-quote-1 { 321 | font-style: italic; 322 | color: var(--quote-color); 323 | } 324 | 325 | /* Bold font */ 326 | body:not(.default-font-color) strong, 327 | body:not(.default-font-color) span:not(.cm-highlight).cm-strong { 328 | color: var(--strong-color); 329 | } 330 | 331 | /* Italics */ 332 | body:not(.default-font-color) em, 333 | body:not(.default-font-color) span:not(.cm-highlight).cm-em { 334 | color: var(--em-color); 335 | } 336 | 337 | /* Styled highlights */ 338 | body:not(.default-font-color) mark strong, 339 | body:not(.default-font-color) mark em { 340 | color: var(--text-normal); 341 | } 342 | 343 | /* Fancy highlight */ 344 | body.fancy-highlight span.cm-highlight, 345 | body.fancy-highlight .markdown-preview-view mark, 346 | body.fancy-highlight span.search-result-file-matched-text { 347 | background-image: linear-gradient( 348 | 0deg, 349 | var(--highlight-background-color-underline) 0%, 350 | var(--highlight-background-color-underline) 2px, 351 | var(--highlight-background-color--active) 2px, 352 | var(--highlight-background-color--active) 100% 353 | ) !important; 354 | background-color: var(--highlight-text-color--active) !important; 355 | } 356 | 357 | /* Markdown formatting */ 358 | .cm-formatting-strong, 359 | .cm-formatting-em, 360 | .cm-formatting.cm-formatting-quote { 361 | color: var(--text-formatted) !important; 362 | font-weight: var(--normal-weight); 363 | letter-spacing: -0.02em; 364 | } 365 | 366 | /* Completed checkboxes */ 367 | .markdown-preview-view ul > li.task-list-item.is-checked, 368 | .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task='x'], 369 | .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task='X'], 370 | .markdown-source-view.mod-cm6 .HyperMD-task-line[data-task='M'] { 371 | text-decoration: none; 372 | color: var(--text-faint); 373 | } 374 | 375 | /* Image card */ 376 | img { 377 | border-radius: 4px; 378 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 379 | } 380 | 381 | .theme-dark .modal-container .suggestion-item.is-selected { 382 | background: var(--color-accent); 383 | } 384 | 385 | /* Focus active line */ 386 | body.active-line .cm-active:not(.HyperMD-header, .HyperMD-codeblock)::before, 387 | body.active-line .cm-active.HyperMD-quote::before { 388 | content: ''; 389 | height: 100%; 390 | position: absolute; 391 | left: -1.125em; 392 | border-left: 3px solid var(--color-accent); 393 | border-radius: 10px 0 0 10px; 394 | opacity: 0.85; 395 | } 396 | 397 | /* Code blocks horizontal scroll */ 398 | .markdown-reading-view .markdown-preview-view pre:not(.frontmatter) code { 399 | white-space: pre; 400 | } 401 | 402 | /* Fancy code blocks */ 403 | .cm-inline-code, 404 | .cm-s-obsidian .HyperMD-codeblock, 405 | .markdown-preview-view.markdown-preview-view :is(pre, code) { 406 | --codeblock-border: var(--color-base-30); 407 | --codeblock-roundness: var(--radius-s); 408 | --code-block-alt-bg: var(--color-base-30); 409 | --slight-code-roundish: var(--radius-xs); 410 | } 411 | 412 | body.fancy-code .cm-s-obsidian .HyperMD-codeblock { 413 | line-height: 1.4em; 414 | } 415 | 416 | body.fancy-code .HyperMD-codeblock-begin { 417 | counter-reset: codeblock-line-numbers; 418 | } 419 | 420 | body.fancy-code 421 | .HyperMD-codeblock:not( 422 | .HyperMD-codeblock-begin, 423 | .HyperMD-codeblock-end 424 | )::before { 425 | counter-increment: codeblock-line-numbers; 426 | content: counter(codeblock-line-numbers); 427 | font-size: 0.75em; 428 | line-height: 2; 429 | text-align: right; 430 | height: 100%; 431 | width: 1.7em; 432 | color: var(--text-muted); 433 | /* background-color: #1e2029; */ 434 | background-color: var(--code-background); 435 | position: absolute; 436 | left: 0; 437 | padding-right: 1.4em; 438 | } 439 | 440 | body.fancy-code 441 | .HyperMD-codeblock.cm-line:not( 442 | .HyperMD-codeblock-begin, 443 | .HyperMD-codeblock-end 444 | ) { 445 | padding-left: 2.8em; 446 | } 447 | 448 | body.fancy-code .cm-s-obsidian div.HyperMD-codeblock-begin-bg { 449 | background-color: var(--code-block-alt-bg); 450 | border: var(--codeblock-border); 451 | border-bottom: none; 452 | border-top-right-radius: var(--codeblock-roundness); 453 | border-top-left-radius: var(--codeblock-roundness); 454 | } 455 | 456 | body.fancy-code div.HyperMD-codeblock-bg:not(.HyperMD-codeblock-begin-bg) { 457 | border-right: var(--codeblock-border); 458 | border-left: var(--codeblock-border); 459 | } 460 | 461 | body.fancy-code .cm-line.HyperMD-codeblock .code-block-flair { 462 | font-size: calc(var(--code-size) * 0.9); 463 | color: var(--text-muted); 464 | padding: 0 1px; 465 | top: 0; 466 | } 467 | 468 | body.fancy-code .markdown-reading-view pre[class*='language-']::before { 469 | display: block; 470 | content: ' '; 471 | line-height: 1.5em; 472 | background-color: var(--code-block-alt-bg); 473 | border-top-right-radius: calc(var(--codeblock-roundness) * 0.8); 474 | border-top-left-radius: calc(var(--codeblock-roundness) * 0.8); 475 | } 476 | 477 | body.fancy-code pre[class*='language-']::after { 478 | content: attr(class); 479 | font-size: 0.9rem; 480 | text-shadow: none; 481 | color: var(--text-muted); 482 | position: absolute; 483 | top: 2px; 484 | right: 5px; 485 | } 486 | 487 | body.fancy-code .markdown-preview-view pre code { 488 | padding: var(--size-4-1) var(--size-4-2); 489 | } 490 | 491 | body.fancy-code .copy-code-button.copy-code-button.copy-code-button { 492 | background-color: var(--interactive-normal); 493 | top: unset; 494 | bottom: 0; 495 | padding: 0 var(--size-2-2); 496 | } 497 | 498 | body.fancy-code .markdown-preview-view.markdown-preview-view pre { 499 | padding: 0; 500 | margin-top: var(--size-4-2); 501 | border: var(--codeblock-border); 502 | border-radius: var(--codeblock-roundness); 503 | } 504 | 505 | body.fancy-code .markdown-reading-view .markdown-preview-view pre code { 506 | display: block; 507 | } 508 | 509 | body.fancy-code .markdown-preview-view pre code { 510 | padding: var(--size-4-1) var(--size-4-5); 511 | } 512 | 513 | /* ------------------- */ 514 | /* One Dark Syntax Coloring */ 515 | /* Source: https://github.com/AGMStudio/prism-theme-one-dark */ 516 | /* ------------------- */ 517 | .theme-light .token.comment .theme-light .cm-comment { 518 | color: #ababab; 519 | } 520 | .theme-dark .token.comment, 521 | .theme-dark .cm-comment, 522 | .token.prolog, 523 | .token.doctype, 524 | .token.cdata { 525 | color: var(--atom-gray-1); 526 | } 527 | .token.punctuation, 528 | .token.plain-text, 529 | .token.dom.variable, 530 | .cm-hmd-codeblock.cm-bracket { 531 | color: var(--atom-gray-2); 532 | } 533 | code[class*='language-'], 534 | .token.selector, 535 | .cm-tag, 536 | .token.dom.variable, 537 | .token.tag, 538 | .cm-def, 539 | .token.parameter, 540 | .cm-property, 541 | .cm-qualifier { 542 | color: var(--atom-red); 543 | } 544 | .token.class-name, 545 | .token.maybe-class-name, 546 | .token.property-access, 547 | .token.constant, 548 | .token.builtin, 549 | .cm-variable-2, 550 | .cm-type, 551 | .cm-atom, 552 | code .cm-tag { 553 | color: var(--atom-yellow); 554 | } 555 | .token.property, 556 | .token.boolean, 557 | .token.number, 558 | .token.symbol, 559 | .token.attr-name, 560 | .token.deleted, 561 | .cm-attribute, 562 | .cm-number, 563 | .cm-property.cm-string { 564 | color: var(--atom-orange); 565 | } 566 | .token.string, 567 | .token.char, 568 | .token.attr-value, 569 | .token.inserted, 570 | .cm-hmd-codeblock.cm-string, 571 | .cm-hmd-codeblock.cm-string-2 { 572 | color: var(--atom-green); 573 | } 574 | .token.operator, 575 | .cm-operator, 576 | .token.entity, 577 | .token.url, 578 | .language-css .token.string, 579 | .style .token.string { 580 | color: var(--atom-aqua); 581 | } 582 | .token.atrule, 583 | .token.keyword, 584 | .cm-keyword { 585 | color: var(--atom-purple); 586 | } 587 | .cm-variable, 588 | .token.function, 589 | .token.method, 590 | .token.macro.property { 591 | color: var(--atom-blue); 592 | } 593 | .token.regex, 594 | .token.important, 595 | .token.variable { 596 | color: var(--atom-purple); 597 | } 598 | .token.important, 599 | .token.bold { 600 | font-weight: bold; 601 | } 602 | .token.italic { 603 | font-style: italic; 604 | } 605 | .token.entity { 606 | cursor: help; 607 | } 608 | 609 | /* --------------------- */ 610 | /* Mobile toolbar button */ 611 | /* --------------------- */ 612 | 613 | body.is-mobile:not(.floating-button-off):not(.advanced-toolbar) 614 | .view-action:nth-last-of-type(2), 615 | body.is-mobile:not(.floating-button-off):not(.advanced-toolbar) 616 | .view-action:nth-last-of-type(2) { 617 | color: white; 618 | background-color: var(--blue); 619 | opacity: 1; 620 | top: calc(90vh - 110px); 621 | display: flex; 622 | padding: 5px; 623 | position: fixed; 624 | left: 86vw; 625 | transform: translate(-40%, 5%); 626 | justify-content: center; 627 | align-items: center; 628 | width: 53px; 629 | height: 53px; 630 | border-radius: 50% !important; 631 | box-shadow: 1.1px 0.3px 2.2px rgba(0, 0, 0, 0.02), 632 | 2.7px 0.7px 5.3px rgba(0, 0, 0, 0.028), 5px 1.3px 10px rgba(0, 0, 0, 0.035), 633 | 8.9px 2.2px 17.9px rgba(0, 0, 0, 0.042), 634 | 16.7px 4.2px 33.4px rgba(0, 0, 0, 0.05), 40px 10px 80px rgba(0, 0, 0, 0.07); 635 | } 636 | 637 | body.is-mobile:not(.floating-button-off).advanced-toolbar 638 | .view-action:nth-last-of-type(2), 639 | body.is-mobile:not(.floating-button-off).advanced-toolbar 640 | .view-action:nth-last-of-type(2) { 641 | color: white; 642 | background-color: var(--blue); 643 | opacity: 1; 644 | position: fixed; 645 | top: calc(100vh - 138px); 646 | display: flex; 647 | padding: 5px; 648 | left: 86vw; 649 | transform: translate(-40%, -115%); 650 | justify-content: center; 651 | align-items: center; 652 | width: 53px; 653 | height: 53px; 654 | border-radius: 50% !important; 655 | box-shadow: 1.1px 0.3px 2.2px rgba(0, 0, 0, 0.02), 656 | 2.7px 0.7px 5.3px rgba(0, 0, 0, 0.028), 5px 1.3px 10px rgba(0, 0, 0, 0.035), 657 | 8.9px 2.2px 17.9px rgba(0, 0, 0, 0.042), 658 | 16.7px 4.2px 33.4px rgba(0, 0, 0, 0.05), 40px 10px 80px rgba(0, 0, 0, 0.07); 659 | } 660 | 661 | /* ------------------- */ 662 | /* Checkbox styling & icons. Credit Minimal theme: https://minimal.guide/Block+types/Checklists#Checkbox+styling */ 663 | /* Support @kepano - https://www.buymeacoffee.com/kepano */ 664 | /* ------------------- */ 665 | 666 | input[data-task='!']:checked, 667 | input[data-task='*']:checked, 668 | input[data-task='-']:checked, 669 | input[data-task='<']:checked, 670 | input[data-task='>']:checked, 671 | input[data-task='I']:checked, 672 | input[data-task='b']:checked, 673 | input[data-task='c']:checked, 674 | input[data-task='d']:checked, 675 | input[data-task='f']:checked, 676 | input[data-task='k']:checked, 677 | input[data-task='l']:checked, 678 | input[data-task='p']:checked, 679 | input[data-task='u']:checked, 680 | input[data-task='w']:checked, 681 | input[data-task='P']:checked, /* Open PR */ 682 | input[data-task='M']:checked, /* Merged PR */ 683 | input[data-task='D']:checked, /* Draft PR */ 684 | li[data-task='!'] > input:checked, 685 | li[data-task='!'] > p > input:checked, 686 | li[data-task='*'] > input:checked, 687 | li[data-task='*'] > p > input:checked, 688 | li[data-task='-'] > input:checked, 689 | li[data-task='-'] > p > input:checked, 690 | li[data-task='<'] > input:checked, 691 | li[data-task='<'] > p > input:checked, 692 | li[data-task='>'] > input:checked, 693 | li[data-task='>'] > p > input:checked, 694 | li[data-task='I'] > input:checked, 695 | li[data-task='I'] > p > input:checked, 696 | li[data-task='b'] > input:checked, 697 | li[data-task='b'] > p > input:checked, 698 | li[data-task='c'] > input:checked, 699 | li[data-task='c'] > p > input:checked, 700 | li[data-task='d'] > input:checked, 701 | li[data-task='d'] > p > input:checked, 702 | li[data-task='f'] > input:checked, 703 | li[data-task='f'] > p > input:checked, 704 | li[data-task='k'] > input:checked, 705 | li[data-task='k'] > p > input:checked, 706 | li[data-task='l'] > input:checked, 707 | li[data-task='l'] > p > input:checked, 708 | li[data-task='p'] > input:checked, 709 | li[data-task='p'] > p > input:checked, 710 | li[data-task='u'] > input:checked, 711 | li[data-task='u'] > p > input:checked, 712 | li[data-task='w'] > input:checked, 713 | li[data-task='w'] > p > input:checked, 714 | li[data-task='P'] > input:checked, 715 | li[data-task='P'] > p > input:checked, 716 | li[data-task='M'] > input:checked, 717 | li[data-task='M'] > p > input:checked, 718 | li[data-task='D'] > input:checked, 719 | li[data-task='D'] > p > input:checked { 720 | --checkbox-marker-color: transparent; 721 | border: none; 722 | border-radius: 0; 723 | background-image: none; 724 | background-color: currentColor; 725 | -webkit-mask-size: var(--checkbox-icon); 726 | -webkit-mask-position: 50% 50%; 727 | } 728 | input[data-task='>']:checked, 729 | li[data-task='>'] > input:checked, 730 | li[data-task='>'] > p > input:checked { 731 | color: var(--text-faint); 732 | transform: rotate(90deg); 733 | -webkit-mask-position: 50% 100%; 734 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z' /%3E%3C/svg%3E"); 735 | } 736 | input[data-task='<']:checked, 737 | li[data-task='<'] > input:checked, 738 | li[data-task='<'] > p > input:checked { 739 | color: var(--text-faint); 740 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z' clip-rule='evenodd' /%3E%3C/svg%3E"); 741 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z' clip-rule='evenodd' /%3E%3C/svg%3E"); 742 | } 743 | input[data-task='?']:checked, 744 | li[data-task='?'] > input:checked, 745 | li[data-task='?'] > p > input:checked { 746 | --checkbox-marker-color: transparent; 747 | background-color: var(--color-yellow); 748 | border-color: var(--color-yellow); 749 | background-position: 50% 50%; 750 | background-size: 200% 90%; 751 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="white" fill-rule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215c0 1.344-.665 2.288-1.79 2.973c-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712c1.03-.632 1.397-1.135 1.397-2.028c0-.979-.758-1.698-1.926-1.698c-1.009 0-1.71.529-1.938 1.402c-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09c0-.618-.473-1.092-1.095-1.092c-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z"%2F%3E%3C%2Fsvg%3E'); 752 | } 753 | .theme-dark input[data-task='?']:checked, 754 | .theme-dark li[data-task='?'] > input:checked, 755 | .theme-dark li[data-task='?'] > p > input:checked { 756 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="black" fill-opacity="0.8" fill-rule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215c0 1.344-.665 2.288-1.79 2.973c-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712c1.03-.632 1.397-1.135 1.397-2.028c0-.979-.758-1.698-1.926-1.698c-1.009 0-1.71.529-1.938 1.402c-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09c0-.618-.473-1.092-1.095-1.092c-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z"%2F%3E%3C%2Fsvg%3E'); 757 | } 758 | input[data-task='/']:checked, 759 | li[data-task='/'] > input:checked, 760 | li[data-task='/'] > p > input:checked { 761 | background-image: none; 762 | background-color: transparent; 763 | position: relative; 764 | overflow: hidden; 765 | } 766 | input[data-task='/']:checked:after, 767 | li[data-task='/'] > input:checked:after, 768 | li[data-task='/'] > p > input:checked:after { 769 | top: 0; 770 | left: 0; 771 | content: ' '; 772 | display: block; 773 | position: absolute; 774 | background-color: var(--color-accent); 775 | width: calc(50% - 0.5px); 776 | height: 100%; 777 | -webkit-mask-image: none; 778 | } 779 | input[data-task='!']:checked, 780 | li[data-task='!'] > input:checked, 781 | li[data-task='!'] > p > input:checked { 782 | color: var(--color-orange); 783 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z' clip-rule='evenodd' /%3E%3C/svg%3E"); 784 | } 785 | input[data-task='"']:checked, 786 | input[data-task='“']:checked, 787 | li[data-task='"'] > input:checked, 788 | li[data-task='"'] > p > input:checked, 789 | li[data-task='“'] > input:checked, 790 | li[data-task='“'] > p > input:checked { 791 | --checkbox-marker-color: transparent; 792 | background-position: 50% 50%; 793 | background-color: var(--color-cyan); 794 | border-color: var(--color-cyan); 795 | background-size: 75%; 796 | background-repeat: no-repeat; 797 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="white" d="M6.5 10c-.223 0-.437.034-.65.065c.069-.232.14-.468.254-.68c.114-.308.292-.575.469-.844c.148-.291.409-.488.601-.737c.201-.242.475-.403.692-.604c.213-.21.492-.315.714-.463c.232-.133.434-.28.65-.35l.539-.222l.474-.197l-.485-1.938l-.597.144c-.191.048-.424.104-.689.171c-.271.05-.56.187-.882.312c-.318.142-.686.238-1.028.466c-.344.218-.741.4-1.091.692c-.339.301-.748.562-1.05.945c-.33.358-.656.734-.909 1.162c-.293.408-.492.856-.702 1.299c-.19.443-.343.896-.468 1.336c-.237.882-.343 1.72-.384 2.437c-.034.718-.014 1.315.028 1.747c.015.204.043.402.063.539l.025.168l.026-.006A4.5 4.5 0 1 0 6.5 10zm11 0c-.223 0-.437.034-.65.065c.069-.232.14-.468.254-.68c.114-.308.292-.575.469-.844c.148-.291.409-.488.601-.737c.201-.242.475-.403.692-.604c.213-.21.492-.315.714-.463c.232-.133.434-.28.65-.35l.539-.222l.474-.197l-.485-1.938l-.597.144c-.191.048-.424.104-.689.171c-.271.05-.56.187-.882.312c-.317.143-.686.238-1.028.467c-.344.218-.741.4-1.091.692c-.339.301-.748.562-1.05.944c-.33.358-.656.734-.909 1.162c-.293.408-.492.856-.702 1.299c-.19.443-.343.896-.468 1.336c-.237.882-.343 1.72-.384 2.437c-.034.718-.014 1.315.028 1.747c.015.204.043.402.063.539l.025.168l.026-.006A4.5 4.5 0 1 0 17.5 10z"%2F%3E%3C%2Fsvg%3E'); 798 | } 799 | .theme-dark input[data-task='"']:checked, 800 | .theme-dark input[data-task='“']:checked, 801 | .theme-dark li[data-task='"'] > input:checked, 802 | .theme-dark li[data-task='"'] > p > input:checked, 803 | .theme-dark li[data-task='“'] > input:checked, 804 | .theme-dark li[data-task='“'] > p > input:checked { 805 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="black" fill-opacity="0.7" d="M6.5 10c-.223 0-.437.034-.65.065c.069-.232.14-.468.254-.68c.114-.308.292-.575.469-.844c.148-.291.409-.488.601-.737c.201-.242.475-.403.692-.604c.213-.21.492-.315.714-.463c.232-.133.434-.28.65-.35l.539-.222l.474-.197l-.485-1.938l-.597.144c-.191.048-.424.104-.689.171c-.271.05-.56.187-.882.312c-.318.142-.686.238-1.028.466c-.344.218-.741.4-1.091.692c-.339.301-.748.562-1.05.945c-.33.358-.656.734-.909 1.162c-.293.408-.492.856-.702 1.299c-.19.443-.343.896-.468 1.336c-.237.882-.343 1.72-.384 2.437c-.034.718-.014 1.315.028 1.747c.015.204.043.402.063.539l.025.168l.026-.006A4.5 4.5 0 1 0 6.5 10zm11 0c-.223 0-.437.034-.65.065c.069-.232.14-.468.254-.68c.114-.308.292-.575.469-.844c.148-.291.409-.488.601-.737c.201-.242.475-.403.692-.604c.213-.21.492-.315.714-.463c.232-.133.434-.28.65-.35l.539-.222l.474-.197l-.485-1.938l-.597.144c-.191.048-.424.104-.689.171c-.271.05-.56.187-.882.312c-.317.143-.686.238-1.028.467c-.344.218-.741.4-1.091.692c-.339.301-.748.562-1.05.944c-.33.358-.656.734-.909 1.162c-.293.408-.492.856-.702 1.299c-.19.443-.343.896-.468 1.336c-.237.882-.343 1.72-.384 2.437c-.034.718-.014 1.315.028 1.747c.015.204.043.402.063.539l.025.168l.026-.006A4.5 4.5 0 1 0 17.5 10z"%2F%3E%3C%2Fsvg%3E'); 806 | } 807 | input[data-task='-']:checked, 808 | li[data-task='-'] > input:checked, 809 | li[data-task='-'] > p > input:checked { 810 | color: var(--text-faint); 811 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z' clip-rule='evenodd' /%3E%3C/svg%3E"); 812 | } 813 | body:not(.tasks) 814 | .markdown-preview-view 815 | ul 816 | li[data-task='-'].task-list-item.is-checked, 817 | body:not(.tasks) 818 | .markdown-source-view.mod-cm6 819 | .HyperMD-task-line[data-task]:is([data-task='-']), 820 | body:not(.tasks) li[data-task='-'].task-list-item.is-checked { 821 | color: var(--text-faint); 822 | text-decoration: line-through solid var(--text-faint) 1px; 823 | } 824 | input[data-task='*']:checked, 825 | li[data-task='*'] > input:checked, 826 | li[data-task='*'] > p > input:checked { 827 | color: var(--color-yellow); 828 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z' /%3E%3C/svg%3E"); 829 | } 830 | input[data-task='l']:checked, 831 | li[data-task='l'] > input:checked, 832 | li[data-task='l'] > p > input:checked { 833 | color: var(--color-red); 834 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z' clip-rule='evenodd' /%3E%3C/svg%3E"); 835 | } 836 | input[data-task='i']:checked, 837 | li[data-task='i'] > input:checked, 838 | li[data-task='i'] > p > input:checked { 839 | --checkbox-marker-color: transparent; 840 | background-color: var(--color-blue); 841 | border-color: var(--color-blue); 842 | background-position: 50%; 843 | background-size: 100%; 844 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"%3E%3Cpath fill="none" stroke="white" stroke-linecap="round" stroke-linejoin="round" stroke-width="40" d="M196 220h64v172"%2F%3E%3Cpath fill="none" stroke="white" stroke-linecap="round" stroke-miterlimit="10" stroke-width="40" d="M187 396h138"%2F%3E%3Cpath fill="white" d="M256 160a32 32 0 1 1 32-32a32 32 0 0 1-32 32Z"%2F%3E%3C%2Fsvg%3E'); 845 | } 846 | .theme-dark input[data-task='i']:checked, 847 | .theme-dark li[data-task='i'] > input:checked, 848 | .theme-dark li[data-task='i'] > p > input:checked { 849 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 512 512"%3E%3Cpath fill="none" stroke="black" stroke-opacity="0.8" stroke-linecap="round" stroke-linejoin="round" stroke-width="40" d="M196 220h64v172"%2F%3E%3Cpath fill="none" stroke="black" stroke-opacity="0.8" stroke-linecap="round" stroke-miterlimit="10" stroke-width="40" d="M187 396h138"%2F%3E%3Cpath fill="black" fill-opacity="0.8" d="M256 160a32 32 0 1 1 32-32a32 32 0 0 1-32 32Z"%2F%3E%3C%2Fsvg%3E'); 850 | } 851 | input[data-task='S']:checked, 852 | li[data-task='S'] > input:checked, 853 | li[data-task='S'] > p > input:checked { 854 | --checkbox-marker-color: transparent; 855 | border-color: var(--color-green); 856 | background-color: var(--color-green); 857 | background-size: 100%; 858 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 48 48"%3E%3Cpath fill="white" fill-rule="evenodd" d="M26 8a2 2 0 1 0-4 0v2a8 8 0 1 0 0 16v8a4.002 4.002 0 0 1-3.773-2.666a2 2 0 0 0-3.771 1.332A8.003 8.003 0 0 0 22 38v2a2 2 0 1 0 4 0v-2a8 8 0 1 0 0-16v-8a4.002 4.002 0 0 1 3.773 2.666a2 2 0 0 0 3.771-1.332A8.003 8.003 0 0 0 26 10V8Zm-4 6a4 4 0 0 0 0 8v-8Zm4 12v8a4 4 0 0 0 0-8Z" clip-rule="evenodd"%2F%3E%3C%2Fsvg%3E'); 859 | } 860 | .theme-dark input[data-task='S']:checked, 861 | .theme-dark li[data-task='S'] > input:checked, 862 | .theme-dark li[data-task='S'] > p > input:checked { 863 | background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 48 48"%3E%3Cpath fill-opacity="0.8" fill="black" fill-rule="evenodd" d="M26 8a2 2 0 1 0-4 0v2a8 8 0 1 0 0 16v8a4.002 4.002 0 0 1-3.773-2.666a2 2 0 0 0-3.771 1.332A8.003 8.003 0 0 0 22 38v2a2 2 0 1 0 4 0v-2a8 8 0 1 0 0-16v-8a4.002 4.002 0 0 1 3.773 2.666a2 2 0 0 0 3.771-1.332A8.003 8.003 0 0 0 26 10V8Zm-4 6a4 4 0 0 0 0 8v-8Zm4 12v8a4 4 0 0 0 0-8Z" clip-rule="evenodd"%2F%3E%3C%2Fsvg%3E'); 864 | } 865 | input[data-task='I']:checked, 866 | li[data-task='I'] > input:checked, 867 | li[data-task='I'] > p > input:checked { 868 | color: var(--color-yellow); 869 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M11 3a1 1 0 10-2 0v1a1 1 0 102 0V3zM15.657 5.757a1 1 0 00-1.414-1.414l-.707.707a1 1 0 001.414 1.414l.707-.707zM18 10a1 1 0 01-1 1h-1a1 1 0 110-2h1a1 1 0 011 1zM5.05 6.464A1 1 0 106.464 5.05l-.707-.707a1 1 0 00-1.414 1.414l.707.707zM5 10a1 1 0 01-1 1H3a1 1 0 110-2h1a1 1 0 011 1zM8 16v-1h4v1a2 2 0 11-4 0zM12 14c.015-.34.208-.646.477-.859a4 4 0 10-4.954 0c.27.213.462.519.476.859h4.002z' /%3E%3C/svg%3E"); 870 | } 871 | input[data-task='f']:checked, 872 | li[data-task='f'] > input:checked, 873 | li[data-task='f'] > p > input:checked { 874 | color: var(--color-red); 875 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M12.395 2.553a1 1 0 00-1.45-.385c-.345.23-.614.558-.822.88-.214.33-.403.713-.57 1.116-.334.804-.614 1.768-.84 2.734a31.365 31.365 0 00-.613 3.58 2.64 2.64 0 01-.945-1.067c-.328-.68-.398-1.534-.398-2.654A1 1 0 005.05 6.05 6.981 6.981 0 003 11a7 7 0 1011.95-4.95c-.592-.591-.98-.985-1.348-1.467-.363-.476-.724-1.063-1.207-2.03zM12.12 15.12A3 3 0 017 13s.879.5 2.5.5c0-1 .5-4 1.25-4.5.5 1 .786 1.293 1.371 1.879A2.99 2.99 0 0113 13a2.99 2.99 0 01-.879 2.121z' clip-rule='evenodd' /%3E%3C/svg%3E"); 876 | } 877 | input[data-task='k']:checked, 878 | li[data-task='k'] > input:checked, 879 | li[data-task='k'] > p > input:checked { 880 | color: var(--color-yellow); 881 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z' clip-rule='evenodd' /%3E%3C/svg%3E"); 882 | } 883 | input[data-task='u']:checked, 884 | li[data-task='u'] > input:checked, 885 | li[data-task='u'] > p > input:checked { 886 | color: var(--color-green); 887 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z' clip-rule='evenodd' /%3E%3C/svg%3E"); 888 | } 889 | input[data-task='d']:checked, 890 | li[data-task='d'] > input:checked, 891 | li[data-task='d'] > p > input:checked { 892 | color: var(--color-red); 893 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M12 13a1 1 0 100 2h5a1 1 0 001-1V9a1 1 0 10-2 0v2.586l-4.293-4.293a1 1 0 00-1.414 0L8 9.586 3.707 5.293a1 1 0 00-1.414 1.414l5 5a1 1 0 001.414 0L11 9.414 14.586 13H12z' clip-rule='evenodd' /%3E%3C/svg%3E"); 894 | } 895 | input[data-task='w']:checked, 896 | li[data-task='w'] > input:checked, 897 | li[data-task='w'] > p > input:checked { 898 | color: var(--color-purple); 899 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath fill-rule='evenodd' d='M6 3a1 1 0 011-1h.01a1 1 0 010 2H7a1 1 0 01-1-1zm2 3a1 1 0 00-2 0v1a2 2 0 00-2 2v1a2 2 0 00-2 2v.683a3.7 3.7 0 011.055.485 1.704 1.704 0 001.89 0 3.704 3.704 0 014.11 0 1.704 1.704 0 001.89 0 3.704 3.704 0 014.11 0 1.704 1.704 0 001.89 0A3.7 3.7 0 0118 12.683V12a2 2 0 00-2-2V9a2 2 0 00-2-2V6a1 1 0 10-2 0v1h-1V6a1 1 0 10-2 0v1H8V6zm10 8.868a3.704 3.704 0 01-4.055-.036 1.704 1.704 0 00-1.89 0 3.704 3.704 0 01-4.11 0 1.704 1.704 0 00-1.89 0A3.704 3.704 0 012 14.868V17a1 1 0 001 1h14a1 1 0 001-1v-2.132zM9 3a1 1 0 011-1h.01a1 1 0 110 2H10a1 1 0 01-1-1zm3 0a1 1 0 011-1h.01a1 1 0 110 2H13a1 1 0 01-1-1z' clip-rule='evenodd' /%3E%3C/svg%3E"); 900 | } 901 | input[data-task='p']:checked, 902 | li[data-task='p'] > input:checked, 903 | li[data-task='p'] > p > input:checked { 904 | color: var(--color-green); 905 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M2 10.5a1.5 1.5 0 113 0v6a1.5 1.5 0 01-3 0v-6zM6 10.333v5.43a2 2 0 001.106 1.79l.05.025A4 4 0 008.943 18h5.416a2 2 0 001.962-1.608l1.2-6A2 2 0 0015.56 8H12V4a2 2 0 00-2-2 1 1 0 00-1 1v.667a4 4 0 01-.8 2.4L6.8 7.933a4 4 0 00-.8 2.4z' /%3E%3C/svg%3E"); 906 | } 907 | input[data-task='c']:checked, 908 | li[data-task='c'] > input:checked, 909 | li[data-task='c'] > p > input:checked { 910 | color: var(--color-orange); 911 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M18 9.5a1.5 1.5 0 11-3 0v-6a1.5 1.5 0 013 0v6zM14 9.667v-5.43a2 2 0 00-1.105-1.79l-.05-.025A4 4 0 0011.055 2H5.64a2 2 0 00-1.962 1.608l-1.2 6A2 2 0 004.44 12H8v4a2 2 0 002 2 1 1 0 001-1v-.667a4 4 0 01.8-2.4l1.4-1.866a4 4 0 00.8-2.4z' /%3E%3C/svg%3E"); 912 | } 913 | input[data-task='b']:checked, 914 | li[data-task='b'] > input:checked, 915 | li[data-task='b'] > p > input:checked { 916 | color: var(--color-orange); 917 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' viewBox='0 0 20 20' fill='currentColor'%3E%3Cpath d='M5 4a2 2 0 012-2h6a2 2 0 012 2v14l-5-2.5L5 18V4z' /%3E%3C/svg%3E"); 918 | } 919 | input[data-task='P']:checked, 920 | li[data-task='P'] > input:checked, 921 | li[data-task='P'] > p > input:checked { 922 | color: var(--color-green); 923 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3E%3Cpath d='M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z'%3E%3C/path%3E%3C/svg%3E"); 924 | } 925 | input[data-task='M']:checked, 926 | li[data-task='M'] > input:checked, 927 | li[data-task='M'] > p > input:checked { 928 | color: var(--color-purple); 929 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3E%3Cpath d='M5.45 5.154A4.25 4.25 0 0 0 9.25 7.5h1.378a2.251 2.251 0 1 1 0 1.5H9.25A5.734 5.734 0 0 1 5 7.123v3.505a2.25 2.25 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.95-.218ZM4.25 13.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm8.5-4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5ZM5 3.25a.75.75 0 1 0 0 .005V3.25Z'%3E%3C/path%3E%3C/svg%3E"); 930 | } 931 | input[data-task='D']:checked, 932 | li[data-task='D'] > input:checked, 933 | li[data-task='D'] > p > input:checked { 934 | color: var(--color-base-50); 935 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'%3E%3Cpath d='M3.25 1A2.25 2.25 0 0 1 4 5.372v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.251 2.251 0 0 1 3.25 1Zm9.5 14a2.25 2.25 0 1 1 0-4.5 2.25 2.25 0 0 1 0 4.5ZM2.5 3.25a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0ZM3.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm9.5 0a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM14 7.5a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm0-4.25a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Z'%3E%3C/path%3E%3C/svg%3E"); 936 | } 937 | 938 | body:not(.tasks) li[data-task='>'].task-list-item.is-checked, 939 | body:not(.tasks) li[data-task='<'].task-list-item.is-checked, 940 | body:not(.tasks) li[data-task='b'].task-list-item.is-checked, 941 | body:not(.tasks) li[data-task='i'].task-list-item.is-checked, 942 | body:not(.tasks) li[data-task='*'].task-list-item.is-checked, 943 | body:not(.tasks) li[data-task='!'].task-list-item.is-checked, 944 | body:not(.tasks) li[data-task='S'].task-list-item.is-checked, 945 | body:not(.tasks) li[data-task='?'].task-list-item.is-checked, 946 | body:not(.tasks) li[data-task='/'].task-list-item.is-checked, 947 | body:not(.tasks) li[data-task='"'].task-list-item.is-checked, 948 | body:not(.tasks) li[data-task='l'].task-list-item.is-checked, 949 | body:not(.tasks) li[data-task='I'].task-list-item.is-checked, 950 | body:not(.tasks) li[data-task='p'].task-list-item.is-checked, 951 | body:not(.tasks) li[data-task='c'].task-list-item.is-checked, 952 | body:not(.tasks) li[data-task='f'].task-list-item.is-checked, 953 | body:not(.tasks) li[data-task='k'].task-list-item.is-checked, 954 | body:not(.tasks) li[data-task='w'].task-list-item.is-checked, 955 | body:not(.tasks) li[data-task='u'].task-list-item.is-checked, 956 | body:not(.tasks) li[data-task='d'].task-list-item.is-checked, 957 | body:not(.tasks) li[data-task='P'].task-list-item.is-checked, 958 | body:not(.tasks) li[data-task='D'].task-list-item.is-checked { 959 | color: var(--text-normal); 960 | } 961 | 962 | /* ------------------- */ 963 | /* Progress bars styling. Credit Minimal theme: https://minimal.guide/progress-bars */ 964 | /* Support @kepano - https://www.buymeacoffee.com/kepano */ 965 | /* ------------------- */ 966 | .progress-color { 967 | .markdown-rendered progress, 968 | .markdown-source-view.is-live-preview progress, 969 | .markdown-preview-view progress { 970 | &[value^='1']::-webkit-progress-value, 971 | &[value^='2']::-webkit-progress-value, 972 | &[value^='3']::-webkit-progress-value { 973 | background-color: var(--progress-color-1); 974 | } 975 | &[value^='4']::-webkit-progress-value, 976 | &[value^='5']::-webkit-progress-value { 977 | background-color: var(--progress-color-2); 978 | } 979 | &[value^='6']::-webkit-progress-value, 980 | &[value^='7']::-webkit-progress-value { 981 | background-color: var(--progress-color-3); 982 | } 983 | &[value^='8']::-webkit-progress-value, 984 | &[value^='9']::-webkit-progress-value { 985 | background-color: var(--progress-color-4); 986 | } 987 | &[value='1']::-webkit-progress-value, 988 | &[value='100']::-webkit-progress-value { 989 | background-color: var(--progress-color-5); 990 | } 991 | 992 | &[value='0']::-webkit-progress-value, 993 | &[value='2']::-webkit-progress-value, 994 | &[value='3']::-webkit-progress-value, 995 | &[value='4']::-webkit-progress-value, 996 | &[value='5']::-webkit-progress-value, 997 | &[value='6']::-webkit-progress-value, 998 | &[value='7']::-webkit-progress-value, 999 | &[value='8']::-webkit-progress-value, 1000 | &[value='9']::-webkit-progress-value { 1001 | background-color: var(--progress-color-1); 1002 | } 1003 | } 1004 | } 1005 | 1006 | /* ─────────────────────────────────────────────────── */ 1007 | /* Plugins */ 1008 | /* ─────────────────────────────────────────────────── */ 1009 | 1010 | /* --------------- */ 1011 | /* Todoist */ 1012 | /* --------------- */ 1013 | 1014 | .todoist-p1 > input[type='checkbox'] { 1015 | border: 1px solid #ff757f !important; 1016 | background-color: rgba(255, 117, 127, 0.25) !important; 1017 | } 1018 | .todoist-p1 > input[type='checkbox']:hover { 1019 | background-color: rgba(255, 117, 127, 0.5) !important; 1020 | } 1021 | .todoist-p2 > input[type='checkbox'] { 1022 | border: 1px solid #ffc777 !important; 1023 | background-color: rgba(255, 199, 119, 0.25) !important; 1024 | } 1025 | .todoist-p2 > input[type='checkbox']:hover { 1026 | background-color: rgba(255, 199, 119, 0.5) !important; 1027 | } 1028 | .todoist-p3 > input[type='checkbox'] { 1029 | border: 1px solid #65bcff !important; 1030 | background-color: rgba(101, 188, 255, 0.25) !important; 1031 | } 1032 | .todoist-p3 > input[type='checkbox']:hover { 1033 | background-color: rgba(101, 188, 255, 0.5) !important; 1034 | } 1035 | .todoist-p4 > input[type='checkbox'] { 1036 | border: 1px solid #b4c2f0 !important; 1037 | background-color: rgba(180, 194, 240, 0.25) !important; 1038 | } 1039 | .todoist-p4 > input[type='checkbox']:hover { 1040 | background-color: rgba(180, 194, 240, 0.5) !important; 1041 | } 1042 | .task-metadata { 1043 | font-size: var(--font-todoist-metadata-size); 1044 | color: #7a88cf; 1045 | margin-left: unset; 1046 | } 1047 | .task-metadata > * { 1048 | margin-right: 30px; 1049 | } 1050 | .task-date.task-overdue { 1051 | color: rgba(255, 152, 164, 0.75) !important; 1052 | } 1053 | .task-calendar-icon, 1054 | .task-project-icon, 1055 | .task-labels-icon { 1056 | vertical-align: middle; 1057 | height: 17px; 1058 | width: 17px; 1059 | } 1060 | .todoist-project .todoist-project { 1061 | margin-left: 20px; 1062 | } 1063 | .todoist-section { 1064 | margin-left: 20px; 1065 | } 1066 | .todoist-project .todoist-project-title { 1067 | font-weight: 700; 1068 | margin-block-end: 0px; 1069 | } 1070 | .todoist-section .todoist-section-title { 1071 | font-size: var(--font-todoist-title-size); 1072 | color: #7a88cf; 1073 | font-weight: 700; 1074 | margin-block-end: 0px; 1075 | } 1076 | .todoist-error { 1077 | border: 1px solid #ff98a4; 1078 | background-color: rgba(255, 152, 164, 0.05); 1079 | padding: 1em 1em; 1080 | margin: 1em 0px; 1081 | } 1082 | .todoist-error p { 1083 | margin: 0 0 1em 0; 1084 | font-weight: 600; 1085 | } 1086 | .todoist-error code { 1087 | background-color: unset !important; 1088 | padding: unset !important; 1089 | margin: unset !important; 1090 | } 1091 | .todoist-success { 1092 | border: 1px solid #c3e88d !important; 1093 | background-color: rgba(195, 232, 141, 0.05); 1094 | padding: 1em 1em !important; 1095 | margin: 1em 0px; 1096 | } 1097 | .todoist-success p { 1098 | margin: 0; 1099 | font-weight: 600; 1100 | } 1101 | /* .priority-container .priority-1 { 1102 | color: #ff98a4; 1103 | } 1104 | .priority-container .priority-2 { 1105 | color: #ffc777; 1106 | } 1107 | .priority-container .priority-3 { 1108 | color: #65bcff; 1109 | } 1110 | .priority-container .priority-4 { 1111 | color: #b4c2f0; 1112 | } */ 1113 | 1114 | /* --------------- */ 1115 | /* Checklist */ 1116 | /* --------------- */ 1117 | 1118 | .checklist-plugin-main .title { 1119 | font-size: var(--nav-item-size); 1120 | } 1121 | 1122 | .checklist-plugin-main .container input.search { 1123 | font-size: var(--font-ui-small); 1124 | } 1125 | 1126 | .checklist-plugin-main .group-header button.collapse, 1127 | .checklist-plugin-main button.toggle { 1128 | box-shadow: none; 1129 | cursor: pointer; 1130 | } 1131 | 1132 | .checklist-plugin-main .classic .content > p { 1133 | font-size: var(--font-ui-smaller); 1134 | } 1135 | 1136 | .checklist-plugin-main .toggle .checkbox { 1137 | border-radius: var(--checkbox-radius) !important; 1138 | } 1139 | 1140 | /* --------------- */ 1141 | /* Kanban */ 1142 | /* --------------- */ 1143 | 1144 | body:not(.no-kanban-styles) .kanban-plugin { 1145 | --dot-color: hsl(0 0% 40% / 10%); 1146 | --dot-spacing: 9px; 1147 | --dot-size: 1px; 1148 | } 1149 | body:not(.no-kanban-styles) .kanban-plugin__board { 1150 | background-image: radial-gradient( 1151 | circle, 1152 | var(--dot-color) var(--dot-size), 1153 | transparent var(--dot-size) 1154 | ); 1155 | border-top: var(--thin-border); 1156 | background-size: var(--dot-spacing) var(--dot-spacing); 1157 | background-attachment: local; 1158 | } 1159 | 1160 | body:not(.no-kanban-styles) .kanban-plugin__board > div { 1161 | margin: 0 auto; 1162 | } 1163 | body:not(.no-kanban-styles) .kanban-plugin__item-form { 1164 | border-top: none; 1165 | } 1166 | body:not(.no-kanban-styles) .kanban-plugin__lane-header-wrapper { 1167 | border-bottom: 0; 1168 | } 1169 | body:not(.no-kanban-styles) .kanban-plugin button { 1170 | box-shadow: none; 1171 | } 1172 | body:not(.no-kanban-styles) .kanban-plugin__item-button-wrapper > button:hover { 1173 | color: var(--text-normal); 1174 | background: var(--background-modifier-hover); 1175 | } 1176 | body:not(.no-kanban-styles) .kanban-plugin__item-button-wrapper { 1177 | border-top: none; 1178 | } 1179 | body:not(.no-kanban-styles) .kanban-plugin__lane { 1180 | border: 1px solid transparent; 1181 | } 1182 | body:not(.no-kanban-styles) .kanban-plugin__item-content-wrapper { 1183 | box-shadow: none; 1184 | } 1185 | 1186 | body:not(.no-kanban-styles):not(.is-mobile) 1187 | .kanban-plugin__grow-wrap 1188 | > textarea:focus { 1189 | box-shadow: none; 1190 | } 1191 | body:not(.no-kanban-styles) .kanban-plugin__item-input-actions button, 1192 | body:not(.no-kanban-styles) .kanban-plugin__lane-input-actions button { 1193 | font-size: var(--font-adaptive-small); 1194 | } 1195 | body:not(.no-kanban-styles) 1196 | .kanban-plugin__lane-header-wrapper 1197 | .kanban-plugin__grow-wrap 1198 | > textarea, 1199 | body:not(.no-kanban-styles) 1200 | .kanban-plugin__lane-input-wrapper 1201 | .kanban-plugin__grow-wrap 1202 | > textarea { 1203 | background: transparent; 1204 | } 1205 | body .kanban-plugin__item-button-wrapper > button { 1206 | color: var(--text-muted); 1207 | font-weight: 400; 1208 | background: 0 0; 1209 | min-height: calc(var(--input-height) + 8px); 1210 | } 1211 | body .kanban-plugin__item-form .kanban-plugin__item-input-wrapper { 1212 | min-height: calc(var(--input-height) + 8px); 1213 | display: flex; 1214 | justify-content: center; 1215 | } 1216 | .kanban-plugin__item-input-wrapper textarea { 1217 | background-color: var(--background-primary); 1218 | } 1219 | .kanban-plugin__lane-items { 1220 | padding-bottom: 0; 1221 | } 1222 | 1223 | /* ─────────────────────────────────────────────────── */ 1224 | /* Styles Settings */ 1225 | /* ─────────────────────────────────────────────────── */ 1226 | 1227 | /* @settings 1228 | name: Things Theme 1229 | id: things-style 1230 | settings: 1231 | - 1232 | id: features 1233 | title: Features 1234 | type: heading 1235 | level: 2 1236 | collapsed: true 1237 | - 1238 | title: Black mobile background 1239 | description: Change mobile editor background to default theme black 1240 | id: mobile-black-background 1241 | type: class-toggle 1242 | default: false 1243 | - 1244 | title: Disable mobile floating-action button 1245 | description: Revert placement of edit/preview button to default in header (mobile) 1246 | id: floating-button-off 1247 | type: class-toggle 1248 | default: false 1249 | - 1250 | title: Highlight active line 1251 | description: Change background color of current working line 1252 | id: active-line 1253 | type: class-toggle 1254 | default: false 1255 | - 1256 | title: Fancy code blocks 1257 | description: Enable fancy numbered code blocks 1258 | id: fancy-code 1259 | type: class-toggle 1260 | default: false 1261 | - 1262 | title: Fancy highlighting 1263 | description: Enable fancy highlight styles with highlight underlines 1264 | id: fancy-highlight 1265 | type: class-toggle 1266 | default: false 1267 | - 1268 | title: Disable Kanban board styles 1269 | description: Remove minimalist styling to the Kanban plugin 1270 | id: no-kanban-styles 1271 | type: class-toggle 1272 | default: false 1273 | - 1274 | id: link-decoration 1275 | title: Underline internal links 1276 | description: Show underlines on internal links 1277 | type: variable-select 1278 | default: Underline 1279 | options: 1280 | - Underline 1281 | - None 1282 | - 1283 | id: link-external-decoration 1284 | title: Underline external links 1285 | description: Show underlines on external links 1286 | type: variable-select 1287 | default: Underline 1288 | options: 1289 | - Underline 1290 | - None 1291 | - 1292 | id: custom-fonts 1293 | title: Typography 1294 | type: heading 1295 | level: 2 1296 | collapsed: true 1297 | - 1298 | id: default-font-color 1299 | title: Default font colors 1300 | description: Use the default font color styling for bold, italics, and quotes 1301 | type: class-toggle 1302 | default: false 1303 | - 1304 | id: text-highlight-bg-l 1305 | title: Highlight color (light) 1306 | type: variable-color 1307 | format: rgb 1308 | default: 'rgba(255, 208, 0, 0.4)' 1309 | - 1310 | id: text-highlight-bg-d 1311 | title: Highlight color (dark) 1312 | type: variable-color 1313 | format: rgb 1314 | default: 'rgba(255, 208, 0, 0.4)' 1315 | - 1316 | id: strong-color 1317 | title: Bold font color 1318 | type: variable-color 1319 | format: hex 1320 | default: '#FF82B2' 1321 | - 1322 | id: em-color 1323 | title: Italics font color 1324 | type: variable-color 1325 | format: hex 1326 | default: '#FF82B2' 1327 | - 1328 | id: quote-color 1329 | title: Blockquotes font color 1330 | type: variable-color 1331 | format: hex 1332 | default: '#3EB4BF' 1333 | - 1334 | id: code-normal 1335 | title: Inline code blocks font color (Light mode) 1336 | type: variable-color 1337 | format: hex 1338 | default: '#BEC6CF' 1339 | - 1340 | id: code-color-d 1341 | title: Inline code blocks font color (Dark mode) 1342 | type: variable-color 1343 | format: hex 1344 | default: '#555E68' 1345 | - 1346 | id: tag-background-color-l 1347 | title: Tag background color (Light mode) 1348 | type: variable-color 1349 | format: hex 1350 | default: '#BDE1D3' 1351 | - 1352 | id: tag-font-color-l 1353 | title: Tag font color (Light mode) 1354 | type: variable-color 1355 | format: hex 1356 | default: '#1D694B' 1357 | - 1358 | id: tag-background-color-d 1359 | title: Tag background color (Dark mode) 1360 | type: variable-color 1361 | format: hex 1362 | default: '#1D694B' 1363 | - 1364 | id: tag-font-color-d 1365 | title: Tag font color (Dark mode) 1366 | type: variable-color 1367 | format: hex 1368 | default: '#FFFFFF' 1369 | - 1370 | id: progress-color 1371 | title: Progress colorful mode switcher 1372 | description: Toggle progress color scheme 1373 | type: class-toggle 1374 | default: false 1375 | - 1376 | id: progress-color-1 1377 | title: progress 2-39% color 1378 | type: variable-color 1379 | opacity: true 1380 | format: hex 1381 | default: '#ad5758' 1382 | - 1383 | id: progress-color-2 1384 | title: progress 40-59% color 1385 | type: variable-color 1386 | opacity: true 1387 | format: hex 1388 | default: '#b87f4c' 1389 | - 1390 | id: progress-color-3 1391 | title: progress 60-79% color 1392 | type: variable-color 1393 | opacity: true 1394 | format: hex 1395 | default: '#d2b874' 1396 | - 1397 | id: progress-color-4 1398 | title: progress 80-99% color 1399 | type: variable-color 1400 | opacity: true 1401 | format: hex 1402 | default: '#b0c07e' 1403 | - 1404 | id: progress-color-5 1405 | title: progress 1,100% color 1406 | type: variable-color 1407 | opacity: true 1408 | format: hex 1409 | default: '#768399' 1410 | - 1411 | id: headings 1412 | title: Headings 1413 | type: heading 1414 | level: 2 1415 | collapsed: true 1416 | - 1417 | id: level-1-headings 1418 | title: Level 1 Headings 1419 | type: heading 1420 | level: 3 1421 | collapsed: true 1422 | - 1423 | id: h1-size 1424 | title: H1 font size 1425 | description: Accepts any CSS font-size value 1426 | type: variable-text 1427 | default: 1.7em 1428 | - 1429 | id: h1-weight 1430 | title: H1 font weight 1431 | description: Accepts numbers representing the CSS font-weight 1432 | type: variable-number 1433 | default: 700 1434 | - 1435 | id: h1-color 1436 | title: H1 color 1437 | type: variable-color 1438 | format: hex 1439 | default: '#' 1440 | - 1441 | id: level-2-headings 1442 | title: Level 2 Headings 1443 | type: heading 1444 | level: 3 1445 | collapsed: true 1446 | - 1447 | id: h2-size 1448 | title: H2 font size 1449 | description: Accepts any CSS font-size value 1450 | type: variable-text 1451 | default: 1.5em 1452 | - 1453 | id: h2-weight 1454 | title: H2 font weight 1455 | description: Accepts numbers representing the CSS font-weight 1456 | type: variable-number 1457 | default: 700 1458 | - 1459 | id: h2-color 1460 | title: H2 color 1461 | type: variable-color 1462 | format: hex 1463 | default: '#2E80F2' 1464 | - 1465 | id: h2-underline 1466 | title: H2 underline 1467 | description: Toggle H2 underline (border-bottom) 1468 | type: class-toggle 1469 | default: true 1470 | - 1471 | id: level-3-headings 1472 | title: Level 3 Headings 1473 | type: heading 1474 | level: 3 1475 | collapsed: true 1476 | - 1477 | id: h3-size 1478 | title: H3 font size 1479 | description: Accepts any CSS font-size value 1480 | type: variable-text 1481 | default: 1.2em 1482 | - 1483 | id: h3-weight 1484 | title: H3 font weight 1485 | description: Accepts numbers representing the CSS font-weight 1486 | type: variable-number 1487 | default: 600 1488 | - 1489 | id: h3-color 1490 | title: H3 color 1491 | type: variable-color 1492 | format: hex 1493 | default: '#2E80F2' 1494 | - 1495 | id: level-4-headings 1496 | title: Level 4 Headings 1497 | type: heading 1498 | level: 3 1499 | collapsed: true 1500 | - 1501 | id: h4-size 1502 | title: H4 font size 1503 | description: Accepts any CSS font-size value 1504 | type: variable-text 1505 | default: 1.1em 1506 | - 1507 | id: h4-weight 1508 | title: H4 font weight 1509 | description: Accepts numbers representing the CSS font-weight 1510 | type: variable-number 1511 | default: 500 1512 | - 1513 | id: h4-color 1514 | title: H4 color 1515 | type: variable-color 1516 | format: hex 1517 | default: '#E5B567' 1518 | - 1519 | id: h4-transform 1520 | title: H4 transform 1521 | description: Transform the H4 heading text 1522 | type: variable-select 1523 | default: uppercase 1524 | options: 1525 | - 1526 | label: Uppercase 1527 | value: uppercase 1528 | - 1529 | label: None 1530 | value: none 1531 | - 1532 | id: level-5-headings 1533 | title: Level 5 Headings 1534 | type: heading 1535 | level: 3 1536 | collapsed: true 1537 | - 1538 | id: h5-size 1539 | title: H5 font size 1540 | description: Accepts any CSS font-size value 1541 | type: variable-text 1542 | default: 1em 1543 | - 1544 | id: h5-weight 1545 | title: H5 font weight 1546 | description: Accepts numbers representing the CSS font-weight 1547 | type: variable-number 1548 | default: 500 1549 | - 1550 | id: h5-color 1551 | title: H5 color 1552 | type: variable-color 1553 | format: hex 1554 | default: '#E83E3E' 1555 | - 1556 | id: level-6-headings 1557 | title: Level 6 Headings 1558 | type: heading 1559 | level: 3 1560 | collapsed: true 1561 | - 1562 | id: h6-size 1563 | title: H6 font size 1564 | description: Accepts any CSS font-size value 1565 | type: variable-text 1566 | default: 0.9em 1567 | - 1568 | id: h6-weight 1569 | title: H6 font weight 1570 | description: Accepts numbers representing the CSS font-weight 1571 | type: variable-number 1572 | default: 400 1573 | - 1574 | id: h6-color 1575 | title: H6 color 1576 | type: variable-color 1577 | format: hex 1578 | default: '#' 1579 | - 1580 | id: credits 1581 | title: Credits 1582 | type: heading 1583 | description: Created with ❤︎ by @colineckert. Support @colineckert at buymeacoffee.com/colineckert 1584 | level: 2 1585 | collapsed: true 1586 | 1587 | */ 1588 | 1589 | /* ─────────────────────────────────────────────────── */ 1590 | /* Plugin Compatibility info for the Obsidian Hub */ 1591 | /* ─────────────────────────────────────────────────── */ 1592 | 1593 | /* @plugins 1594 | core: 1595 | - backlink 1596 | - command-palette 1597 | - file-explorer 1598 | - global-search 1599 | - graph 1600 | - outgoing-link 1601 | - outline 1602 | - page-preview 1603 | - starred 1604 | - switcher 1605 | - tag-pane 1606 | - file-recovery 1607 | - daily-notes 1608 | - random-note 1609 | - publish 1610 | - sync 1611 | - word-count 1612 | community: 1613 | - sliding-panes-obsidian 1614 | - obsidian-codemirror-options 1615 | - obsidian-kanban 1616 | - dataview 1617 | - obsidian-hider 1618 | - calendar 1619 | - mysnippets-plugin 1620 | - cmenu-plugin 1621 | - obsidian-outliner 1622 | - readwise-official 1623 | - tag-wrangler 1624 | - todoist-sync-plugin 1625 | - templater-obsidian 1626 | - obsidian-system-dark-mode 1627 | - obsidian-style-settings 1628 | */ 1629 | --------------------------------------------------------------------------------