├── docs ├── .obsidian │ ├── hotkeys.json │ ├── plugins │ │ ├── qatt │ │ │ ├── .hotreload │ │ │ └── data.json │ │ ├── customjs │ │ │ ├── styles.css │ │ │ └── manifest.json │ │ ├── hot-reload │ │ │ └── manifest.json │ │ ├── OA-file-hider │ │ │ ├── data.json │ │ │ └── manifest.json │ │ ├── obsidian-5e-statblocks │ │ │ ├── manifest.json │ │ │ └── data.json │ │ ├── cmdr │ │ │ ├── manifest.json │ │ │ └── data.json │ │ ├── dataview │ │ │ ├── manifest.json │ │ │ └── data.json │ │ ├── obsidian-meta-bind-plugin │ │ │ ├── manifest.json │ │ │ └── data.json │ │ └── obsidian-shellcommands │ │ │ └── manifest.json │ ├── snippets │ │ └── no-external-link-icon.css │ ├── app.json │ ├── appearance.json │ ├── community-plugins.json │ ├── core-plugins.json │ └── core-plugins-migration.json ├── templates │ ├── _header.md │ ├── hb-helpers │ │ ├── _header.md │ │ ├── index.md │ │ ├── codeblockfooter.md │ │ ├── codeblockheader.md │ │ ├── lowercase.md │ │ ├── uppercase.md │ │ ├── htmltasklist.md │ │ ├── taskcheckbox.md │ │ ├── notelink.md │ │ ├── group.md │ │ ├── flexibletaskcheckbox.md │ │ ├── taskcheckboxwithappend.md │ │ ├── pad.md │ │ ├── capitalize.md │ │ ├── micromark.md │ │ ├── obsidian.md │ │ ├── obsidianhtmlinternallink.md │ │ ├── stringify.md │ │ └── relationaloperators.md │ ├── index.md │ └── template.md ├── queries │ ├── sql-functions │ │ ├── _header.md │ │ ├── arrayfrom.md │ │ ├── index.md │ │ ├── stringify.md │ │ ├── parsewikilinkdisplayname.md │ │ ├── updatepropertyfromlist.md │ │ ├── parsewikilinklocation.md │ │ ├── notePath.md │ │ ├── notePathWithFileExtension.md │ │ ├── notePathWithoutFileExtension.md │ │ ├── custom-functions.md │ │ ├── noteFileName.md │ │ └── wikilinkhasdisplayname.md │ ├── sql-statements │ │ ├── _header.md │ │ ├── index.md │ │ ├── coalesce.md │ │ ├── case.md │ │ ├── in.md │ │ ├── pageproperty.md │ │ ├── if-then-else.md │ │ ├── joinarray.md │ │ ├── extractline.md │ │ ├── reverse.md │ │ ├── charindex.md │ │ └── lineindex.md │ └── index.md ├── examples-tutorials │ ├── handlebars │ │ ├── _header.md │ │ ├── index.md │ │ ├── pad.md │ │ ├── notelink.md │ │ ├── htmltasklist.md │ │ ├── taskcheckbox.md │ │ ├── obsidian.md │ │ ├── micromark.md │ │ ├── flexibletaskcheckbox.md │ │ ├── taskcheckboxwithappend.md │ │ ├── capitalize.md │ │ ├── codeblockfooter.md │ │ ├── codeblockheader.md │ │ ├── obsidianhtmlinternallink.md │ │ ├── relationaloperators.md │ │ └── group.md │ ├── live-examples │ │ ├── notes │ │ │ ├── Journal 1.md │ │ │ ├── Journal 2.md │ │ │ ├── index.md │ │ │ └── projects │ │ │ │ ├── Project Two.md │ │ │ │ └── Project One.md │ │ ├── index.md │ │ └── project-list.md │ ├── generated-top-ten-notes.md │ ├── ttrpg │ │ ├── index.md │ │ ├── monster-search-live.md │ │ └── monster-search.md │ ├── index.md │ ├── using-pageproperty-simple-live.md │ ├── listing-recently-updated-files-live.md │ ├── listing-recently-updated-files.md │ ├── csv-loader-web.md │ ├── csv-loader.md │ ├── replace-target-path-example.md │ ├── dataview-fields.md │ └── active-tasks-grouped-by-day-live.md ├── public │ ├── qatt_logo_v1.png │ └── qatt_logo_v2.png ├── contributing │ └── index.md ├── verfication │ ├── readme.md │ └── simple-queries.md ├── 404.html ├── data-tables │ ├── legacy-tables │ │ ├── index.md │ │ ├── obsidian-markdown-files.md │ │ ├── obsidian-markdown-lists.md │ │ └── obsidian-markdown-tasks.md │ ├── dataview │ │ ├── dataview-lists.md │ │ ├── index.md │ │ └── dataview-tasks.md │ ├── pageproperty.md │ ├── obsidian-lists.md │ ├── index.md │ └── obsidian-tasks.md ├── about.md ├── installation.md ├── api-examples.md ├── markdown-examples.md ├── index.md ├── first-query.md └── output-generation.md ├── .config ├── global-setup.js ├── tsconfig.commonjs.json ├── .markdownlint-cli2.jsonc ├── commitlint.config.js ├── obsidian-versions-updater.js └── esbuild.config.mjs ├── src ├── Render │ ├── HandlebarsHelpers │ │ ├── Trim.ts │ │ ├── IsLowPriority.ts │ │ ├── ToInt.ts │ │ ├── IsHighPriority.ts │ │ ├── IsMediumPriority.ts │ │ ├── FormatDate.ts │ │ ├── Lowercase.ts │ │ ├── Uppercase.ts │ │ ├── Capitalize.ts │ │ ├── index.ts │ │ ├── NoteLink.ts │ │ ├── CodeBlockFooter.ts │ │ ├── CodeBlockHeader.ts │ │ ├── Pad.ts │ │ ├── Stringify.ts │ │ ├── TaskCheckbox.ts │ │ ├── Micromark.ts │ │ ├── ObsidianHtmlInternalLink.ts │ │ ├── TaskCheckboxWithAppend.ts │ │ └── Obsidian.ts │ ├── IRenderer.ts │ ├── TextRenderer.ts │ ├── RenderFactory.ts │ └── HandlebarsRenderer.ts ├── typings │ ├── mdast-util-wiki-link.d.ts │ ├── micromark-extension-wiki-link.d.ts │ ├── alasql-ex.d.ts │ └── obsidian-ex.d.ts ├── Query │ ├── IQuery.ts │ ├── Functions │ │ ├── index.ts │ │ ├── ArrayFrom.ts │ │ ├── Rand.ts │ │ ├── Stringify.ts │ │ ├── Reverse.ts │ │ ├── JoinArray.ts │ │ ├── ExtractLine.ts │ │ ├── Charindex.ts │ │ ├── UpdatePropertyFromList.ts │ │ └── UpdatePropertyFromPrompt.ts │ └── QueryFactory.ts ├── Settings │ ├── featureConfiguration.json │ └── DefaultSettings.ts ├── UI │ ├── alaSqlFunctions.yaml │ └── handlebarsHelpers.yaml ├── PostRender │ ├── IPostRenderer.ts │ ├── HtmlPostRenderer.ts │ ├── RawPostRenderer.ts │ └── ObsidianPostRenderer.ts ├── Interfaces │ ├── ISettingsManager.ts │ ├── Settings.ts │ └── IQueryAllTheThingsPlugin.ts ├── ReleaseNotes.ts ├── Integrations │ └── DataviewService.ts ├── handlers │ └── EventHandler.ts ├── lib │ └── SuggesterModal.ts └── Data │ └── JsonLoaderService.ts ├── init.ps1 ├── .gitattributes ├── manifest-beta.json ├── dist └── manifest-beta.json ├── library └── README.md ├── SECURITY.md ├── .vscode ├── tasks.json ├── extensions.json └── settings.json ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── workflows │ ├── verify.yml │ └── publish-docs-v2.yml ├── .gitignore ├── ophidian.config.mjs ├── tsconfig.json ├── versions.json ├── LICENSE ├── .devcontainer └── devcontainer.json ├── tests └── AlaSQLQuery.test.ts └── README.md /docs/.obsidian/hotkeys.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /docs/.obsidian/plugins/qatt/.hotreload: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/templates/_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 4 3 | 4 | parent: Templates 5 | -------------------------------------------------------------------------------- /.config/global-setup.js: -------------------------------------------------------------------------------- 1 | module.exports = async () => { 2 | process.env.TZ = 'UTC'; 3 | }; 4 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/_header.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Trim.ts: -------------------------------------------------------------------------------- 1 | export function trim(value: string) { 2 | return String(value).trim(); 3 | } 4 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/customjs/styles.css: -------------------------------------------------------------------------------- 1 | /* Sets all the text color to red! */ 2 | /* body { 3 | color: red; 4 | } */ 5 | -------------------------------------------------------------------------------- /docs/.obsidian/snippets/no-external-link-icon.css: -------------------------------------------------------------------------------- 1 | .external-link { 2 | background-image: none; 3 | padding-right: 0px; 4 | } -------------------------------------------------------------------------------- /docs/public/qatt_logo_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sytone/obsidian-queryallthethings/HEAD/docs/public/qatt_logo_v1.png -------------------------------------------------------------------------------- /docs/public/qatt_logo_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sytone/obsidian-queryallthethings/HEAD/docs/public/qatt_logo_v2.png -------------------------------------------------------------------------------- /init.ps1: -------------------------------------------------------------------------------- 1 | nvm install 20.15.1 2 | nvm use 20.15.1 3 | npm install -g pnpm@latest-9 4 | pnpm install 5 | pnpm run build 6 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/IsLowPriority.ts: -------------------------------------------------------------------------------- 1 | export function isLowPriority(value: number) { 2 | return value === 3; 3 | } 4 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/ToInt.ts: -------------------------------------------------------------------------------- 1 | export function toInt(value: string) { 2 | return Number.parseInt(value, 10); 3 | } 4 | -------------------------------------------------------------------------------- /docs/.obsidian/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "useMarkdownLinks": true, 3 | "showInlineTitle": false, 4 | "showUnsupportedFiles": true 5 | } -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/IsHighPriority.ts: -------------------------------------------------------------------------------- 1 | export function isHighPriority(value: number) { 2 | return value === 1; 3 | } 4 | -------------------------------------------------------------------------------- /src/typings/mdast-util-wiki-link.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/filename-case */ 2 | 3 | declare module 'mdast-util-wiki-link'; 4 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/notes/Journal 1.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | type: journal 4 | --- 5 | 6 | # Journal One 7 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/notes/Journal 2.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | type: journal 4 | --- 5 | 6 | # Journal Two 7 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/IsMediumPriority.ts: -------------------------------------------------------------------------------- 1 | export function isMediumPriority(value: number) { 2 | return value === 2; 3 | } 4 | -------------------------------------------------------------------------------- /src/typings/micromark-extension-wiki-link.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/filename-case */ 2 | 3 | declare module 'micromark-extension-wiki-link'; 4 | -------------------------------------------------------------------------------- /docs/.obsidian/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "accentColor": "", 3 | "enabledCssSnippets": [ 4 | "no-external-link-icon" 5 | ], 6 | "showViewHeader": true 7 | } -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/FormatDate.ts: -------------------------------------------------------------------------------- 1 | import {DateTime} from 'luxon'; 2 | 3 | export function formatDate(value: number) { 4 | return DateTime.fromMillis(value).toFormat('yyyy-MM-dd'); 5 | } 6 | -------------------------------------------------------------------------------- /src/typings/alasql-ex.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/filename-case */ 2 | /* eslint-disable @typescript-eslint/naming-convention */ 3 | declare module 'alasql' { 4 | interface AlaSQL { 5 | tables: any; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/notes/index.md: -------------------------------------------------------------------------------- 1 | This folder contains all the notes that are used in the examples. This makes it simpler to use the same notes across examples reducing time to make the examples and have them populated. -------------------------------------------------------------------------------- /src/Render/IRenderer.ts: -------------------------------------------------------------------------------- 1 | import {type QattCodeBlock} from 'QattCodeBlock'; 2 | 3 | export interface IRenderer { 4 | renderTemplate: (codeblockConfiguration: QattCodeBlock, result: any) => Promise; 5 | defaultTemplate: string; 6 | } 7 | -------------------------------------------------------------------------------- /docs/contributing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 50 3 | 4 | title: Contributing 5 | has_children: true 6 | --- 7 | 8 | If you have a issue or want to improve the plugin please fee free to contribute back by creating a PR or a detailed issue of the problem. 9 | -------------------------------------------------------------------------------- /docs/.obsidian/community-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "dataview", 3 | "qatt", 4 | "obsidian-shellcommands", 5 | "cmdr", 6 | "hot-reload", 7 | "customjs", 8 | "obsidian-meta-bind-plugin", 9 | "obsidian-5e-statblocks", 10 | "OA-file-hider" 11 | ] -------------------------------------------------------------------------------- /src/Query/IQuery.ts: -------------------------------------------------------------------------------- 1 | import {type QattCodeBlock} from 'QattCodeBlock'; 2 | 3 | export interface IQuery { 4 | name: string | undefined; 5 | codeblockConfiguration: QattCodeBlock; 6 | error: string | undefined; 7 | applyQuery: (queryId: string) => any; 8 | } 9 | -------------------------------------------------------------------------------- /docs/examples-tutorials/generated-top-ten-notes.md: -------------------------------------------------------------------------------- 1 | - replace-target-path-example 2 | - codeblock 3 | - obsidian-markdown-tasks 4 | - taskcheckboxwithappend 5 | - taskcheckboxwithappend 6 | - taskcheckbox 7 | - taskcheckbox 8 | - stringify 9 | - pad 10 | - pad 11 | -------------------------------------------------------------------------------- /.config/tsconfig.commonjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "noImplicitAny": false, 6 | "skipLibCheck": true, 7 | "types": [ 8 | "node", 9 | "mocha" 10 | ] 11 | } 12 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.ps1 text eol=crlf 4 | 5 | *.sh text eol=lf 6 | 7 | *.config text 8 | *.json text 9 | *.xml text 10 | 11 | *.bmp binary 12 | *.gif binary 13 | *.ico binary 14 | *.jpg binary 15 | *.pdf binary 16 | *.png binary 17 | -------------------------------------------------------------------------------- /docs/verfication/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | exclude: true 3 | --- 4 | 5 | This contains pages that allow a build to be verified in Obsidan. Use the `pnpm test:docs` command to open the vault and then as you develop you can check pages in this section to ensure nothing is breaking. 6 | 7 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/hot-reload/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "hot-reload", 3 | "name": "Hot Reload", 4 | "version": "0.1.10", 5 | "minAppVersion": "0.15.9", 6 | "description": "Automatically reload in-development plugins when their files are changed", 7 | "isDesktopOnly": true 8 | } -------------------------------------------------------------------------------- /docs/queries/sql-functions/arrayfrom.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: arrayfrom(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `arrayfrom` function allows the user to map a item to an array, for example a Map via mapname->values() -------------------------------------------------------------------------------- /src/Settings/featureConfiguration.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "index": 0, 4 | "internalName": "ENABLE_TEXTAREA_SETTING", 5 | "displayName": "Enable Textarea Setting", 6 | "description": "Description.", 7 | "enabledByDefault": false, 8 | "stable": false 9 | } 10 | ] -------------------------------------------------------------------------------- /src/UI/alaSqlFunctions.yaml: -------------------------------------------------------------------------------- 1 | - description: "Reverse characters in a string" 2 | declaration: "REVERSE ( basename )" 3 | author: "Sytone" 4 | - description: "Join array with optional separator" 5 | declaration: "JoinArray ( dataArray [ , separator ] )" 6 | author: "Sytone" 7 | 8 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 30 3 | 4 | title: Handlebars 5 | has_children: true 6 | parent: Examples / Tutorials 7 | --- 8 | 9 | This section contains examples of the different Handlebars helpers available for use in the templates for this plugin. 10 | -------------------------------------------------------------------------------- /src/Settings/DefaultSettings.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface ISqlSettings { 3 | onStartSqlQueries: string; 4 | } 5 | 6 | export const SqlSettingsDefaults: ISqlSettings = { 7 | onStartSqlQueries: 'CREATE TABLE my_lookup(name,birthday);\nINSERT INTO my_lookup VALUES ("fred", 2000-02-03);', 8 | }; 9 | -------------------------------------------------------------------------------- /docs/examples-tutorials/ttrpg/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 20 3 | 4 | title: TTRPG Examples 5 | has_children: true 6 | parent: Examples / Tutorials 7 | --- 8 | 9 | These examples show how you can use the plugin to help with running or being in Tabletop Role Playing Games (TTRPG) like Pathfinder or D&D 10 | -------------------------------------------------------------------------------- /docs/examples-tutorials/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 20 3 | 4 | title: Examples / Tutorials 5 | has_children: true 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | This section provides examples on how to use the plugin to render information about your vault to make exploration and navigation simpler and more custom to your needs. 10 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/OA-file-hider/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "hidden": true, 3 | "hiddenList": [ 4 | "Gemfile.old", 5 | "Gemfile.lock", 6 | "Gemfile", 7 | "Dockerfile", 8 | "404.html", 9 | "_config.docker.yml", 10 | "_config.yml", 11 | "templates/handlebars-helpers.md.removed", 12 | "_site" 13 | ] 14 | } -------------------------------------------------------------------------------- /docs/queries/sql-statements/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 20 3 | 4 | title: SQL Statements 5 | has_children: true 6 | parent: Writing Queries 7 | --- 8 | 9 | The following pages provide guidance on the most common SQL statements you may want to use in your queries. They have plugin specific and general examples to help you understand them. 10 | -------------------------------------------------------------------------------- /src/PostRender/IPostRenderer.ts: -------------------------------------------------------------------------------- 1 | import {type Component} from 'obsidian'; 2 | 3 | // Standard interface for all post renderers. The element children should be updated as needed. 4 | export interface IPostRenderer { 5 | renderMarkdown: (renderResults: string, element: HTMLElement, sourcePath: string, component: Component) => Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/UI/handlebarsHelpers.yaml: -------------------------------------------------------------------------------- 1 | - description: 'Capitalize the first letter of the string' 2 | declaration: "{{capitalize 'replace with property'}}" 3 | - description: 'Start markdown code block with optional type' 4 | declaration: "{{codeBlockHeader 'text'}}" 5 | - description: 'End markdown code block' 6 | declaration: "{{codeBlockFooter}}" 7 | 8 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/customjs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "customjs", 3 | "name": "CustomJS", 4 | "version": "1.0.21", 5 | "minAppVersion": "0.9.12", 6 | "description": "This plugin lets you use custom javascript files inside your vault.", 7 | "author": "Sam Lewis", 8 | "authorUrl": "https://github.com/samlewis0602", 9 | "isDesktopOnly": false 10 | } -------------------------------------------------------------------------------- /manifest-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "qatt", 3 | "name": "Query all the things", 4 | "version": "0.8.2", 5 | "minAppVersion": "1.2.8", 6 | "description": "Execute SQL base queries against your data in Obsidian and render it how you want using templates.", 7 | "author": "Sytone", 8 | "authorUrl": "https://github.com/sytone", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /dist/manifest-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "qatt", 3 | "name": "Query all the things", 4 | "version": "0.8.2", 5 | "minAppVersion": "1.2.8", 6 | "description": "Execute SQL base queries against your data in Obsidian and render it how you want using templates.", 7 | "author": "Sytone", 8 | "authorUrl": "https://github.com/sytone", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 40 3 | 4 | title: Handlebars Helpers 5 | has_children: true 6 | parent: Using Templates 7 | --- 8 | 9 | The main engine used to render the results of a query uses the handlebars syntax. This allows you to create a flexible template to render the results. 10 | 11 | The following helpers can be used to simplify the rendering of the query results. 12 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/OA-file-hider/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "OA-file-hider", 3 | "name": "File Hider", 4 | "version": "1.1.1", 5 | "minAppVersion": "0.14.6", 6 | "description": "An Obsidian plugin that allows hiding files and folders in the built-in file explorer", 7 | "author": "Oliver Akins", 8 | "authorUrl": "https://github.com/Oliver-Akins", 9 | "isDesktopOnly": false, 10 | "branch": "main" 11 | } 12 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/codeblockfooter.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: codeBlockFooter 6 | --- 7 | 8 | # {{ $frontmatter.title }} 9 | 10 | The `codeBlockFooter`\-helper will insert the three back ticks into the resulting markdown. 11 | 12 | ```handlebars 13 | {{codeBlockFooter}} 14 | ``` 15 | 16 | will result in: 17 | 18 | ````markdown 19 | ``` 20 | ```` -------------------------------------------------------------------------------- /src/PostRender/HtmlPostRenderer.ts: -------------------------------------------------------------------------------- 1 | import {type IPostRenderer} from 'PostRender/IPostRenderer'; 2 | import {type Component} from 'obsidian'; 3 | 4 | export class HtmlPostRenderer implements IPostRenderer { 5 | public async renderMarkdown(renderResults: string, element: HTMLElement, sourcePath: string, component: Component) { 6 | element.innerHTML = renderResults; 7 | return renderResults; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/PostRender/RawPostRenderer.ts: -------------------------------------------------------------------------------- 1 | import {type IPostRenderer} from 'PostRender/IPostRenderer'; 2 | import {type Component} from 'obsidian'; 3 | 4 | export class RawPostRenderer implements IPostRenderer { 5 | public async renderMarkdown(renderResults: string, element: HTMLElement, sourcePath: string, component: Component) { 6 | element.textContent = renderResults; 7 | return renderResults; 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/obsidian-5e-statblocks/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-5e-statblocks", 3 | "name": "Fantasy Statblocks", 4 | "version": "4.7.8", 5 | "description": "Create Fantasy Statblocks in Obsidian.md", 6 | "minAppVersion": "0.12.0", 7 | "author": "Jeremy Valentine", 8 | "authorUrl": "", 9 | "fundingUrl": "https://www.buymeacoffee.com/valentine195", 10 | "isDesktopOnly": false 11 | } 12 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/cmdr/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "cmdr", 3 | "name": "Commander", 4 | "version": "0.5.2", 5 | "minAppVersion": "1.4.0", 6 | "description": "Customize your workspace by adding commands everywhere, create Macros and supercharge your mobile toolbar.", 7 | "author": "jsmorabito & phibr0", 8 | "authorUrl": "https://github.com/phibr0", 9 | "fundingUrl": "https://ko-fi.com/phibr0", 10 | "isDesktopOnly": false 11 | } 12 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/dataview/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "dataview", 3 | "name": "Dataview", 4 | "version": "0.5.67", 5 | "minAppVersion": "0.13.11", 6 | "description": "Complex data views for the data-obsessed.", 7 | "author": "Michael Brenan ", 8 | "authorUrl": "https://github.com/blacksmithgu", 9 | "helpUrl": "https://blacksmithgu.github.io/obsidian-dataview/", 10 | "isDesktopOnly": false 11 | } 12 | -------------------------------------------------------------------------------- /library/README.md: -------------------------------------------------------------------------------- 1 | # Query, Template and data library 2 | 3 | This folder holds the files that can be imported by the plugin. It is a simple way to share queries and render templates across the community. 4 | 5 | ## Usage 6 | 7 | Use the setting in Obsidian to import the library to a folder of your choice. 8 | 9 | ## Contribution 10 | 11 | ### Adding Queries 12 | 13 | ### Adding Render Templates 14 | 15 | ### Adding Scripts 16 | 17 | ### Adding Data 18 | -------------------------------------------------------------------------------- /.config/.markdownlint-cli2.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "MD013": false, // Ignore the line length rule. 4 | "MD025": false, // Allow the title in the front matter and markdown heading one to match. 5 | "MD033": false, // Allow Inline HTML. 6 | "MD028": false, // Allow Blank line inside blockquote 7 | "MD046": false // allow mixed Code block style [Expected: indented; Actual: fenced]markdownlintMD046 8 | }, 9 | "fix": true 10 | } 11 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/codeblockheader.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: codeBlockHeader 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `codeBlockHeader`\-helper will insert the three back ticks into the resulting markdown with the name specified as a parameter as the code block type. 10 | 11 | ```handlebars 12 | {{codeBlockHeade 'text'}} 13 | ``` 14 | 15 | will result in: 16 | 17 | ````markdown 18 | ```text 19 | ```` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/lowercase.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: lowercase value 6 | --- 7 | The `lowercase`\-helper will convert the entire string to lowercase. 8 | 9 | ```handlebars 10 | {{{lowercase sentence}}} 11 | ``` 12 | 13 | when used with this context: 14 | 15 | ```json 16 | { 17 | sentence: "This Is Some SENtence" 18 | } 19 | ``` 20 | 21 | will result in: 22 | 23 | ```text 24 | this is some sentence 25 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/uppercase.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Templates 5 | title: uppercase value 6 | --- 7 | The `uppercase`\-helper will convert the entire string to uppercase. 8 | 9 | 10 | ```handlebars 11 | {{{uppercase sentence}}} 12 | ``` 13 | 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | sentence: "This Is Some SENtence" 20 | } 21 | ``` 22 | 23 | will result in: 24 | 25 | ```text 26 | THIS IS SOME SENTENCE 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/index.md: -------------------------------------------------------------------------------- 1 | This folder contains examples that run when the parent document folder is loaded as an obsidian vault. They are to make playing with the plugin and examples simpler to do. They should reflect the written examples in the examples folder. 2 | 3 | Contributions are always welcome. 4 | 5 | These are based off use cases I have and may or may not work well in your model of knowledge management. Take what you want and learn from them, no one model works for everyone. -------------------------------------------------------------------------------- /docs/templates/hb-helpers/htmltasklist.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: htmlTaskList 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `htmlTaskList`-helper will ... 10 | 11 | ```handlebars 12 | {{htmlTaskList}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | ... 28 | ```` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/taskcheckbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: taskCheckbox 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `taskCheckbox`-helper will ... 10 | 11 | ```handlebars 12 | {{taskCheckbox}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | ... 28 | ```` -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the currently released version will have security updates. Currently this is 0.10.x or later. 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 0.10.x | :white_check_mark: | 10 | | < 0.10.x | :x: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | If you find a security vunrability please create a issue with the details and it will be reviewed, also feel free to make a PR with the fix. 15 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/coalesce.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: COALESCE 6 | --- 7 | 8 | Show first NON NULL and non-NaN parameter: 9 | 10 | ```sql 11 | SELECT path, COALESCE(frontmatter->area, frontmatter->Area, '') AS Area 12 | FROM obsidian_notes 13 | ``` 14 | 15 | ```sql 16 | SELECT CAST(COALESCE(hourly_wage * 40 * 52, 17 | salary, 18 | commission * num_sales) AS money) AS [Total Salary] 19 | FROM wages 20 | ORDER BY [Total Salary]; 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /404.html 3 | 4 | --- 5 | 6 | 19 | 20 |
21 |

404

22 | 23 |

Page not found :(

24 |

The requested page could not be found.

25 |
26 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/notelink.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: noteLink 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `noteLink`-helper will ... 10 | 11 | ```handlebars 12 | {{noteLink path}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | [[notepages/school/My Cool Page]] 28 | ```` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/group.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: group 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `group`\-helper will group the result by the specified column. 10 | 11 | ```handlebars 12 | {{{#group result by="column_name"}}} 13 | column_name value is specified by {{value}} 14 | collection of items in the group is specified by {{items}} 15 | {{/group}} 16 | ``` 17 | 18 | See the [group](../../examples-tutorials/handlebars/group) page for a full example. -------------------------------------------------------------------------------- /docs/templates/hb-helpers/flexibletaskcheckbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: flexibleTaskCheckbox 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `flexibleTaskCheckbox`-helper will ... 10 | 11 | ```handlebars 12 | {{flexibleTaskCheckbox}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | ... 28 | ```` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/taskcheckboxwithappend.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: taskCheckboxWithAppend 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `taskCheckboxWithAppend`-helper will ... 10 | 11 | ```handlebars 12 | {{taskCheckboxWithAppend}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | ... 28 | ```` -------------------------------------------------------------------------------- /docs/.obsidian/plugins/obsidian-meta-bind-plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-meta-bind-plugin", 3 | "name": "Meta Bind", 4 | "version": "1.2.2", 5 | "minAppVersion": "1.4.0", 6 | "description": "Make your notes interactive with inline input fields, metadata displays, and buttons.", 7 | "author": "Moritz Jung", 8 | "authorUrl": "https://www.moritzjung.dev/", 9 | "fundingUrl": "https://github.com/sponsors/mProjectsCode", 10 | "helpUrl": "https://www.moritzjung.dev/obsidian-meta-bind-plugin-docs/", 11 | "isDesktopOnly": false 12 | } 13 | -------------------------------------------------------------------------------- /docs/queries/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 15 3 | 4 | title: Writing Queries 5 | has_children: true 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | To find the data to render the plugin allows multiple query engines to be use. Currently only the AlaSQL query engine has been enabled. This is a SQL base engine that allows you to use SQL statements to query collections of object in JavaScript or in memory tables with rows and columns. 10 | 11 | Full details on AlaSQL can be found on the [AlaSQL Wiki](https://github.com/AlaSQL/alasql/wiki). 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Query/Functions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "Query/Functions/ArrayFrom"; 2 | export * from "Query/Functions/Charindex"; 3 | export * from "Query/Functions/ExtractLine"; 4 | export * from "Query/Functions/JoinArray"; 5 | export * from "Query/Functions/LineIndex"; 6 | export * from "Query/Functions/ParseWikiLinks"; 7 | export * from "Query/Functions/Reverse"; 8 | export * from "Query/Functions/Stringify"; 9 | export * from "Query/Functions/UpdatePropertyFromList"; 10 | export * from "Query/Functions/UpdatePropertyFromPrompt"; 11 | export * from "Query/Functions/Rand"; 12 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 30 3 | 4 | title: SQL Functions 5 | has_children: true 6 | parent: Writing Queries 7 | --- 8 | 9 | The main query engine AlaSQL allows for functions to be added to it. By default the following functions have been made available to make the query process simpler. 10 | 11 | > [!NOTE] 12 | > Dynamic creation of functions via js snippets is coming in a future release. 13 | 14 | Full details on AlaSQL can be found on their site and will not be replicated here. 15 | 16 | [AlaSQL Wiki](https://github.com/AlaSQL/alasql/wiki) 17 | -------------------------------------------------------------------------------- /docs/templates/hb-helpers/pad.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: pad 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `pad`-helper will pad a string with the specified character the specified number of times. By default the character is a space ' '. 10 | 11 | ```handlebars 12 | {{pad}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: 'notepages/school/My Cool Page', 20 | name: 'My Cool Page is here!' 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | ... 28 | ```` -------------------------------------------------------------------------------- /docs/templates/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 16 3 | 4 | title: Using Templates 5 | has_children: true 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The main engine used to render the results of a query uses the handlebars syntax. This allows you to create a flexible template to render the results. 10 | 11 | Full details on Handlebars can be found on their site. [Handlebars Guide](https://handlebarsjs.com/guide/) 12 | 13 | The handlebars syntax is about rendering and has limited logic, the query is expected to handle the complexity of the data manipulation leaving the output to handlebars. 14 | -------------------------------------------------------------------------------- /src/Interfaces/ISettingsManager.ts: -------------------------------------------------------------------------------- 1 | import {type FeatureFlag} from 'Settings/Feature'; 2 | import {type ISettings} from 'Interfaces/Settings'; 3 | 4 | export interface ISettingsManager { 5 | settings: ISettings; 6 | getValue(name: string): string | boolean | number; 7 | setValue(name: string, value: string | boolean | number): void; 8 | isFeatureEnabled(name: string): boolean; 9 | toggleFeature(name: string, enabled: boolean): FeatureFlag; 10 | updateSettings(newSettings: Partial): ISettings; 11 | getSettings(): ISettings; 12 | toggleDebug(): boolean; 13 | } 14 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/obsidian-shellcommands/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-shellcommands", 3 | "name": "Shell commands", 4 | "version": "0.22.0", 5 | "minAppVersion": "1.4.0", 6 | "description": "You can predefine system commands that you want to run frequently, and assign hotkeys for them. For example open external applications. Automatic execution is also supported, and execution via URI links.", 7 | "author": "Jarkko Linnanvirta", 8 | "authorUrl": "https://github.com/Taitava", 9 | "fundingUrl": "https://publish.obsidian.md/shellcommands/Donate", 10 | "isDesktopOnly": true 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "echo", 8 | "type": "shell", 9 | "command": "echo Hello" 10 | }, 11 | { 12 | "type": "npm", 13 | "script": "build:dev", 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | }, 18 | "problemMatcher": [], 19 | "label": "npm: build:dev", 20 | "detail": "node ophidian.config.mjs dev" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://EditorConfig.org for more information about .editorconfig files. 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | 12 | [*.xml] 13 | indent_size = 2 14 | 15 | [*.json] 16 | indent_size = 2 17 | 18 | [*.{yml,yaml}] 19 | indent_size = 2 20 | 21 | [*.{md,mdx}] 22 | trim_trailing_whitespace = true 23 | 24 | [*.{htm,html,js,jsm,ts,tsx,mjs}] 25 | indent_size = 2 26 | 27 | [*.{cmd,bat,ps1}] 28 | end_of_line = lf 29 | 30 | [*.sh] 31 | end_of_line = lf 32 | -------------------------------------------------------------------------------- /src/Query/Functions/ArrayFrom.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='alasql-function-arrayfrom' options='file=queries/sql-functions/arrayfrom.md' 5 | title: arrayfrom(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `arrayfrom` function allows the user to map a item to an array, for example a Map via mapname->values() 10 | 11 | // << alasql-function-arrayfrom 12 | */ 13 | export function registerFunctionArrayFrom(): void { 14 | alasql.fn.arrayFrom = function (value) { 15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument 16 | return Array.from(value); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/Render/TextRenderer.ts: -------------------------------------------------------------------------------- 1 | import {Service} from '@ophidian/core'; 2 | import {type QattCodeBlock} from 'QattCodeBlock'; 3 | import {type IRenderer} from 'Render/IRenderer'; 4 | import {LoggingService} from 'lib/LoggingService'; 5 | 6 | export class TextRenderer extends Service implements IRenderer { 7 | defaultTemplate = ''; 8 | logger = this.use(LoggingService).getLogger('Qatt.TextRenderer'); 9 | 10 | public async renderTemplate(codeblockConfiguration: QattCodeBlock, result: any) { 11 | this.logger.debug('rendering template is just JSON.stringify'); 12 | 13 | return JSON.stringify(result); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/obsidian-meta-bind-plugin/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "devMode": false, 3 | "ignoreCodeBlockRestrictions": false, 4 | "preferredDateFormat": "YYYY-MM-DD", 5 | "firstWeekday": { 6 | "index": 1, 7 | "name": "Monday", 8 | "shortName": "Mo" 9 | }, 10 | "syncInterval": 200, 11 | "minSyncInterval": 50, 12 | "maxSyncInterval": 1000, 13 | "enableJs": false, 14 | "viewFieldDisplayNullAsEmpty": false, 15 | "enableSyntaxHighlighting": true, 16 | "enableEditorRightClickMenu": true, 17 | "inputFieldTemplates": [], 18 | "buttonTemplates": [], 19 | "excludedFolders": [ 20 | "templates" 21 | ] 22 | } -------------------------------------------------------------------------------- /docs/examples-tutorials/using-pageproperty-simple-live.md: -------------------------------------------------------------------------------- 1 | --- 2 | task_status: x 3 | exclude: true 4 | --- 5 | 6 | - [ ] Not Done 7 | - [x] Done 8 | - [x] Also Done 9 | 10 | 11 | ```qatt 12 | query: | 13 | SELECT pageProperty('task_status') AS TaskStatus 14 | template: | 15 | {{#each result}} 16 | The page property 'task_status' is set to {{TaskStatus}} 17 | {{/each}} 18 | ``` 19 | 20 | 21 | 22 | ```qatt 23 | query: | 24 | SELECT TOP 5 * 25 | FROM obsidian_markdown_tasks 26 | WHERE status = pageProperty('task_status') 27 | template: | 28 | {{#each result}} 29 | - {{page}}: {{text}} 30 | {{/each}} 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/case.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: CASE 6 | --- 7 | 8 | Syntax: 9 | 10 | ```sql 11 | CASE 12 | WHEN expression1 THEN expression1 13 | WHEN expression2 THEN expression2 14 | WHEN expressionN THEN expressionN 15 | ELSE expression 16 | END; 17 | ``` 18 | 19 | ```sql 20 | CASE 21 | WHEN frontmatter->status = 'Todo' THEN '⏹️' 22 | WHEN frontmatter->status = 'In progress' THEN '🏗' 23 | WHEN frontmatter->status = 'Blocked' THEN '👀' 24 | WHEN frontmatter->status = 'Done' THEN '✅' 25 | ELSE '❓' 26 | END AS StatusEmoji 27 | ``` 28 | -------------------------------------------------------------------------------- /src/PostRender/ObsidianPostRenderer.ts: -------------------------------------------------------------------------------- 1 | import {type IPostRenderer} from 'PostRender/IPostRenderer'; 2 | import {type App, MarkdownPreviewView, type Component} from 'obsidian'; 3 | 4 | export class ObsidianPostRenderer implements IPostRenderer { 5 | /** 6 | * @param app - The Obsidian application instance. 7 | */ 8 | constructor(private readonly app: App) {} 9 | 10 | public async renderMarkdown(renderResults: string, element: HTMLElement, sourcePath: string, component: Component) { 11 | await MarkdownPreviewView.render(this.app, renderResults, element, sourcePath, component); 12 | return element.innerHTML; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/obsidian-5e-statblocks/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "monsters": [], 3 | "defaultLayouts": {}, 4 | "layouts": [], 5 | "default": "basic-5e-layout", 6 | "useDice": true, 7 | "renderDice": false, 8 | "export": true, 9 | "showAdvanced": false, 10 | "version": { 11 | "major": 4, 12 | "minor": 7, 13 | "patch": 8 14 | }, 15 | "paths": [ 16 | "/" 17 | ], 18 | "autoParse": false, 19 | "disableSRD": false, 20 | "tryToRenderLinks": true, 21 | "debug": false, 22 | "notifiedOfFantasy": false, 23 | "hideConditionHelp": false, 24 | "alwaysImport": false, 25 | "defaultLayoutsIntegrated": true, 26 | "atomicWrite": true 27 | } -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/pad.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: pad Helper 6 | --- 7 | 8 | This uses a simple query to help show what the pad helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'something to render in a code block. ' AS code 16 | template: | 17 | {{#each result}} 18 | {{pad 'text'}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'something to render in a code block. ' AS code 28 | template: | 29 | {{#each result}} 30 | {{pad 'text'}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/notelink.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: noteLink Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `noteLink`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'notepages/school/My Cool Page' AS link 16 | template: | 17 | {{#each result}} 18 | {{noteLink link}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'notepages/school/My Cool Page' AS link 28 | template: | 29 | {{#each result}} 30 | {{noteLink link}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Lowercase.ts: -------------------------------------------------------------------------------- 1 | /* 2 | // >> id='docs-handlebars-helper-lowercase' options='file=templates/hb-helpers/lowercase.md' 3 | title: lowercase value 4 | --- 5 | The `lowercase`\-helper will convert the entire string to lowercase. 6 | 7 | ```handlebars 8 | {{{lowercase sentence}}} 9 | ``` 10 | 11 | when used with this context: 12 | 13 | ```json 14 | { 15 | sentence: "This Is Some SENtence" 16 | } 17 | ``` 18 | 19 | will result in: 20 | 21 | ```text 22 | this is some sentence 23 | ``` 24 | 25 | // << docs-handlebars-helper-lowercase 26 | */ 27 | export function lowercase(value: string) { 28 | return typeof value === 'string' ? value.toLowerCase() : typeof (value as any); 29 | } 30 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Uppercase.ts: -------------------------------------------------------------------------------- 1 | /* 2 | // >> id='docs-handlebars-helper-lowercase' options='file=templates/hb-helpers/lowercase.md' 3 | title: lowercase value 4 | --- 5 | # {{ $frontmatter.title }} 6 | 7 | The `lowercase`\-helper will convert the entire string to lowercase. 8 | 9 | ```handlebars 10 | {{{lowercase sentence}}} 11 | ``` 12 | 13 | when used with this context: 14 | 15 | ```json 16 | { 17 | sentence: "This Is Some SENtence" 18 | } 19 | ``` 20 | 21 | will result in: 22 | 23 | ```text 24 | this is some sentence 25 | ``` 26 | 27 | // << docs-handlebars-helper-lowercase 28 | */ 29 | export function uppercase(value: string) { 30 | return value ? value.toUpperCase() : value; 31 | } 32 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/htmltasklist.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: htmlTaskList Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `htmlTaskList`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'something to render in a code block. ' AS code 16 | template: | 17 | {{#each result}} 18 | {{htmlTaskList 'text'}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'something to render in a code block. ' AS code 28 | template: | 29 | {{#each result}} 30 | {{htmlTaskList 'text'}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/taskcheckbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: taskCheckbox Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `taskCheckbox`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'something to render in a code block. ' AS code 16 | template: | 17 | {{#each result}} 18 | {{taskCheckbox 'text'}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'something to render in a code block. ' AS code 28 | template: | 29 | {{#each result}} 30 | {{taskCheckbox 'text'}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm 2 | node_modules 3 | 4 | # build 5 | *.js.map 6 | .log 7 | 8 | # obsidian 9 | /dist/main.js 10 | /dist/data.json 11 | /dist/manifest.json 12 | # If the folder is symlinked make sure this is not checked in. 13 | /dist/.hotreload 14 | 15 | # osx 16 | .DS_Store 17 | 18 | # Store the info about processed files in order to only operate on the changed ones. 19 | .eslintcache 20 | 21 | docs/_site 22 | coverage 23 | .nyc_output 24 | 25 | src/**/*.js 26 | ts-jest.log 27 | .github/actions/obsidian-vault-to-jekyll-markdown/__pycache__/**/* 28 | .jekyll-cache 29 | docs/.vitepress/cache 30 | docs/.vitepress/dist 31 | src/UI/alaSqlFunctions.json 32 | src/UI/communityQueries.json 33 | src/UI/handlebarsHelpers.json 34 | -------------------------------------------------------------------------------- /docs/data-tables/legacy-tables/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 100 3 | 4 | title: Legacy Tables 5 | has_children: true 6 | --- 7 | These tables are legacy and will be retired / removed. 8 | 9 | | Table Name | Description | 10 | | -------------------------------------------------- | ------------------------------------------------- | 11 | | [obsidian_markdown_notes](obsidian-markdown-notes) | This table is being retired, use `obsidian_notes` | 12 | | [obsidian_markdown_lists](obsidian-markdown-lists) | This table is being retired, use `obsidian_lists` | 13 | | [obsidian_markdown_tasks](obsidian-markdown-tasks) | This table is being retired, use `obsidian_tasks` | 14 | -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | --- 4 | 5 | This is a plugin for Obsidian that allows you to query internal data and render it anyway you want, it is designed to be extensible and flexible at the cost of increased complexity. 6 | 7 | The main query language is SQL using Alasql as the engine. This allows the user to write queries against arrays, objects and object collections. 8 | 9 | For rendering handlebars is used as the default render with some additional helpers specific to obsidian. 10 | 11 | This is a work in progress and bound to have issues, please flag them and always feel free to contribute, as long as it looks good and will not break others it will probably go straight in. I aim for low friction public collaboration. 12 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/obsidian.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: obsidian Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `obsidian`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'This is a **thing** to do' AS code 16 | template: | 17 | {{#each result}} 18 | {{{#obsidian inline="true"}}}{{{code}}}{{{/obsidian}}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'This is a **thing** to do' AS code 28 | template: | 29 | {{#each result}} 30 | {{{#obsidian inline="true"}}}{{{code}}}{{{/obsidian}}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/micromark.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: micromark Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `micromark`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'This is a **thing** to do' AS code 16 | template: | 17 | {{#each result}} 18 | {{{#micromark inline="true"}}}{{{code}}}{{{/micromark}}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'This is a **thing** to do' AS code 28 | template: | 29 | {{#each result}} 30 | {{{#micromark inline="true"}}}{{{code}}}{{{/micromark}}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/flexibletaskcheckbox.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: flexibleTaskCheckbox Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `flexibleTaskCheckbox`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'something to render in a code block. ' AS code 16 | template: | 17 | {{#each result}} 18 | {{flexibleTaskCheckbox 'text'}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'something to render in a code block. ' AS code 28 | template: | 29 | {{#each result}} 30 | {{flexibleTaskCheckbox 'text'}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | * **Please check if the PR fulfills these requirements** 2 | - [ ] The commit message follows our guidelines 3 | - [ ] Tests for the changes have been added (for bug fixes / features) 4 | - [ ] Docs have been added / updated (for bug fixes / features) 5 | 6 | 7 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 8 | 9 | 10 | 11 | * **What is the current behavior?** (You can also link to an open issue here) 12 | 13 | 14 | 15 | * **What is the new behavior (if this is a feature change)?** 16 | 17 | 18 | 19 | * **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?) 20 | 21 | 22 | 23 | * **Other information**: 24 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/taskcheckboxwithappend.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: taskCheckboxWithAppend Helper 6 | --- 7 | 8 | This uses a simple query to help show what the `taskCheckboxWithAppend`-helper does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'something to render in a code block. ' AS code 16 | template: | 17 | {{#each result}} 18 | {{taskCheckboxWithAppend 'text'}} 19 | {{/each}} 20 | ``` 21 | ```` 22 | 23 | ### Live in Vault 24 | 25 | ```qatt 26 | query: | 27 | SELECT 'something to render in a code block. ' AS code 28 | template: | 29 | {{#each result}} 30 | {{taskCheckboxWithAppend 'text'}} 31 | {{/each}} 32 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-functions/stringify.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: stringify(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `stringify` function will convert the provided value to a JSON string. 10 | 11 | In this example it takes the `stat` property of the first note found and renders it as a JSON blob. This can be handy to explore objects if you are not sure what is available. 12 | 13 | ````markdown 14 | ```qatt 15 | query: SELECT TOP 1 stringify(stat) AS statPropertyAsJsonString FROM obsidian_notes 16 | template: | 17 | {{#each result}}{{statPropertyAsJsonString}}{{/each}} 18 | ``` 19 | ```` 20 | 21 | will result in: 22 | 23 | ```text 24 | {"ctime":1670345758620,"mtime":1670345758620,"size":316} 25 | ``` -------------------------------------------------------------------------------- /ophidian.config.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import Builder from '@ophidian/build'; 3 | import yaml from 'js-yaml'; 4 | 5 | // Transform YAML files to JSON so the editor menu will provide examples. 6 | const yamlFiles = ['alaSqlFunctions', 'communityQueries', 'handlebarsHelpers']; 7 | 8 | for (const file of yamlFiles) { 9 | const inputYML = `src/UI/${file}.yaml`; 10 | const outputJSON = `src/UI/${file}.json`; 11 | const object = yaml.load(fs.readFileSync(inputYML, {encoding: 'utf8'})); 12 | fs.writeFileSync(outputJSON, JSON.stringify(object, null, 2)); 13 | } 14 | 15 | new Builder('src/main.ts') // <-- the path of your main module 16 | .withSass() // Could be omitted 17 | .withInstall() // Optional: publish to OBSIDIAN_TEST_VAULT on build 18 | .build(); 19 | -------------------------------------------------------------------------------- /docs/.obsidian/core-plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "file-explorer": true, 3 | "global-search": true, 4 | "switcher": true, 5 | "graph": false, 6 | "backlink": true, 7 | "canvas": false, 8 | "outgoing-link": true, 9 | "tag-pane": true, 10 | "page-preview": true, 11 | "daily-notes": false, 12 | "templates": false, 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": true 30 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "davidanson.vscode-markdownlint", 5 | "editorconfig.editorconfig", 6 | "pkief.material-icon-theme", 7 | "visualstudioexptteam.intellicode-api-usage-examples", 8 | "ms-vscode.powershell", 9 | "streetsidesoftware.code-spell-checker", 10 | "henryhediff.obsidian-code", 11 | "brpaz.vscode-obsidianmd", 12 | "nhoizey.gremlins", 13 | "dotup.dotup-vscode-interface-generator", 14 | "oouo-diogo-perdigao.docthis", 15 | "stkb.rewrap", 16 | "stackbreak.comment-divider", 17 | "yzhang.markdown-all-in-one", 18 | "github.vscode-github-actions", 19 | "vivaxy.vscode-conventional-commits", 20 | "hbenl.vscode-test-explorer", 21 | "orta.vscode-jest" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /docs/.obsidian/core-plugins-migration.json: -------------------------------------------------------------------------------- 1 | { 2 | "file-explorer": true, 3 | "global-search": true, 4 | "switcher": true, 5 | "graph": false, 6 | "backlink": true, 7 | "canvas": false, 8 | "outgoing-link": true, 9 | "tag-pane": true, 10 | "page-preview": true, 11 | "daily-notes": false, 12 | "templates": false, 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": true 30 | } -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/capitalize.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: Capitalize Helper 6 | --- 7 | 8 | This uses a simple query to help show what the Capitalize helper does when rendering. 9 | 10 | ### Example 11 | ````markdown 12 | ```qatt 13 | query: | 14 | SELECT 'word' AS LowerCaseWord 15 | template: | 16 | {{#each result}} 17 | Value from result: **{{LowerCaseWord}}** 18 | Value using capitalize: **{{capitalize LowerCaseWord}}** 19 | {{/each}} 20 | ``` 21 | ```` 22 | ### Live in Vault 23 | ```qatt 24 | query: | 25 | SELECT 'word' AS LowerCaseWord 26 | template: | 27 | {{#each result}} 28 | Value from result: **{{LowerCaseWord}}** 29 | Value using capitalize: **{{capitalize LowerCaseWord}}** 30 | {{/each}} 31 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/codeblockfooter.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: codeBlockFooter Helper 6 | --- 7 | 8 | This uses a simple query to help show what the codeBlockFooter helper does when rendering. 9 | 10 | ### Example 11 | ````markdown 12 | ```qatt 13 | query: | 14 | SELECT 'something to render in a code block. ' AS code 15 | template: | 16 | {{#each result}} 17 | {{codeBlockHeader 'text'}} 18 | {{code}} 19 | {{codeBlockFooter}} 20 | {{/each}} 21 | ``` 22 | ```` 23 | ### Live in Vault 24 | ```qatt 25 | query: | 26 | SELECT 'something to render in a code block. ' AS code 27 | template: | 28 | {{#each result}} 29 | {{codeBlockHeader 'text'}} 30 | {{code}} 31 | {{codeBlockFooter}} 32 | {{/each}} 33 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/codeblockheader.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: codeBlockHeader Helper 6 | --- 7 | 8 | This uses a simple query to help show what the codeBlockHeader helper does when rendering. 9 | 10 | ### Example 11 | ````markdown 12 | ```qatt 13 | query: | 14 | SELECT 'something to render in a code block. ' AS code 15 | template: | 16 | {{#each result}} 17 | {{codeBlockHeader 'text'}} 18 | {{code}} 19 | {{codeBlockFooter}} 20 | {{/each}} 21 | ``` 22 | ```` 23 | ### Live in Vault 24 | ```qatt 25 | query: | 26 | SELECT 'something to render in a code block. ' AS code 27 | template: | 28 | {{#each result}} 29 | {{codeBlockHeader 'text'}} 30 | {{code}} 31 | {{codeBlockFooter}} 32 | {{/each}} 33 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/obsidianhtmlinternallink.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: obsidianHtmlInternalLink Helper 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | This uses a simple query to help show what the obsidianHtmlInternalLink helper does when rendering. 10 | 11 | ### Example 12 | 13 | ````markdown 14 | ```qatt 15 | query: | 16 | SELECT TOP 1 path, basename FROM obsidian_notes 17 | template: | 18 | {{#each result}} 19 | {{obsidianHtmlInternalLink path basename}} 20 | {{/each}} 21 | ``` 22 | ```` 23 | 24 | ### Live in Vault 25 | 26 | ```qatt 27 | query: | 28 | SELECT TOP 1 path, basename FROM obsidian_notes 29 | template: | 30 | {{#each result}} 31 | {{obsidianHtmlInternalLink path basename}} 32 | {{/each}} 33 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/capitalize.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: capitalize 6 | --- 7 | 8 | # {{ $frontmatter.title }} 9 | 10 | This uses a simple query to help show what the Capitalize helper does when rendering. 11 | 12 | ### Example 13 | ````markdown 14 | ```qatt 15 | query: | 16 | SELECT 'word' AS LowerCaseWord 17 | template: | 18 | {{#each result}} 19 | Value from result: **{{LowerCaseWord}}** 20 | Value using capitalize: **{{capitalize LowerCaseWord}}** 21 | {{/each}} 22 | ``` 23 | ```` 24 | ### Live in Vault 25 | ```qatt 26 | query: | 27 | SELECT 'word' AS LowerCaseWord 28 | template: | 29 | {{#each result}} 30 | Value from result: **{{LowerCaseWord}}** 31 | Value using capitalize: **{{capitalize LowerCaseWord}}** 32 | {{/each}} 33 | ``` -------------------------------------------------------------------------------- /src/Interfaces/Settings.ts: -------------------------------------------------------------------------------- 1 | import {type FeatureFlag} from 'Settings/Feature'; 2 | 3 | type SettingsMap = Record; 4 | 5 | type HeadingState = Record; 6 | 7 | /** 8 | * Logging options structure. 9 | * 10 | * @export 11 | * @interface ILogOptions 12 | */ 13 | export interface ILogOptions { 14 | minLevels: Record; 15 | } 16 | 17 | export interface ISettings { 18 | 19 | // Collection of feature flag IDs and their state. 20 | features: FeatureFlag; 21 | 22 | // Settings are moved to a more general map to allow the settings UI to be 23 | // dynamically generated. 24 | generalSettings: SettingsMap; 25 | 26 | // Tracks the stage of the headings in the settings UI. 27 | headingOpened: HeadingState; 28 | 29 | loggingOptions: ILogOptions; 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist", // target for compiled files 4 | "baseUrl": "./src", 5 | "rootDir": "./src", 6 | "inlineSourceMap": true, 7 | "inlineSources": true, 8 | "module": "ESNext", 9 | "target": "ES6", 10 | "allowJs": true, 11 | "noImplicitAny": true, 12 | "moduleResolution": "node", 13 | "importHelpers": true, 14 | "isolatedModules": true, 15 | "strictNullChecks": true, 16 | "allowSyntheticDefaultImports": true, 17 | "resolveJsonModule": true, 18 | "esModuleInterop": true, 19 | "lib": [ 20 | "DOM", 21 | "ES5", 22 | "ES6", 23 | "ES7" 24 | ], 25 | "paths": { 26 | "src/*": [ 27 | "src/*" 28 | ] 29 | }, 30 | }, 31 | "include": [ 32 | "src/**/*.ts" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/in.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: IN 6 | --- 7 | 8 | Referenced From: 9 | 10 | Syntax: 11 | 12 | ```sql 13 | expression IN array 14 | ``` 15 | 16 | AlaSQL supports ```IN``` operation: 17 | 18 | ```sql 19 | SELECT path FROM obsidian_notes WHERE 'project' IN tags 20 | SELECT a FROM one WHERE b IN (1,2,3) 21 | SELECT a FROM one WHERE b IN (SELECT b FROM two) 22 | SELECT a FROM one WHERE b IN ? 23 | ``` 24 | 25 | ### In the expression 26 | 27 | ```js 28 | expression IN @(expression) 29 | ``` 30 | 31 | like: 32 | 33 | ```sql 34 | SELECT path FROM obsidian_notes WHERE frontmatter->area IN @('area1','area2') 35 | 10 IN @(?) 36 | @a IN @(@b) 37 | 20 IN @(@[10,20,30]) 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/pageproperty.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: pageProperty 6 | --- 7 | 8 | ## Syntax 9 | 10 | ```sql 11 | pageProperty ( string ) 12 | ``` 13 | 14 | ## Returns 15 | 16 | Returns the value of the front matter field specified from the page the query is running on. 17 | 18 | ## Example 19 | 20 | The following example returns the frontmatter->status value reversed. 21 | 22 | ```yaml 23 | query: | 24 | SELECT pageProperty('task_status') AS TaskStatus 25 | template: | 26 | {{#each result}} 27 | The page property 'task_status' is set to {{TaskStatus}} 28 | {{/each}} 29 | ``` 30 | 31 | ```text 32 | The page property 'task_status' is set to x 33 | 34 | ``` 35 | 36 | Open these pages in an Obsidian vault and view 'Examples\using-pageproperty-simple-live.md' for a live example. -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 3 3 | title: 🚀 Installation 4 | --- 5 | 6 | # Automatically via Community Plugins 7 | 8 | In Obsidian: 9 | 10 | 1. Open Settings > Community plugins 11 | 2. Make sure Safe mode is off 12 | 3. Click Browse community plugins 13 | 4. Search for "Query All the Things" 14 | 5. Click Install 15 | 16 | Once installed, close the community plugins window and activate the newly installed plugin 17 | 18 | > [!NOTE] 19 | > If you want to use the dataview integration please make sure it is installed. Without it the `dataview_pages`, `dataview_tasks` and `dataview_lists` tables will be empty. 20 | 21 | # Manually 22 | 23 | Copy the main.js, style.css and manifest.json file from the latest release on [sytone/obsidian-queryallthethings:](https://github.com/sytone/obsidian-queryallthethings) to a folder called qatt in the plugins directory. 24 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/if-then-else.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: IF ... THEN ... ELSE 6 | --- 7 | 8 | Syntax: 9 | 10 | ```sql 11 | IF expression THEN statement1 [ELSE statement2] 12 | ``` 13 | 14 | Obsidian Example: 15 | 16 | ```sql 17 | SELECT 18 | IIF( frontmatter->status = 'Done', '✔️', '⏹️') AS StatusEmoji 19 | FROM obsidian_notes WHERE 20 | ``` 21 | 22 | For example: 23 | 24 | ```sql 25 | IF EXISTS(SELECT * FROM one WHERE a=10) 26 | UPDATE one SET b = 200 WHERE a=10 27 | ELSE 28 | INSERT INTO one VALUES (5,500); 29 | ``` 30 | 31 | Alternative Syntax: 32 | 33 | MySQL notation 34 | 35 | ```sql 36 | SELECT IF(1>2,2,3); 37 | ``` 38 | 39 | Will return 3 40 | 41 | or the MsSQL notation 42 | 43 | ```sql 44 | SELECT IIF(1>2,2,3); 45 | ``` 46 | 47 | Will return 3 48 | -------------------------------------------------------------------------------- /.config/commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | 4 | rules: { 5 | 'body-case': [2, 'always', 'sentence-case'], 6 | 'body-max-line-length': [1, 'always', 72], 7 | 'header-max-length': [1, 'always', 52], 8 | 'type-enum': [ 9 | 2, 10 | 'always', 11 | [ 12 | 'build', 13 | 'change', 14 | 'chore', 15 | 'ci', 16 | 'deprecate', 17 | 'docs', 18 | 'feat', 19 | 'fix', 20 | 'perf', 21 | 'refactor', 22 | 'remove', 23 | 'revert', 24 | 'security', 25 | 'style', 26 | 'test', 27 | ], 28 | ], 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/Query/Functions/Rand.ts: -------------------------------------------------------------------------------- 1 | import alasql from "alasql"; 2 | 3 | /* 4 | // >> id='alasql-function-rand' options='file=queries/sql-functions/rand.md' 5 | title: rand() 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `rand()` function returns a random floating-point number between 0 (inclusive) and 1 (exclusive). 10 | 11 | ## Syntax 12 | ```sql 13 | SELECT RAND() 14 | ``` 15 | 16 | ## Examples 17 | ```sql 18 | -- Generate a random number 19 | SELECT RAND() AS random_value 20 | 21 | -- Generate 5 random numbers 22 | SELECT RAND() AS random_value FROM (VALUES(1),(2),(3),(4),(5)) AS t(n) 23 | 24 | -- Use in a calculation (random number between 1 and 100) 25 | SELECT ROUND(RAND() * 99 + 1) AS random_1_to_100 26 | ``` 27 | // << alasql-function-rand 28 | */ 29 | export function registerFunctionRand(): void { 30 | alasql.fn.RAND = function () { 31 | return Math.random(); 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /docs/verfication/simple-queries.md: -------------------------------------------------------------------------------- 1 | --- 2 | exclude: true 3 | --- 4 | 5 | **SELECT TOP 5 * FROM obsidian_notes ORDER BY modified DESC** 6 | ```qatt 7 | query: | 8 | SELECT TOP 5 * FROM obsidian_notes ORDER BY modified DESC 9 | template: | 10 | {{#each result}} 11 | [[{{basename}}]] - Last Updated: {{formatDate modified}} 12 | {{/each}} 13 | ``` 14 | 15 | **Select top 5 and bottom five using multiple queries** 16 | ```qatt 17 | query: | 18 | SELECT TOP 5 * FROM obsidian_notes ORDER BY modified DESC; 19 | SELECT TOP 5 * FROM obsidian_notes ORDER BY modified ASC; 20 | template: | 21 | Result[0] 22 | {{#each result.[0]}} 23 | [[{{basename}}]] - Last Updated: {{formatDate modified}} 24 | {{/each}} 25 | Result[1] 26 | {{#each result.[1]}} 27 | [[{{basename}}]] - Last Updated: {{formatDate modified}} 28 | {{/each}} 29 | ``` 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/joinarray.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: JoinArray 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | JoinArray ( dataArray [ , separator ] ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *dataArray* 18 | An array of strings to be joined. 19 | 20 | *separator* 21 | A string to be used as a separator. If omitted, the array elements are not separated with anything. 22 | 23 | ## Returns 24 | 25 | A string that is the result of concatenating the array elements. 26 | 27 | ## Example 28 | 29 | The following example finds the location of the word `string` in the string `This is the string to search`. 30 | 31 | ```yaml 32 | query: | 33 | SELECT JoinArray(@['Hello','World','!']) AS JoinArrayValue 34 | template: | 35 | {{stringify result}} 36 | ``` 37 | 38 | ```json 39 | [ { "JoinArrayValue": "HelloWorld!" } ] 40 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/micromark.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: micromark 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `micromark`-helper renders markdown as HTML using the micromark library. It has one setting which will remove the wrapping `

` tag from the output if inline is set to true. 10 | 11 | ```handlebars 12 | {{{#micromark inline="true"}}} {{{task}}} [[{{{page}}}|📝]] {{{/micromark}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | task: "This is a **thing** to do", 20 | page: "folder/SomePage.md" 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | This is a thing to do 28 | ```` 29 | 30 | If the inline property is not set, then the output will be wrapped in a `

` tag and result in: 31 | 32 | ``` 33 |

This is a thing to do

34 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/obsidian.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: obsidian 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `obsidian`-helper renders markdown as HTML using the obsidian markdown engine. It has one setting which will remove the wrapping `

` tag from the output if inline is set to true. 10 | 11 | ```handlebars 12 | {{{#obsidian inline="true"}}} {{{task}}} [[{{{page}}}|📝]] {{{/obsidian}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | task: "This is a **thing** to do", 20 | page: "folder/SomePage.md" 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | This is a thing to do 28 | ```` 29 | 30 | If the inline property is not set, then the output will be wrapped in a `

` tag and result in: 31 | 32 | ``` 33 |

This is a thing to do

34 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-statements/extractline.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: EXTRACTLINE 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | EXTRACTLINE ( line_number, stringToExtractLineFrom ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *line_number* 18 | A line number to extract from the string. 19 | 20 | *stringToExtractLineFrom* 21 | The string to extract the line from. 22 | 23 | ## Returns 24 | 25 | Returns a string containing the line at the specified line number. Empty string if the line number is invalid. 26 | 27 | ## Example 28 | 29 | The following example returns the third line from the string `This\nis\nthe\nstring\nto\nuse`. 30 | 31 | ```yaml 32 | query: | 33 | SELECT EXTRACTLINE(3, 'This\nis\nthe\nstring\nto\nuse') AS LineContents 34 | template: | 35 | {{stringify result}} 36 | ``` 37 | 38 | ```json 39 | [ { "LineContents": "the" } ] 40 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-statements/reverse.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: REVERSE 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | REVERSE ( string ) 13 | ``` 14 | 15 | ## Returns 16 | 17 | Returns the string with the characters in reverse order. 18 | 19 | ## Example 20 | 21 | The following example returns the frontmatter->status value reversed. 22 | 23 | ```yaml 24 | query: | 25 | SELECT TOP 2 path AS Path, 26 | REVERSE(frontmatter->status) AS Status 27 | FROM obsidian_notes 28 | WHERE frontmatter->status 29 | template: | 30 | {{stringify result}} 31 | ``` 32 | 33 | ```json 34 | [ 35 | { 36 | "path": "Projects - Demo Project/Why You Should Be Taking More Notes.md", 37 | "Status": "gnioD" 38 | }, { 39 | "path": "Projects - Demo Project/What I Learned From Taking 15,000 Notes.md", 40 | "Status": "golkcaB" 41 | } 42 | ] 43 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/obsidianhtmlinternallink.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: obsidianHtmlInternalLink 6 | --- 7 | 8 | The `obsidianHtmlInternalLink` helper takes a path and a label and returns a link 9 | matches the default HTML that obsidian uses for internal links. If ths files ends 10 | in `.md` then the extension is removed from the link. 11 | 12 | ```handlebars 13 | {{{obsidianHtmlInternalLink internalPath basename}}} 14 | ``` 15 | 16 | when used with this context: 17 | 18 | ```json 19 | { 20 | path: "notepages/school/My Cool Page", 21 | name: "My Cool Page is here!" 22 | } 23 | ``` 24 | 25 | will result in: 26 | 27 | ```html 28 | My Cool Page is here! 29 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/stringify.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: stringify value 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `stringify`\-helper will convert the referenced object to a JSON string. If a cyclical object is used the helper will return `[Cyclical]` for the nested instances to prevent infinite recursion. 10 | 11 | ```handlebars 12 | {{{stringify complexObject}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | complexObject: { 20 | "this": "is", 21 | "a": "complex", 22 | "object": "with", 23 | "lots": "of", 24 | "things": "in", 25 | "it": "!" 26 | } 27 | } 28 | ``` 29 | 30 | will result in: 31 | 32 | ```text 33 | { 34 | complexObject: { 35 | "this": "is", 36 | "a": "complex", 37 | "object": "with", 38 | "lots": "of", 39 | "things": "in", 40 | "it": "!" 41 | } 42 | } 43 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-functions/parsewikilinkdisplayname.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: parseWikiLinkDisplayName(string) 6 | --- 7 | There are multiple functions to help with the parsing of the wiki links in a string. 8 | 9 | This is the signature of the function you can use to extract the display name from the wiki link. 10 | 11 | - parseWikiLinkDisplayName(value: string): string 12 | 13 | In this example it takes the string containing the wiki link and calls the different parsing functions. 14 | 15 | ```text 16 | Need to work on [[Projects/Painting The House|Painting The House]] soon. 17 | ``` 18 | 19 | ````markdown 20 | ```qatt 21 | query: | 22 | SELECT 23 | parseWikiLinkDisplayName('Need to work on [[Projects/Painting The House|Painting The House]] soon.') AS Display 24 | template: | 25 | {{stringify result}} 26 | ``` 27 | ```` 28 | 29 | will result in: 30 | 31 | ```text 32 | [ { "Display": "Painting The House" } ] 33 | ``` -------------------------------------------------------------------------------- /docs/templates/hb-helpers/relationaloperators.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars Helpers 4 | grand_parent: Using Templates 5 | title: Relational Operators 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The Relational Operators helpers will allow you to compare two values using different relational operators. 10 | 11 | ## Operators 12 | 13 | - eq - equal to 14 | - ne - not equal to 15 | - lt - less than 16 | - gt - greater than 17 | - le - less than or equal to 18 | - ge - greater than or equal to 19 | 20 | With any helper add values a, b to be compared if their condition validates 21 | 22 | ## Usage 23 | 24 | Include the operators needed in your application. Then condition in your template, for example: 25 | 26 | ```handlebars 27 | {{#eq a b}} 28 | content when condition validates 29 | {{else}} 30 | content in any other case 31 | {{/eq}} 32 | ``` 33 | 34 | ### Credits 35 | 36 | Created by [Makis Tracend](http://github.com/tracend) 37 | Sourced from: https://gist.github.com/tracend/7522125 -------------------------------------------------------------------------------- /docs/.obsidian/plugins/dataview/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "renderNullAs": "\\-", 3 | "taskCompletionTracking": false, 4 | "taskCompletionUseEmojiShorthand": false, 5 | "taskCompletionText": "completion", 6 | "taskCompletionDateFormat": "yyyy-MM-dd", 7 | "recursiveSubTaskCompletion": false, 8 | "warnOnEmptyResult": true, 9 | "refreshEnabled": true, 10 | "refreshInterval": 2500, 11 | "defaultDateFormat": "MMMM dd, yyyy", 12 | "defaultDateTimeFormat": "h:mm a - MMMM dd, yyyy", 13 | "maxRecursiveRenderDepth": 4, 14 | "tableIdColumnName": "File", 15 | "tableGroupColumnName": "Group", 16 | "showResultCount": true, 17 | "allowHtml": true, 18 | "inlineQueryPrefix": "=", 19 | "inlineJsQueryPrefix": "$=", 20 | "inlineQueriesInCodeblocks": true, 21 | "enableInlineDataview": true, 22 | "enableDataviewJs": true, 23 | "enableInlineDataviewJs": false, 24 | "prettyRenderInlineFields": true, 25 | "prettyRenderInlineFieldsInLivePreview": true, 26 | "dataviewJsKeyword": "dataviewjs" 27 | } -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.1.0": "1.2.8", 3 | "0.2.3": "1.2.8", 4 | "0.2.4": "1.2.8", 5 | "0.2.5": "1.2.8", 6 | "0.2.6": "1.2.8", 7 | "0.3.0": "1.2.8", 8 | "0.4.0": "1.2.8", 9 | "0.4.1": "1.2.8", 10 | "0.4.2": "1.2.8", 11 | "0.4.3": "1.2.8", 12 | "0.4.4": "1.2.8", 13 | "0.5.0": "1.2.8", 14 | "0.5.1": "1.2.8", 15 | "0.5.2": "1.2.8", 16 | "0.5.3": "1.2.8", 17 | "0.5.4": "1.2.8", 18 | "0.6.0": "1.2.8", 19 | "0.7.0": "1.2.8", 20 | "0.7.1": "1.2.8", 21 | "0.8.0": "1.2.8", 22 | "0.8.1": "1.2.8", 23 | "0.8.2": "1.2.8", 24 | "0.8.3": "1.2.8", 25 | "0.8.4": "1.2.8", 26 | "0.8.5": "1.2.8", 27 | "0.8.6": "1.2.8", 28 | "0.8.7": "1.2.8", 29 | "0.8.8": "1.2.8", 30 | "0.8.9": "1.2.8", 31 | "0.8.10": "1.2.8", 32 | "0.9.0": "1.2.8", 33 | "0.10.0": "1.2.8", 34 | "1.0.0": "1.2.8", 35 | "1.0.1": "1.2.8", 36 | "1.0.2": "1.2.8", 37 | "1.1.0": "1.2.8", 38 | "1.1.1": "1.2.8", 39 | "1.1.2": "1.2.8", 40 | "1.1.3": "1.2.8", 41 | "1.2.0": "1.2.8", 42 | "1.2.1": "1.2.8", 43 | "1.3.0": "1.7.2" 44 | } -------------------------------------------------------------------------------- /docs/queries/sql-functions/updatepropertyfromlist.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: updatePropertyFromList(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `updatePropertyFromList` function will return a link that when clicked will prompt the user to select a value from a list. When the user selects a value the property will be updated on the file. 10 | 11 | ````markdown 12 | ```qatt 13 | query: SELECT TOP 1 updatePropertyFromList(frontmatter->priority, path, @[1, 2, 3], 'priority') AS updatePriority FROM obsidian_notes 14 | template: | 15 | {{#each result}}{{updatePriority}}{{/each}} 16 | ``` 17 | ```` 18 | 19 | will result in: 20 | 21 | ```text 22 | 3 23 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-functions/parsewikilinklocation.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: parseWikiLinkLocation(string) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | There are multiple functions to help with the parsing of the wiki links in a string. 10 | 11 | This is the signature of the function you can use to extract the location from the wiki link. 12 | 13 | - parseWikiLinkLocation(value: string): string 14 | 15 | In this example it takes the string containing the wiki link and calls the different parsing functions. 16 | 17 | ```text 18 | Need to work on [[Projects/Painting The House|Painting The House]] soon. 19 | ``` 20 | 21 | ````markdown 22 | ```qatt 23 | query: | 24 | SELECT 25 | parseWikiLinkLocation('Need to work on [[Projects/Painting The House|Painting The House]] soon.') AS Location 26 | template: | 27 | {{stringify result}} 28 | ``` 29 | ```` 30 | 31 | will result in: 32 | 33 | ```text 34 | [ { "Location": "Projects/Painting The House" } ] 35 | ``` -------------------------------------------------------------------------------- /docs/data-tables/dataview/dataview-lists.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 26 3 | 4 | parent: Data Tables 5 | title: Dataview Lists 6 | --- 7 | # Dataview Lists (dataview_lists) 8 | 9 | If a property is not found in the task body it will be set to undefined. This table 10 | is backed by Dataview and will be refreshed when Dataview is refreshed. 11 | 12 | | Column Name | Type | Description | 13 | | ----------- | ------------ | ----------- | 14 | | symbol | string | | 15 | | path | string | | 16 | | pageName | string | | 17 | | text | number | | 18 | | line | string array | | 19 | | fields | string array | | 20 | | lineCount | string | | 21 | | list | string | | 22 | | section | string | | 23 | | links | string | | 24 | | children | string | | 25 | | parent | number | | 26 | 27 | -------------------------------------------------------------------------------- /.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 | - Obsidian Version [e.g. 1.2.8] 29 | - Plugin Version [e.g. 0.10.0] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Obsidian Version [e.g. 1.2.8] 35 | - Plugin Version [e.g. 0.10.0] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/Query/Functions/Stringify.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='alasql-function-stringify-snippet' options='file=queries/sql-functions/stringify.md' 5 | title: stringify(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `stringify` function will convert the provided value to a JSON string. 10 | 11 | In this example it takes the `stat` property of the first note found and renders it as a JSON blob. This can be handy to explore objects if you are not sure what is available. 12 | 13 | ````markdown 14 | ```qatt 15 | query: SELECT TOP 1 stringify(stat) AS statPropertyAsJsonString FROM obsidian_notes 16 | template: | 17 | {{#each result}}{{statPropertyAsJsonString}}{{/each}} 18 | ``` 19 | ```` 20 | 21 | will result in: 22 | 23 | ```text 24 | {"ctime":1670345758620,"mtime":1670345758620,"size":316} 25 | ``` 26 | // << alasql-function-stringify-snippet 27 | */ 28 | export function registerFunctionStringify(): void { 29 | alasql.fn.stringify = function (value) { 30 | return JSON.stringify(value); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/Render/RenderFactory.ts: -------------------------------------------------------------------------------- 1 | /* eslint indent: [2, 2, {"SwitchCase": 1}] */ 2 | import {type IQattCodeBlock} from 'QattCodeBlock'; 3 | import {type IRenderer} from 'Render/IRenderer'; 4 | import {HandlebarsRenderer} from 'Render/HandlebarsRenderer'; 5 | import {TextRenderer} from 'Render/TextRenderer'; 6 | import {Service} from '@ophidian/core'; 7 | import {LoggingService} from 'lib/LoggingService'; 8 | 9 | export class RenderFactory extends Service { 10 | logger = this.use(LoggingService).getLogger('Qatt.RenderFactory'); 11 | 12 | async onload() { 13 | this.logger.info('RenderFactory loaded'); 14 | } 15 | 16 | public async getRenderer(codeblockConfiguration: IQattCodeBlock): Promise { 17 | switch (codeblockConfiguration.renderEngine) { 18 | case 'handlebars': { 19 | return this.use(HandlebarsRenderer); 20 | } 21 | 22 | case 'text': { 23 | return this.use(TextRenderer); 24 | } 25 | 26 | default: { 27 | return this.use(HandlebarsRenderer); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/notePath.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: SQL Functions 3 | grand_parent: Writing Queries 4 | title: notePathWithFileExtension() 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `notePath()` function will return path to the current page, so if the file path in your vault is `folder/folder/my cool filename.md` this function will return `folder/folder`. 9 | 10 | Here are all the page functions to extract information from the current page the query is running on. 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT TOP 1 16 | notePathWithFileExtension() AS notePathWithFileExtension, 17 | notePathWithoutFileExtension() AS notePathWithoutFileExtension, 18 | notePath() AS notePath, 19 | noteFileName() AS noteFileName 20 | FROM obsidian_markdown_tasks 21 | template: | 22 | {{#each result}} 23 | notePathWithFileExtension: {{notePathWithFileExtension}} 24 | notePathWithoutFileExtension: {{notePathWithoutFileExtension}} 25 | notePath: {{notePath}} 26 | noteFileName: {{noteFileName}} 27 | {{/each}} 28 | ``` 29 | ```` 30 | -------------------------------------------------------------------------------- /docs/data-tables/dataview/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 50 3 | 4 | title: Dataview Tables 5 | has_children: true 6 | --- 7 | The following tables are exposed if you have the Dataview plugin installed. 8 | 9 | | Table Name | Description | 10 | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | 11 | | [dataview_pages](dataview-pages) | This is same as using pages as a data source in Dataview | 12 | | [dataview_tasks](dataview-tasks) | This is a in memory collection of tasks based off Dataview pages. Details below. | 13 | | [dataview_lists](dataview-lists) | This is a in memory collection of all list items including list items that contain task markdown based off DataView pages. Details Below | 14 | -------------------------------------------------------------------------------- /.github/workflows/verify.yml: -------------------------------------------------------------------------------- 1 | name: Verify Commit 2 | on: 3 | push: 4 | branches: ['*'] 5 | pull_request: 6 | branches: [main] 7 | jobs: 8 | validate: 9 | runs-on: ubuntu-latest 10 | steps: 11 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 12 | - uses: actions/checkout@v2 13 | 14 | - name: Use Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 20 18 | 19 | - uses: pnpm/action-setup@v2 20 | with: 21 | version: 9 22 | 23 | - name: Install modules 24 | run: pnpm install 25 | 26 | - name: Run build 27 | run: pnpm run build 28 | 29 | - name: Run TypeScript compiler ESLint 30 | run: pnpm run lint 31 | 32 | - name: Run mocha 33 | run: pnpm run test 34 | 35 | - name: Archive verified build 36 | uses: actions/upload-artifact@v3 37 | with: 38 | name: dist-verified 39 | path: | 40 | ./dist/main.js 41 | ./dist/manifest.json 42 | ./dist/styles.css 43 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Capitalize.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperDelegate, type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-examples-handlebars-capitalize' options='file=templates/hb-helpers/capitalize.md' 4 | title: capitalize 5 | --- 6 | 7 | # {{ $frontmatter.title }} 8 | 9 | This uses a simple query to help show what the Capitalize helper does when rendering. 10 | 11 | ### Example 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT 'word' AS LowerCaseWord 16 | template: | 17 | {{#each result}} 18 | Value from result: **{{LowerCaseWord}}** 19 | Value using capitalize: **{{capitalize LowerCaseWord}}** 20 | {{/each}} 21 | ``` 22 | ```` 23 | ### Live in Vault 24 | ```qatt 25 | query: | 26 | SELECT 'word' AS LowerCaseWord 27 | template: | 28 | {{#each result}} 29 | Value from result: **{{LowerCaseWord}}** 30 | Value using capitalize: **{{capitalize LowerCaseWord}}** 31 | {{/each}} 32 | ``` 33 | 34 | // << docs-examples-handlebars-capitalize 35 | */ 36 | export function capitalize(word: string) { 37 | return word.charAt(0).toUpperCase() + word.slice(1); 38 | } 39 | -------------------------------------------------------------------------------- /docs/queries/sql-statements/charindex.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: CHARINDEX 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | CHARINDEX ( expressionToFind , expressionToSearch [ , start_location ] ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *expressionToFind* 18 | A character expression containing the sequence to find. 19 | 20 | *expressionToSearch* 21 | A character expression to search. 22 | 23 | *start_location* 24 | An number at which the search starts. If start_location is not specified, has a negative value, or has a zero (0) value, the search starts at the beginning of expressionToSearch. 25 | 26 | ## Returns 27 | 28 | Returns a number indicating the position in the string. 29 | 30 | ## Example 31 | 32 | The following example finds the location of the word `string` in the string `This is the string to search`. 33 | 34 | ```yaml 35 | query: | 36 | SELECT CHARINDEX('string', 'This is the string to search') AS StringIndex 37 | template: | 38 | {{stringify result}} 39 | ``` 40 | 41 | ```json 42 | [ { "StringIndex": 13 } ] 43 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-functions/notePathWithFileExtension.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: SQL Functions 3 | grand_parent: Writing Queries 4 | title: notePathWithFileExtension() 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `notePathWithFileExtension()` function will return the current page including path and extension, so if the file path in your vault is `folder/folder/my cool filename.md` this function will return `folder/folder/my cool filename.md`. 9 | 10 | Here are all the page functions to extract information from the current page the query is running on. 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT TOP 1 16 | notePathWithFileExtension() AS notePathWithFileExtension, 17 | notePathWithoutFileExtension() AS notePathWithoutFileExtension, 18 | notePath() AS notePath, 19 | noteFileName() AS noteFileName 20 | FROM obsidian_markdown_tasks 21 | template: | 22 | {{#each result}} 23 | notePathWithFileExtension: {{notePathWithFileExtension}} 24 | notePathWithoutFileExtension: {{notePathWithoutFileExtension}} 25 | notePath: {{notePath}} 26 | noteFileName: {{noteFileName}} 27 | {{/each}} 28 | ``` 29 | ```` 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jon Bullen 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 | -------------------------------------------------------------------------------- /src/ReleaseNotes.ts: -------------------------------------------------------------------------------- 1 | import {Service} from '@ophidian/core'; 2 | import {LoggingService} from 'lib/LoggingService'; 3 | import {Plugin} from 'obsidian'; 4 | 5 | export interface IRelease { 6 | version: string; 7 | body: string; 8 | } 9 | 10 | export class ReleaseNotes extends Service { 11 | plugin = this.use(Plugin); 12 | logger = this.use(LoggingService).getLogger('Qatt.ReleaseNotes'); 13 | 14 | private readonly notes: Map; 15 | 16 | constructor() { 17 | super(); 18 | this.notes = new Map(); 19 | } 20 | 21 | addVersion(version: string, changes: string) { 22 | this.notes.set(version, changes); 23 | } 24 | 25 | getChangesSince(version: string): IRelease[] { 26 | const changes: IRelease[] = []; 27 | 28 | let foundVersion = false; 29 | for (const [key, value] of this.notes) { 30 | if (foundVersion) { 31 | changes.push({ 32 | version: key, 33 | body: value, 34 | }); 35 | } 36 | 37 | if (key === version) { 38 | foundVersion = true; 39 | } 40 | } 41 | 42 | return changes.reverse(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/api-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | exclude: true 4 | --- 5 | 6 | # Runtime API Examples 7 | 8 | This page demonstrates usage of some of the runtime APIs provided by VitePress. 9 | 10 | The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: 11 | 12 | ```md 13 | 18 | 19 | ## Results 20 | 21 | ### Theme Data 22 |
{{ theme }}
23 | 24 | ### Page Data 25 |
{{ page }}
26 | 27 | ### Page Frontmatter 28 |
{{ frontmatter }}
29 | ``` 30 | 31 | 36 | 37 | ## Results 38 | 39 | ### Theme Data 40 |
{{ theme }}
41 | 42 | ### Page Data 43 |
{{ page }}
44 | 45 | ### Page Frontmatter 46 |
{{ frontmatter }}
47 | 48 | ## More 49 | 50 | Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). 51 | -------------------------------------------------------------------------------- /docs/data-tables/legacy-tables/obsidian-markdown-files.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 100 3 | 4 | parent: Data Tables 5 | title: RETIRED - Obsidian Markdown Files 6 | --- 7 | ## Obsidian Markdown Files (obsidian_markdown_files) 8 | 9 | This has been retired, please use obsidian_notes instead. 10 | 11 | If you need to reference a property of a object do not forget to use `->` and not `.` 12 | 13 | | Column Name | Type | Description | 14 | | ----------- | ------ | ----------------------------------------------- | 15 | | path | string | Full path to the markdown file. | 16 | | name | string | The name of the file including the extension. | 17 | | basename | number | Just the name of the file. | 18 | | extension | number | The extension of the file. Usually `md` | 19 | | stat | object | contains the time and size details of the file. | 20 | | stat->ctime | number | Time the file was creates as a serial. | 21 | | stat->mtime | number | Time the file was last modified as a serial. | 22 | | stat->size | number | Size of the file in bytes | -------------------------------------------------------------------------------- /docs/queries/sql-statements/lineindex.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Statements 4 | grand_parent: Writing Queries 5 | title: LINEINDEX 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | LINEINDEX ( expressionToFind , expressionToSearch [ , start_location ] ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *expressionToFind* 18 | A character expression containing the sequence to find. 19 | 20 | *expressionToSearch* 21 | A character expression to search. 22 | 23 | *start_location* 24 | An line number at which the search starts. If start_location is not specified, has a negative value, or has a zero (0) value, the search starts at the beginning of expressionToSearch. 25 | 26 | ## Returns 27 | 28 | Returns a line number indicating the line in the string where the expression is found. 29 | 30 | ## Example 31 | 32 | The following example finds the location of the word `string` in the string `This is the string to search`. 33 | 34 | ```yaml 35 | query: | 36 | SELECT LINEINDEX('string', 'This is the\nstring to search') AS LineIndex 37 | template: | 38 | {{stringify result}} 39 | ``` 40 | 41 | ```json 42 | [ { "LineIndex": 1 } ] 43 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/listing-recently-updated-files-live.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Listing recently updated files 5 | exclude: true 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | This example will walk you though making a codeblock that lists the 10 most recently modified files. 10 | 11 | The query below will return the top 10 notes from the `obsidian_notes` table, it will order them by the last modified time (`stat->mtime`) in a descending (`DESC`) order. In addition to all the columns in the table it adds a new colum with the value set to a human readable string stating how long ago the modification was. This uses moment and parses the `mtime` and get the time since now. 12 | 13 | If you are using javascript objects instead of using the `.` when calling functions/properties off classes you use a `->` to address the function or property. 14 | 15 | ```qatt 16 | query: | 17 | SELECT TOP 10 *, moment(stat->mtime)->fromNow() AS LastModified 18 | FROM obsidian_notes 19 | ORDER BY stat->mtime DESC 20 | template: | 21 | {{#each result}} 22 | - [[{{path}}|{{basename}}]] was updated {{LastModified}} 23 | {{/each}} 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/notePathWithoutFileExtension.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: SQL Functions 3 | grand_parent: Writing Queries 4 | title: notePathWithFileExtension() 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `notePathWithoutFileExtension()` function will return path for the current page and the file name with out the extension, so if the file path in your vault is `folder/folder/my cool filename.md` this function will return `folder/folder/my cool filename`. 9 | 10 | Here are all the page functions to extract information from the current page the query is running on. 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT TOP 1 16 | notePathWithFileExtension() AS notePathWithFileExtension, 17 | notePathWithoutFileExtension() AS notePathWithoutFileExtension, 18 | notePath() AS notePath, 19 | noteFileName() AS noteFileName 20 | FROM obsidian_markdown_tasks 21 | template: | 22 | {{#each result}} 23 | notePathWithFileExtension: {{notePathWithFileExtension}} 24 | notePathWithoutFileExtension: {{notePathWithoutFileExtension}} 25 | notePath: {{notePath}} 26 | noteFileName: {{noteFileName}} 27 | {{/each}} 28 | ``` 29 | ```` 30 | -------------------------------------------------------------------------------- /docs/examples-tutorials/listing-recently-updated-files.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Listing recently updated files 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | This example will walk you though making a codeblock that lists the 10 most recently modified files. 9 | 10 | The query below will return the top 10 notes from the `obsidian_notes` table, it will order them by the last modified time (`stat->mtime`) in a descending (`DESC`) order. In addition to all the columns in the table it adds a new colum with the value set to a human readable string stating how long ago the modification was. This uses moment and parses the `mtime` and get the time since now. 11 | 12 | If you are using javascript objects instead of using the `.` when calling functions/properties off classes you use a `->` to address the function or property. 13 | 14 | ````markdown 15 | ```qatt 16 | query: | 17 | SELECT TOP 10 *, moment(stat->mtime)->fromNow() AS LastModified 18 | FROM obsidian_notes 19 | ORDER BY stat->mtime DESC 20 | template: | 21 | {{#each result}} 22 | - [[{{path}}|{{basename}}]] was updated {{LastModified}} 23 | {{/each}} 24 | ``` 25 | ```` 26 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "material-icon-theme.folders.theme": "specific", 3 | "cSpell.words": [ 4 | "alasql", 5 | "alwaysappend", 6 | "alwaysprepend", 7 | "charindex", 8 | "collab", 9 | "collapser", 10 | "columnid", 11 | "DATETIME", 12 | "devcontainers", 13 | "embedcache", 14 | "endraw", 15 | "frontmattercache", 16 | "gnio", 17 | "golkca", 18 | "headingcache", 19 | "lezer", 20 | "linkcache", 21 | "listcache", 22 | "listitem", 23 | "luxon", 24 | "mapname", 25 | "mdast", 26 | "micromark", 27 | "nocount", 28 | "outfile", 29 | "pagedata", 30 | "papaparse", 31 | "parsewikilinks", 32 | "Qatt", 33 | "Recordset", 34 | "sectioncache", 35 | "Sytone", 36 | "taskcheckbox", 37 | "useid" 38 | ], 39 | "[markdown]": { 40 | "editor.formatOnSave": true, 41 | "editor.formatOnPaste": true 42 | }, 43 | "conventionalCommits.scopes": [ 44 | "logging", 45 | "render", 46 | "query", 47 | "docs" 48 | ], 49 | "markdownlint.config": { 50 | "MD007": { 51 | "indent": 2 52 | }, 53 | "single-trailing-newline": false 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Query/QueryFactory.ts: -------------------------------------------------------------------------------- 1 | /* eslint indent: [2, 2, {"SwitchCase": 1}] */ 2 | import {type QattCodeBlock} from 'QattCodeBlock'; 3 | import {AlaSqlQuery} from 'Query/AlaSqlQuery'; 4 | import {type IQuery} from 'Query/IQuery'; 5 | import {Service} from '@ophidian/core'; 6 | import {LoggingService} from 'lib/LoggingService'; 7 | 8 | export class QueryFactory extends Service { 9 | logger = this.use(LoggingService).getLogger('Qatt.QueryFactory'); 10 | 11 | async onload() { 12 | this.logger.info('QueryFactory loaded'); 13 | } 14 | 15 | public async getQuery(codeblockConfiguration: QattCodeBlock, sourcePath: string, frontmatter: any, renderId?: string): Promise { 16 | switch (codeblockConfiguration.queryEngine) { 17 | case 'alasql': { 18 | const query = this.use.fork().use(AlaSqlQuery); 19 | await query.setupQuery(codeblockConfiguration, sourcePath, frontmatter, renderId ?? ''); 20 | return query; 21 | } 22 | 23 | default: { 24 | const query = this.use.fork().use(AlaSqlQuery); 25 | await query.setupQuery(codeblockConfiguration, sourcePath, frontmatter, renderId ?? ''); 26 | return query; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Query/Functions/Reverse.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='docs-sql-statements-reverse' options='file=queries/sql-statements/reverse.md' 5 | title: REVERSE 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | REVERSE ( string ) 13 | ``` 14 | 15 | ## Returns 16 | 17 | Returns the string with the characters in reverse order. 18 | 19 | ## Example 20 | 21 | The following example returns the frontmatter->status value reversed. 22 | 23 | ```yaml 24 | query: | 25 | SELECT TOP 2 path AS Path, 26 | REVERSE(frontmatter->status) AS Status 27 | FROM obsidian_notes 28 | WHERE frontmatter->status 29 | template: | 30 | {{stringify result}} 31 | ``` 32 | 33 | ```json 34 | [ 35 | { 36 | "path": "Projects - Demo Project/Why You Should Be Taking More Notes.md", 37 | "Status": "gnioD" 38 | }, { 39 | "path": "Projects - Demo Project/What I Learned From Taking 15,000 Notes.md", 40 | "Status": "golkcaB" 41 | } 42 | ] 43 | ``` 44 | 45 | // << docs-sql-statements-reverse 46 | */ 47 | export function registerFunctionReverse(): void { 48 | alasql.fn.REVERSE = function (value: string): string { 49 | return value.split('').reverse().join(''); 50 | }; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/custom-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | title: Custom Functions 4 | order: 7 5 | nav_exclude: true 6 | --- 7 | 8 | # Custom Functions 9 | 10 | If you have CustomJS enabled you can add custom functions to be available in the SQL queries. The example below uses the class found in [[customjs/custom-functions.js]]. To reference a function in your query you need to add a directive to the code block telling it what functions to make available. It will then register the CustomJS function in the referenced class. 11 | 12 | The format of the directive is: `customjs: ClassName FunctionName` 13 | 14 | The example below uses this class registered in CustomJS 15 | 16 | ```javascript 17 | class MyTaskFunctions { 18 | SuperImportant(status) { 19 | return status === '!'; 20 | } 21 | } 22 | ``` 23 | 24 | It is then made available in the query via the `customjs` directive specifying the class name and then the function name. It is `customjs MyTaskFunctions SuperImportant` for this example. 25 | 26 | When called from the query the status object is passed to the function as a property allowing the function to return true if the indicator value is equal to `!` 27 | 28 | ```qatt 29 | query: SELECT * FROM tasks WHERE SuperImportant(status) 30 | customJSForSql: ['MyTaskFunctions SuperImportant'] 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/examples-tutorials/csv-loader-web.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: Examples / Tutorials 3 | title: Web based CSV loading 4 | --- 5 | # {{ $frontmatter.title }} 6 | 7 | This example will help you setup and use the CSV loader functionality using a CSV file from the web. This is similar to the [csv-loader](csv-loader.md) example with the main difference being the CSV file is downloaded from the internet. 8 | 9 | In the configuration the following has been added the the CSV loader configuration. 10 | 11 | ``` 12 | WEB|webcsvexample|https://raw.githubusercontent.com/sytone/obsidian-queryallthethings/refs/heads/main/docs/examples-tutorials/gamedata.csv 13 | ``` 14 | 15 | When prefixed with `WEB|` the processing will expect two more values delimited by the pipe `|` symbol. The first value is the table name, so in this example the table name will be `webcsvexample` and the second value will be the URL. It needs to be accessible by obsidian and not require authentication. 16 | 17 | Here is a query that runs against the `webcsvexample` table. The data is pulled from the csv file stored on GitHub. 18 | 19 | 20 | ```qatt 21 | query: | 22 | SELECT TOP 5 * FROM webcsvexample 23 | template: | 24 | {{#each result}} 25 | ## {{player_id}} - {{username}} 26 | - Level: {{level}} 27 | - Score: {{score}} 28 | 29 | --- 30 | {{/each}} 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/templates/template.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 4 3 | 4 | parent: Templates 5 | title: Template 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The template uses Handlebars as the formatting structure. Full details on Handlebars can be found on their site and will not be replicated here. 10 | 11 | [Handlebars Guide](https://handlebarsjs.com/guide/) 12 | 13 | ## Custom Block Helpers 14 | 15 | The following Block level helpers are avaliable to be used in rendering your output. 16 | 17 | ### #micromark 18 | 19 | %%snippet id='handlebars-helper-micromark-snippet' options='nocodeblock'%% 20 | The `micromark`\-helper renders markdown as HTML using the micromark library. It has one setting 21 | which will remove the wrapping `

` tag from the output if inline is set to true. 22 | 23 | 24 | ```handlebars 25 | {{{#micromark inline="true"}}} {{{task}}} [[{{{page}}}|📝]] {{{/micromark}}} 26 | ``` 27 | 28 | 29 | when used with this context: 30 | 31 | ``` 32 | { 33 | task: "This is a **thing** to do", 34 | page: "folder/SomePage.md" 35 | } 36 | ``` 37 | 38 | will result in: 39 | 40 | ``` 41 | This is a thing to do 42 | ``` 43 | 44 | If the inline property is not set, then the output will be wrapped in a `

` tag and result in: 45 | 46 | ``` 47 |

This is a thing to do

48 | ``` 49 | %%/snippet%% 50 | -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/relationaloperators.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: Relational Operators Helper 6 | --- 7 | 8 | This uses a simple query to help show what the Relational Operators helpers does when rendering. 9 | 10 | ### Example 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT TOP 10 path, name, frontmatter->type AS page_type 16 | FROM obsidian_notes 17 | WHERE frontmatter->type 18 | template: | 19 | ## ✅ Next actions 20 | {{#each result}} 21 | - [[{{name}}]] - {{page_type}} - 22 | {{#eq page_type 'journal'}} 23 | 24 | Journal Page!!!! 25 | {{else}} 26 | 27 | Not Journal Page 28 | {{/eq}} 29 | {{/each}} 30 | ``` 31 | ```` 32 | 33 | ### Live in Vault 34 | 35 | ```qatt 36 | query: | 37 | SELECT TOP 10 path, name, frontmatter->type AS page_type 38 | FROM obsidian_notes 39 | WHERE frontmatter->type 40 | template: | 41 | ## ✅ Next actions 42 | {{#each result}} 43 | - [[{{name}}]] - {{page_type}} - 44 | {{#eq page_type 'Journal'}} 45 | 46 | Journal Page!!!! 47 | {{else}} 48 | 49 | Not Journal Page 50 | {{/eq}} 51 | {{/each}} 52 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/project-list.md: -------------------------------------------------------------------------------- 1 | This note contains live examples of: 2 | 3 | - [using-updatepropertyfromlist](../using-updatepropertyfromlist.md) 4 | 5 | 6 | ## Project list 7 | 8 | Click on the Status to update it using the suggester. 9 | 10 | ```qatt 11 | postRenderFormat: micromark 12 | query: | 13 | SELECT 14 | frontmatter->[project-name] AS project, 15 | frontmatter->status AS status, 16 | frontmatter->priority AS priority, 17 | frontmatter->area AS area, 18 | path, 19 | updatePropertyFromList(frontmatter->status, path, @['Active','Done','Todo','In Progress','Waiting','Archive'], 'status') AS updateStatus, 20 | updatePropertyFromList(frontmatter->area, path, @['Health', 'Family', 'Fun', 'Social', 'Career', 'Financial', 'Learning'], 'area') AS updateArea, 21 | updatePropertyFromList(frontmatter->priority, path, @[1, 2, 3], 'priority') AS updatePriority 22 | FROM obsidian_notes 23 | WHERE frontmatter->notetype = 'project' 24 | AND frontmatter->status <> 'Archive' 25 | AND frontmatter->status <> 'Done' 26 | ORDER BY status 27 | template: | 28 | | Project | Status | Priority | Area | 29 | | ------- | ------ | -------- | ---- | 30 | {{#each result}} 31 | | [[{{project}}]] | {{{updateStatus}}} | {{{updatePriority}}} | {{{updateArea}}} | 32 | {{/each}} 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/examples-tutorials/csv-loader.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Using CSV loader functionality 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | This example will help you setup and use the CSV loader functionality. If you are viewing this in Obsidian you can see and play with the CSV file loaded for this example. 9 | 10 | In this folder there is a file called `example.csv`, it is a csv file with random data and a comment field that has paragraphs of data. 11 | 12 | Below there is a code block that has a simple query that pulls the top 5 items from the CSV file, by default the file name becomes the table name. So a file called `amazingplacestoeat.csv` will be loaded into a table called `amazingplacestoeat`. 13 | 14 | So as the file in this example is called `example.csv` the table is `example` so the query is `SELECT TOP 5 * FROM example` 15 | 16 | As the CSV file has text that is multi live in it when it is returned and rendered it will show up as multiple lines. 17 | 18 | ```qatt 19 | query: | 20 | SELECT TOP 5 * FROM example 21 | template: | 22 | {{#each result}} 23 | ## {{id}} - All about {{animal}} 24 | The *{{animal}}* lives in **{{city}}** and loves watching {{movie}}. They have saved {{amount}}. 25 | 26 | They love to say... 27 | 28 | {{comment}} 29 | 30 | --- 31 | {{/each}} 32 | ``` 33 | -------------------------------------------------------------------------------- /src/Interfaces/IQueryAllTheThingsPlugin.ts: -------------------------------------------------------------------------------- 1 | import {type App, type Command, type EventRef, type MarkdownPostProcessor, type MarkdownPostProcessorContext, type Plugin} from 'obsidian'; 2 | import {type SettingsManager} from 'Settings/SettingsManager'; 3 | 4 | export interface IQueryAllTheThingsPlugin extends Plugin { 5 | 6 | app: App; 7 | settingsManager: SettingsManager | undefined; 8 | 9 | saveData(data: any): Promise; 10 | onload(): any; 11 | onunload(): void; 12 | // LoadSettings(): any; 13 | // saveSettings(): any; 14 | 15 | registerMarkdownCodeBlockProcessor(language: string, handler: (source: string, element: HTMLElement, ctx: MarkdownPostProcessorContext) => Promise | void, sortOrder?: number): MarkdownPostProcessor; 16 | addCommand(command: Command): Command; 17 | registerEvent(eventRef: EventRef): void; 18 | } 19 | 20 | export interface IQueryAllTheThingsPluginInternal { 21 | 22 | saveData(data: any): Promise; 23 | onload(): any; 24 | onunload(): void; 25 | loadSettings(): any; 26 | saveSettings(): any; 27 | 28 | registerMarkdownCodeBlockProcessor(language: string, handler: (source: string, element: HTMLElement, ctx: MarkdownPostProcessorContext) => Promise | void, sortOrder?: number): MarkdownPostProcessor; 29 | addCommand(command: Command): Command; 30 | registerEvent(eventRef: EventRef): void; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /.config/obsidian-versions-updater.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | module.exports.readVersion = function (contents) { 4 | const json = JSON.parse(contents); 5 | const versionKey = Object.keys(json)[Object.keys(json).length - 1]; 6 | console.log(`ℹ Current Version: ${versionKey}`); 7 | return versionKey; 8 | }; 9 | 10 | module.exports.writeVersion = function (contents, version) { 11 | // Pull version from manifest. 12 | let manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8')); 13 | const { minAppVersion } = manifest; 14 | console.log(`ℹ New Version: ${version}`); 15 | console.log(`ℹ Manifest Minimum Obsidian Version: ${minAppVersion}`); 16 | 17 | // Pull Obsidian version from package.json to validate. 18 | let package = JSON.parse(fs.readFileSync('package.json', 'utf8')); 19 | const { obsidian } = package.devDependencies; 20 | console.log(`ℹ Package Minimum Obsidian Version: ${obsidian}`); 21 | 22 | if (minAppVersion !== obsidian) { 23 | console.error('⚠️ Manifest and Package Minimum Obsidian Versions do not match.'); 24 | console.error('⚠️ Please update the manifest and package versions to match.'); 25 | throw new Error('Manifest and Package Minimum Obsidian Versions do not match.'); 26 | } 27 | 28 | const json = JSON.parse(contents); 29 | json[version] = minAppVersion; 30 | return JSON.stringify(json, null, '\t'); 31 | }; 32 | -------------------------------------------------------------------------------- /src/Query/Functions/JoinArray.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='docs-sql-statements-joinarray' options='file=queries/sql-statements/joinarray.md' 5 | title: JoinArray 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | JoinArray ( dataArray [ , separator ] ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *dataArray* 18 | An array of strings to be joined. 19 | 20 | *separator* 21 | A string to be used as a separator. If omitted, the array elements are not separated with anything. 22 | 23 | ## Returns 24 | 25 | A string that is the result of concatenating the array elements. 26 | 27 | ## Example 28 | 29 | The following example finds the location of the word `string` in the string `This is the string to search`. 30 | 31 | ```yaml 32 | query: | 33 | SELECT JoinArray(@['Hello','World','!']) AS JoinArrayValue 34 | template: | 35 | {{stringify result}} 36 | ``` 37 | 38 | ```json 39 | [ { "JoinArrayValue": "HelloWorld!" } ] 40 | ``` 41 | 42 | // << docs-sql-statements-joinarray 43 | */ 44 | export function registerFunctionJoinArray(): void { 45 | alasql.fn.JoinArray = function (dataArray: any[], separator?: string): string { 46 | if (separator === undefined) { 47 | separator = ''; 48 | } 49 | 50 | if (dataArray !== undefined) { 51 | return dataArray.join(separator); 52 | } 53 | 54 | return ''; 55 | }; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/qatt/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "sqlFiles": "", 3 | "sqlLoaderSettingsOpen": false, 4 | "postRenderFormat": "markdown", 5 | "enableExperimentalRender": false, 6 | "renderingSettingsOpen": true, 7 | "internalQueryRenderChildVersion": 2, 8 | "enableCodeBlockEditor": false, 9 | "queryFileRoot": "", 10 | "templateFileRoot": "", 11 | "debounceWindow": 5000, 12 | "disableDebounce": false, 13 | "enableDataViewInlineFieldParsingForTasks": false, 14 | "notesCacheSettingsOpen": true, 15 | "enableAlaSqlTablePopulation": true, 16 | "disableContinualIndexNotifications": true, 17 | "jsonFiles": "examples-tutorials/ttrpg/creatures_b1.json", 18 | "jsonLoaderSettingsOpen": false, 19 | "markdownTableFiles": "", 20 | "markdownLoaderSettingsOpen": false, 21 | "csvFiles": "examples-tutorials/example.csv\nWEB|webcsvexample|https://raw.githubusercontent.com/sytone/obsidian-queryallthethings/refs/heads/main/docs/examples-tutorials/gamedata.csv", 22 | "csvLoaderSettingsOpen": true, 23 | "onStartSqlQueries": "CREATE TABLE my_lookup(name,birthday);\nINSERT INTO my_lookup VALUES (\"fred\", 2000-02-03);", 24 | "announceUpdates": false, 25 | "version": "1.2.0", 26 | "mainHeadingOpen": true, 27 | "generalHeadingOpen": true, 28 | "internalLoggingConsoleLogLimit": 10, 29 | "disableDataviewMissingNotification": false, 30 | "disableCustomJsMissingNotification": false, 31 | "enableEditorRightClickMenu": true 32 | } -------------------------------------------------------------------------------- /src/Query/Functions/ExtractLine.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='docs-sql-statements-extractline' options='file=queries/sql-statements/extractline.md' 5 | title: EXTRACTLINE 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | EXTRACTLINE ( line_number, stringToExtractLineFrom ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *line_number* 18 | A line number to extract from the string. 19 | 20 | *stringToExtractLineFrom* 21 | The string to extract the line from. 22 | 23 | ## Returns 24 | 25 | Returns a string containing the line at the specified line number. Empty string if the line number is invalid. 26 | 27 | ## Example 28 | 29 | The following example returns the third line from the string `This\nis\nthe\nstring\nto\nuse`. 30 | 31 | ```yaml 32 | query: | 33 | SELECT EXTRACTLINE(3, 'This\nis\nthe\nstring\nto\nuse') AS LineContents 34 | template: | 35 | {{stringify result}} 36 | ``` 37 | 38 | ```json 39 | [ { "LineContents": "the" } ] 40 | ``` 41 | 42 | // << docs-sql-statements-extractline 43 | */ 44 | export function registerFunctionExtractLine(): void { 45 | alasql.fn.EXTRACTLINE = function (line_number: number, stringToExtractLineFrom: string): string { 46 | const lines = stringToExtractLineFrom.split('\n'); 47 | 48 | if (line_number < 0 || line_number >= lines.length) { 49 | return ''; 50 | } 51 | 52 | return lines[line_number]; 53 | }; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from 'Render/HandlebarsHelpers/Capitalize'; 3 | export * from 'Render/HandlebarsHelpers/CodeBlockFooter'; 4 | export * from 'Render/HandlebarsHelpers/CodeBlockHeader'; 5 | export * from 'Render/HandlebarsHelpers/FlexibleTaskCheckbox'; 6 | export * from 'Render/HandlebarsHelpers/Group'; 7 | export * from 'Render/HandlebarsHelpers/HtmlTaskList'; 8 | export * from 'Render/HandlebarsHelpers/index'; 9 | export * from 'Render/HandlebarsHelpers/IsHighPriority'; 10 | export * from 'Render/HandlebarsHelpers/IsLowPriority'; 11 | export * from 'Render/HandlebarsHelpers/IsMediumPriority'; 12 | export * from 'Render/HandlebarsHelpers/Lowercase'; 13 | export * from 'Render/HandlebarsHelpers/Micromark'; 14 | export * from 'Render/HandlebarsHelpers/NoteLink'; 15 | export * from 'Render/HandlebarsHelpers/Obsidian'; 16 | export * from 'Render/HandlebarsHelpers/ObsidianHtmlInternalLink'; 17 | export * from 'Render/HandlebarsHelpers/Pad'; 18 | export * from 'Render/HandlebarsHelpers/Stringify'; 19 | export * from 'Render/HandlebarsHelpers/TaskCheckbox'; 20 | export * from 'Render/HandlebarsHelpers/TaskCheckboxWithAppend'; 21 | export * from 'Render/HandlebarsHelpers/ToInt'; 22 | export * from 'Render/HandlebarsHelpers/Trim'; 23 | export * from 'Render/HandlebarsHelpers/Uppercase'; 24 | export * from 'Render/HandlebarsHelpers/FormatDate'; 25 | export * from 'Render/HandlebarsHelpers/RelationalOperators'; 26 | export * from 'Render/HandlebarsHelpers/MarkdownTable'; 27 | -------------------------------------------------------------------------------- /docs/data-tables/legacy-tables/obsidian-markdown-lists.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 91 3 | 4 | parent: Data Tables 5 | title: Obsidian Markdown Lists 6 | --- 7 | 8 | ## Obsidian Markdown Lists (obsidian_markdown_lists) 9 | 10 | Please use [obsidian_lists](../obsidian-lists) going forward, this table will be eventually removed. 11 | 12 | The list item is parse by the following regular expression to extract information about it, if you encounter issues you can use this to help debug. 13 | 14 | `/^([\s\t>]*)([-*+]|\d+\.)? *(\[(.)])? *(.*)/gm` 15 | 16 | | Column Name | Type | Description | 17 | | ----------- | ------- | ----------------------------------------- | 18 | | parent | number | | 19 | | task | string | | 20 | | content | string | | 21 | | line | number | | 22 | | column | number | | 23 | | isTopLevel | boolean | | 24 | | path | string | Path of the page the list item is on. | 25 | | modified | number | Time where parent note was last modified. | 26 | | text | string | The text part of the list. | 27 | | checked | boolean | | 28 | | status | string | | 29 | -------------------------------------------------------------------------------- /docs/markdown-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | exclude: true 3 | --- 4 | # Markdown Extension Examples 5 | 6 | This page demonstrates some of the built-in markdown extensions provided by VitePress. 7 | 8 | ## Syntax Highlighting 9 | 10 | VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting: 11 | 12 | **Input** 13 | 14 | ````md 15 | ```js{4} 16 | export default { 17 | data () { 18 | return { 19 | msg: 'Highlighted!' 20 | } 21 | } 22 | } 23 | ``` 24 | ```` 25 | 26 | **Output** 27 | 28 | ```js{4} 29 | export default { 30 | data () { 31 | return { 32 | msg: 'Highlighted!' 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | ## Custom Containers 39 | 40 | **Input** 41 | 42 | ```md 43 | ::: info 44 | This is an info box. 45 | ::: 46 | 47 | ::: tip 48 | This is a tip. 49 | ::: 50 | 51 | ::: warning 52 | This is a warning. 53 | ::: 54 | 55 | ::: danger 56 | This is a dangerous warning. 57 | ::: 58 | 59 | ::: details 60 | This is a details block. 61 | ::: 62 | ``` 63 | 64 | **Output** 65 | 66 | ::: info 67 | This is an info box. 68 | ::: 69 | 70 | ::: tip 71 | This is a tip. 72 | ::: 73 | 74 | ::: warning 75 | This is a warning. 76 | ::: 77 | 78 | ::: danger 79 | This is a dangerous warning. 80 | ::: 81 | 82 | ::: details 83 | This is a details block. 84 | ::: 85 | 86 | ## More 87 | 88 | Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown). 89 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/NoteLink.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-notelink' options='file=templates/hb-helpers/notelink.md' 4 | title: noteLink 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `noteLink`-helper will ... 9 | 10 | ```handlebars 11 | {{noteLink path}} 12 | ``` 13 | 14 | when used with this context: 15 | 16 | ```json 17 | { 18 | path: 'notepages/school/My Cool Page', 19 | name: 'My Cool Page is here!' 20 | } 21 | ``` 22 | 23 | will result in: 24 | 25 | ````markdown 26 | [[notepages/school/My Cool Page]] 27 | ```` 28 | 29 | // << docs-handlebars-helper-notelink 30 | */ 31 | /* 32 | // >> id='docs-examples-handlebars-notelink' options='file=examples-tutorials/handlebars/notelink.md' 33 | title: noteLink Helper 34 | --- 35 | 36 | This uses a simple query to help show what the `noteLink`-helper does when rendering. 37 | 38 | ### Example 39 | 40 | ````markdown 41 | ```qatt 42 | query: | 43 | SELECT 'notepages/school/My Cool Page' AS link 44 | template: | 45 | {{#each result}} 46 | {{noteLink link}} 47 | {{/each}} 48 | ``` 49 | ```` 50 | 51 | ### Live in Vault 52 | 53 | ```qatt 54 | query: | 55 | SELECT 'notepages/school/My Cool Page' AS link 56 | template: | 57 | {{#each result}} 58 | {{noteLink link}} 59 | {{/each}} 60 | ``` 61 | 62 | // << docs-examples-handlebars-notelink 63 | */ 64 | export function noteLink(value: string) { 65 | return new Handlebars.SafeString(`[[${value}]]`); 66 | } 67 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/CodeBlockFooter.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | 3 | /* 4 | // >> id='docs-handlebars-helper-codeblockfooter' options='file=templates/hb-helpers/codeblockfooter.md' 5 | title: codeBlockFooter 6 | --- 7 | 8 | # {{ $frontmatter.title }} 9 | 10 | The `codeBlockFooter`\-helper will insert the three back ticks into the resulting markdown. 11 | 12 | ```handlebars 13 | {{codeBlockFooter}} 14 | ``` 15 | 16 | will result in: 17 | 18 | ````markdown 19 | ``` 20 | ```` 21 | 22 | // << docs-handlebars-helper-codeblockfooter 23 | */ 24 | /* 25 | // >> id='docs-examples-handlebars-codeblockfooter' options='file=examples-tutorials/handlebars/codeblockfooter.md' 26 | title: codeBlockFooter Helper 27 | --- 28 | 29 | This uses a simple query to help show what the codeBlockFooter helper does when rendering. 30 | 31 | ### Example 32 | ````markdown 33 | ```qatt 34 | query: | 35 | SELECT 'something to render in a code block. ' AS code 36 | template: | 37 | {{#each result}} 38 | {{codeBlockHeader 'text'}} 39 | {{code}} 40 | {{codeBlockFooter}} 41 | {{/each}} 42 | ``` 43 | ```` 44 | ### Live in Vault 45 | ```qatt 46 | query: | 47 | SELECT 'something to render in a code block. ' AS code 48 | template: | 49 | {{#each result}} 50 | {{codeBlockHeader 'text'}} 51 | {{code}} 52 | {{codeBlockFooter}} 53 | {{/each}} 54 | ``` 55 | 56 | // << docs-examples-handlebars-codeblockfooter 57 | */ 58 | export function codeBlockFooter() { 59 | return new Handlebars.SafeString('```'); 60 | } 61 | -------------------------------------------------------------------------------- /docs/.obsidian/plugins/cmdr/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "confirmDeletion": true, 3 | "showAddCommand": true, 4 | "debug": false, 5 | "editorMenu": [], 6 | "fileMenu": [], 7 | "leftRibbon": [ 8 | { 9 | "id": "obsidian-shellcommands:shell-command-rnlv1f5kwn", 10 | "icon": "chevrons-up-down", 11 | "name": "Shell commands: Execute: Build QATT Documentation with snippets", 12 | "mode": "any", 13 | "color": "#25c19a" 14 | }, 15 | { 16 | "id": "obsidian-shellcommands:shell-command-y3d6u4vupv", 17 | "icon": "download", 18 | "name": "Shell commands: Execute: Install Project Packages", 19 | "mode": "any", 20 | "color": "#ae8484" 21 | }, 22 | { 23 | "id": "obsidian-shellcommands:shell-command-joou77pnjd", 24 | "icon": "plug-zap", 25 | "name": "Shell commands: Execute: Build and install plugin", 26 | "mode": "any", 27 | "color": "#918821" 28 | } 29 | ], 30 | "rightRibbon": [], 31 | "titleBar": [], 32 | "statusBar": [], 33 | "pageHeader": [ 34 | { 35 | "id": "editor:toggle-source", 36 | "icon": "lucide-code-2", 37 | "name": "Toggle Live Preview/Source mode", 38 | "mode": "any" 39 | } 40 | ], 41 | "macros": [], 42 | "explorer": [], 43 | "hide": { 44 | "statusbar": [], 45 | "leftRibbon": [] 46 | }, 47 | "spacing": 8, 48 | "advancedToolbar": { 49 | "rowHeight": 48, 50 | "rowCount": 1, 51 | "spacing": 0, 52 | "buttonWidth": 48, 53 | "columnLayout": false, 54 | "mappedIcons": [], 55 | "tooltips": false, 56 | "heightOffset": 0 57 | } 58 | } -------------------------------------------------------------------------------- /docs/examples-tutorials/handlebars/group.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Handlebars 4 | grand_parent: Examples / Tutorials 5 | title: Group Helper 6 | --- 7 | 8 | This example uses the `qatt.ReferenceCalendar` table to get all the dates for February in the current year and groups by the `weekOfYear` value. 9 | 10 | The render helper `group` then takes the result of the query and does a group by `weekOfYear`. Inside the `group` helper the value of the grouping property can be accessed by using `{{value}}`. In the example below it is being used as the 4th level header value. 11 | 12 | The items in the group are then available via the `items` property. Below this is iterated though to get the `date` value and if it `isWeekend`. These are then used to display different outputs if `isWeekend` is true. 13 | 14 | ### Example 15 | ````markdown 16 | ```qatt 17 | query: | 18 | SELECT * 19 | FROM qatt.ReferenceCalendar 20 | WHERE year = moment()->year() 21 | AND month = 2 22 | template: | 23 | {{#group result by="weekOfYear"}} 24 | #### Week {{value}} 25 | {{#each items}} 26 | {{#if isWeekend}} 27 | - {{date}} 🥳🎂🍾🥂 28 | {{else}} 29 | - {{date}} ⚒️ 30 | {{/if}} 31 | {{/each}} 32 | {{/group}} 33 | ``` 34 | ```` 35 | ### Live in Vault 36 | ```qatt 37 | query: | 38 | SELECT * 39 | FROM qatt.ReferenceCalendar 40 | WHERE year = moment()->year() 41 | AND month = 2 42 | template: | 43 | {{#group result by="weekOfYear"}} 44 | #### Week {{value}} 45 | {{#each items}} 46 | {{#if isWeekend}} 47 | - {{date}} 🥳🎂🍾🥂 48 | {{else}} 49 | - {{date}} ⚒️ 50 | {{/if}} 51 | {{/each}} 52 | {{/group}} 53 | ``` -------------------------------------------------------------------------------- /docs/queries/sql-functions/noteFileName.md: -------------------------------------------------------------------------------- 1 | --- 2 | parent: SQL Functions 3 | grand_parent: Writing Queries 4 | title: noteFileName() 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `noteFileName()` function will return the current page name, so if the file path in your vault is `/folder/folder/my cool filename.md` this function will return `my cool filename`. 9 | 10 | This can be used on pages to make qeries that find information in your vault that references this page name. For example the below block will find all tasks with a reference to this page name in them. 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT page, getNoteName(page) AS name, text 16 | FROM obsidian_markdown_tasks 17 | WHERE 18 | text LIKE '%'+noteFileName()+'%' 19 | template: | 20 | | File | Task | 21 | | ---- | ---- | 22 | {{#each result}} 23 | |{{obsidianHtmlInternalLink page name}} | {{text}}| 24 | {{/each}} 25 | ``` 26 | ```` 27 | 28 | Here are all the page functions to extract information from the current page the query is running on. 29 | 30 | ````markdown 31 | ```qatt 32 | query: | 33 | SELECT TOP 1 34 | notePathWithFileExtension() AS notePathWithFileExtension, 35 | notePathWithoutFileExtension() AS notePathWithoutFileExtension, 36 | notePath() AS notePath, 37 | noteFileName() AS noteFileName 38 | FROM obsidian_markdown_tasks 39 | template: | 40 | {{#each result}} 41 | notePathWithFileExtension: {{notePathWithFileExtension}} 42 | notePathWithoutFileExtension: {{notePathWithoutFileExtension}} 43 | notePath: {{notePath}} 44 | noteFileName: {{noteFileName}} 45 | {{/each}} 46 | ``` 47 | ```` 48 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/CodeBlockHeader.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-codeblockheader' options='file=templates/hb-helpers/codeblockheader.md' 4 | title: codeBlockHeader 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `codeBlockHeader`\-helper will insert the three back ticks into the resulting markdown with the name specified as a parameter as the code block type. 9 | 10 | ```handlebars 11 | {{codeBlockHeade 'text'}} 12 | ``` 13 | 14 | will result in: 15 | 16 | ````markdown 17 | ```text 18 | ```` 19 | 20 | // << docs-handlebars-helper-codeblockheader 21 | */ 22 | /* 23 | // >> id='docs-examples-handlebars-codeblockheader' options='file=examples-tutorials/handlebars/codeblockheader.md' 24 | title: codeBlockHeader Helper 25 | --- 26 | 27 | This uses a simple query to help show what the codeBlockHeader helper does when rendering. 28 | 29 | ### Example 30 | ````markdown 31 | ```qatt 32 | query: | 33 | SELECT 'something to render in a code block. ' AS code 34 | template: | 35 | {{#each result}} 36 | {{codeBlockHeader 'text'}} 37 | {{code}} 38 | {{codeBlockFooter}} 39 | {{/each}} 40 | ``` 41 | ```` 42 | ### Live in Vault 43 | ```qatt 44 | query: | 45 | SELECT 'something to render in a code block. ' AS code 46 | template: | 47 | {{#each result}} 48 | {{codeBlockHeader 'text'}} 49 | {{code}} 50 | {{codeBlockFooter}} 51 | {{/each}} 52 | ``` 53 | 54 | // << docs-examples-handlebars-codeblockheader 55 | */ 56 | export function codeBlockHeader(value: string) { 57 | return new Handlebars.SafeString('```' + value); 58 | } 59 | -------------------------------------------------------------------------------- /src/Query/Functions/Charindex.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='docs-sql-statements-charindex' options='file=queries/sql-statements/charindex.md' 5 | title: CHARINDEX 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | ## Syntax 10 | 11 | ```sql 12 | CHARINDEX ( expressionToFind , expressionToSearch [ , start_location ] ) 13 | ``` 14 | 15 | ## Arguments 16 | 17 | *expressionToFind* 18 | A character expression containing the sequence to find. 19 | 20 | *expressionToSearch* 21 | A character expression to search. 22 | 23 | *start_location* 24 | An number at which the search starts. If start_location is not specified, has a negative value, or has a zero (0) value, the search starts at the beginning of expressionToSearch. 25 | 26 | ## Returns 27 | 28 | Returns a number indicating the position in the string. 29 | 30 | ## Example 31 | 32 | The following example finds the location of the word `string` in the string `This is the string to search`. 33 | 34 | ```yaml 35 | query: | 36 | SELECT CHARINDEX('string', 'This is the string to search') AS StringIndex 37 | template: | 38 | {{stringify result}} 39 | ``` 40 | 41 | ```json 42 | [ { "StringIndex": 13 } ] 43 | ``` 44 | 45 | // << docs-sql-statements-charindex 46 | */ 47 | export function registerFunctionCharindex(): void { 48 | alasql.fn.CHARINDEX = function (expressionToFind: string, expressionToSearch: string, start_location?: number): number { 49 | if (start_location !== undefined) { 50 | return expressionToSearch.indexOf(expressionToFind, start_location) + 1; 51 | } 52 | 53 | return expressionToSearch.indexOf(expressionToFind) + 1; 54 | }; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /docs/examples-tutorials/replace-target-path-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Replace target path example 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | This example shows how the replace target path function works. 9 | 10 | As all output is converted to HTML so Obsidian will render it we need to make sure we do not have that process run if we want to create a markdown file. To so this we need to set `postRenderFormat` to `html` so the render engine knows the result is pre created HTML and not to run a markdown to HTML converter on the result but just output as is. 11 | 12 | ````markdown 13 | ```qatt 14 | query: | 15 | SELECT TOP 10 16 | basename 17 | FROM obsidian_notes 18 | ORDER BY stat->mtime DESC 19 | template: | 20 | {{#each result}} 21 | - {{basename}} 22 | {{/each}} 23 | postRenderFormat: html 24 | replaceTargetPath: examples-tutorials/generated-top-ten-notes.md 25 | replaceType: always 26 | ``` 27 | ```` 28 | 29 | ## Live Example 30 | 31 | Open docs in Obsidian to see this example live. 32 | 33 | View this page in source mode to see the code block below this line that generates the included file. 34 | 35 | ```qatt 36 | query: | 37 | SELECT TOP 10 38 | basename 39 | FROM obsidian_notes 40 | ORDER BY stat->mtime DESC 41 | template: | 42 | {{#each result}} 43 | - {{basename}} 44 | {{/each}} 45 | postRenderFormat: html 46 | replaceTargetPath: examples-tutorials/generated-top-ten-notes.md 47 | replaceType: always 48 | ``` 49 | 50 | Here is the generated file examples-tutorials/generated-top-ten-notes.md embedded in this page. 51 | 52 | ![generated-top-ten-notes](generated-top-ten-notes.md) 53 | 54 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Pad.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-pad' options='file=templates/hb-helpers/pad.md' 4 | title: pad 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `pad`-helper will pad a string with the specified character the specified number of times. By default the character is a space ' '. 9 | 10 | ```handlebars 11 | {{pad}} 12 | ``` 13 | 14 | when used with this context: 15 | 16 | ```json 17 | { 18 | path: 'notepages/school/My Cool Page', 19 | name: 'My Cool Page is here!' 20 | } 21 | ``` 22 | 23 | will result in: 24 | 25 | ````markdown 26 | ... 27 | ```` 28 | 29 | // << docs-handlebars-helper-pad 30 | */ 31 | /* 32 | // >> id='docs-examples-handlebars-pad' options='file=examples-tutorials/handlebars/pad.md' 33 | title: pad Helper 34 | --- 35 | 36 | This uses a simple query to help show what the pad helper does when rendering. 37 | 38 | ### Example 39 | 40 | ````markdown 41 | ```qatt 42 | query: | 43 | SELECT 'something to render in a code block. ' AS code 44 | template: | 45 | {{#each result}} 46 | {{pad 'text'}} 47 | {{/each}} 48 | ``` 49 | ```` 50 | 51 | ### Live in Vault 52 | 53 | ```qatt 54 | query: | 55 | SELECT 'something to render in a code block. ' AS code 56 | template: | 57 | {{#each result}} 58 | {{pad 'text'}} 59 | {{/each}} 60 | ``` 61 | 62 | // << docs-examples-handlebars-pad 63 | */ 64 | export function pad(count: number, options: HelperOptions) { 65 | const fn = options.fn; 66 | const inverse = options.inverse; 67 | const paddingString = options.hash?.padWith as string ?? ' '; 68 | return new Handlebars.SafeString(paddingString.repeat((count))); 69 | } 70 | -------------------------------------------------------------------------------- /docs/data-tables/pageproperty.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 13 3 | 4 | parent: Data Tables 5 | title: PAGEPROPERTY 6 | --- 7 | 8 | # PAGEPROPERTY Table 9 | 10 | Table Name: `PAGEPROPERTY` 11 | 12 | This is a dynamic table based off the page the query is running on. The two examples below show ways this can be used. 13 | 14 | ## Tags property 15 | 16 | A good example is the `tags` page property. Functionality is best shown by the example below. 17 | 18 | If a not has the following YAML/property: 19 | 20 | ``` 21 | ___ 22 | tags: 23 | - Value1 24 | - Value2 25 | - Value3 26 | - Value4 27 | - Value5 28 | ___ 29 | 30 | # Heading 1 31 | ``` 32 | 33 | The following query will return the TOP 2 tags from the list, you can make the query as complex as you want based on the data in the YAML header. 34 | 35 | ````markdown 36 | ```qatt 37 | query: | 38 | SELECT TOP 2 _ AS tag FROM PAGEPROPERTY("tags") 39 | template: | 40 | {{stringify result}} 41 | ``` 42 | ```` 43 | The output would be: 44 | 45 | ``` 46 | [ 47 | { 48 | "tag": "Value1" 49 | }, 50 | { 51 | "tag": "Value2" 52 | } 53 | ] 54 | ``` 55 | 56 | ## Custom property 57 | 58 | YAML Header: 59 | 60 | ``` 61 | ___ 62 | areas: 63 | - Family 64 | - Finances 65 | - Manager 66 | - Business 67 | - Career Growth 68 | - Personal Productivity 69 | ___ 70 | 71 | # Heading 1 72 | ``` 73 | 74 | Query all items and make a list of them. 75 | 76 | ````markdown 77 | ```qatt 78 | query: | 79 | SELECT _ AS area FROM PAGEPROPERTY("areas") 80 | template: | 81 | {{#each result}} 82 | - {{area}} 83 | {{/each}} 84 | ``` 85 | ```` 86 | The output would be: 87 | 88 | ``` 89 | - Family 90 | - Finances 91 | - Manager 92 | - Oro Business 93 | - Career Growth 94 | - Personal Productivity 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/notes/projects/Project Two.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | notetype: project 4 | version: 2 5 | project-name: Project Two 6 | created: 2023-01-01T09:32:16-08:00 7 | tags: 8 | - project 9 | - area/career 10 | - project/projtwo 11 | subtitle: This is project one which is very important 12 | aliases: 13 | - projone 14 | status: In Progress 15 | area: Fun 16 | priority: "3" 17 | --- 18 | 19 | # Project Two 20 | 21 | ## Project Info 22 | 23 | This is related to [[Project One]] 24 | 25 | ## Thoughts 26 | 27 | ## Resources 28 | 29 | ## Review questions 30 | 31 | - Problem Statement 32 | - Success Metrics 33 | - In Scope 34 | - Out of Scope 35 | - Risks 36 | - Open Questions 37 | 38 | ## Tasks 39 | 40 | - [ ] Fill out overview for Project Two 41 | 42 | ## Backlinks 43 | 44 | ```qatt 45 | query: | 46 | SELECT path, getNoteName(path) AS name, moment(stat->mtime).format('YYYY-MM-DD') AS LastUpdate 47 | FROM obsidian_notes 48 | WHERE content LIKE '%'+noteFileName()+'%' AND getNoteName(path) <> noteFileName() 49 | template: | 50 | | File | Last Modified | 51 | | ---- | ------------- | 52 | {{#each result}} 53 | |{{obsidianHtmlInternalLink path name}} | {{LastUpdate}} | 54 | {{/each}} 55 | postRenderFormat: micromark 56 | ``` 57 | 58 | ### Related Tasks 59 | 60 | ```qatt 61 | query: | 62 | SELECT page, getNoteName(page) AS name, text 63 | FROM obsidian_markdown_tasks 64 | WHERE 65 | text LIKE '%'+noteFileName()+'%' 66 | OR text LIKE '%#projtwo%' 67 | OR text LIKE '%#project/projtwo%' 68 | template: | 69 | | File | Task | 70 | | ---- | ---- | 71 | {{#each result}} 72 | |{{obsidianHtmlInternalLink page name}} | {{text}}| 73 | {{/each}} 74 | postRenderFormat: micromark 75 | ``` 76 | -------------------------------------------------------------------------------- /docs/examples-tutorials/live-examples/notes/projects/Project One.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav_exclude: true 3 | notetype: project 4 | version: 2 5 | project-name: Project One 6 | created: 2023-01-01T09:32:16-08:00 7 | tags: 8 | - project 9 | - area/career 10 | - project/projone 11 | subtitle: This is project one which is very important 12 | aliases: 13 | - projone 14 | status: Waiting 15 | area: Career 16 | priority: "2" 17 | --- 18 | 19 | # Project One 20 | 21 | ## Project Info 22 | 23 | ## Thoughts 24 | 25 | ## Resources 26 | 27 | ## Review questions 28 | 29 | - Problem Statement 30 | - Success Metrics 31 | - In Scope 32 | - Out of Scope 33 | - Risks 34 | - Open Questions 35 | 36 | ## Tasks 37 | 38 | - [ ] Fill out overview for Project One 39 | - [ ] Fill out overview for #project/projone 40 | 41 | ## Backlinks 42 | 43 | ```qatt 44 | query: | 45 | SELECT path, getNoteName(path) AS name, moment(stat->mtime).format('YYYY-MM-DD') AS LastUpdate 46 | FROM obsidian_notes 47 | WHERE content LIKE '%'+noteFileName()+'%' AND getNoteName(path) <> noteFileName() 48 | template: | 49 | | File | Last Modified | 50 | | ---- | ------------- | 51 | {{#each result}} 52 | |{{obsidianHtmlInternalLink path name}} | {{LastUpdate}} | 53 | {{/each}} 54 | postRenderFormat: micromark 55 | ``` 56 | 57 | ### Related Tasks 58 | 59 | ```qatt 60 | query: | 61 | SELECT page, getNoteName(page) AS name, text 62 | FROM obsidian_markdown_tasks 63 | WHERE 64 | text LIKE '%'+noteFileName()+'%' 65 | OR text LIKE '%#projone%' 66 | OR text LIKE '%#project/projone%' 67 | template: | 68 | | File | Task | 69 | | ---- | ---- | 70 | {{#each result}} 71 | |{{obsidianHtmlInternalLink page name}} | {{text}}| 72 | {{/each}} 73 | postRenderFormat: micromark 74 | ``` 75 | 76 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Stringify.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | 3 | /* 4 | // >> id='docs-handlebars-helper-stringify' options='file=templates/hb-helpers/stringify.md' 5 | title: stringify value 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `stringify`\-helper will convert the referenced object to a JSON string. If a cyclical object is used the helper will return `[Cyclical]` for the nested instances to prevent infinite recursion. 10 | 11 | ```handlebars 12 | {{{stringify complexObject}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | complexObject: { 20 | "this": "is", 21 | "a": "complex", 22 | "object": "with", 23 | "lots": "of", 24 | "things": "in", 25 | "it": "!" 26 | } 27 | } 28 | ``` 29 | 30 | will result in: 31 | 32 | ```text 33 | { 34 | complexObject: { 35 | "this": "is", 36 | "a": "complex", 37 | "object": "with", 38 | "lots": "of", 39 | "things": "in", 40 | "it": "!" 41 | } 42 | } 43 | ``` 44 | 45 | // << docs-handlebars-helper-stringify 46 | */ 47 | export function stringify(value: any) { 48 | const seenObjects: any[] = []; 49 | function inspectElement(_key: any, value: any): any { 50 | if (detectCycle(value)) { 51 | return '[Cyclical]'; 52 | } 53 | 54 | return value; 55 | } 56 | 57 | function detectCycle(object: any): boolean { 58 | if (object && (typeof object === 'object')) { 59 | for (const r of seenObjects) { 60 | if (r === object) { 61 | return true; 62 | } 63 | } 64 | 65 | seenObjects.push(object); 66 | } 67 | 68 | return false; 69 | } 70 | 71 | return new Handlebars.SafeString(JSON.stringify(value, inspectElement, 2)); 72 | } 73 | -------------------------------------------------------------------------------- /docs/queries/sql-functions/wikilinkhasdisplayname.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: SQL Functions 4 | grand_parent: Writing Queries 5 | title: wikiLinkHasDisplayName(string) 6 | --- 7 | There are multiple functions to help with the parsing of the wiki links in a string. 8 | 9 | This is the signature of the function you can use to check if the wiki link has a display name. 10 | 11 | - wikiLinkHasDisplayName(value: string): boolean 12 | 13 | In this example it takes the string containing the wiki link and calls the different parsing functions. 14 | 15 | ```text 16 | Need to work on [[Projects/Painting The House|Painting The House]] soon. 17 | ``` 18 | 19 | ````markdown 20 | ```qatt 21 | query: | 22 | SELECT 23 | wikiLinkHasDisplayName('Need to work on [[Projects/Painting The House|Painting The House]] soon.') AS HasDisplay, 24 | wikiLinkHasDisplayName('Need to work on [[Projects/Painting The House]] soon.') AS HasNoDisplay, 25 | IIF(wikiLinkHasDisplayName('Need to work on [[Projects/Painting The House|Painting The House]] soon.'), parseWikiLinkDisplayName('Need to work on [[Projects/Painting The House|Painting The House]] soon.'), parseWikiLinkLocation('Need to work on [[Projects/Painting The House|Painting The House]] soon.')) AS HasDisplayIf, 26 | IIF(wikiLinkHasDisplayName('Need to work on [[Projects/Painting The House]] soon.'), parseWikiLinkDisplayName('Need to work on [[Projects/Painting The House|Will Not show]] soon.'), parseWikiLinkLocation('Need to work on [[Projects/Painting The House]] soon.')) AS HasNoDisplayIf 27 | template: | 28 | {{stringify result}} 29 | ``` 30 | ```` 31 | 32 | will result in: 33 | 34 | ```text 35 | [ { "HasDisplay": true, "HasNoDisplay": false, "HasDisplayIf": "Painting The House", "HasNoDisplayIf": "Projects/Painting The House" } ] 36 | ``` -------------------------------------------------------------------------------- /docs/examples-tutorials/dataview-fields.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Dataview Fields 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The fields in Dataview are stored in a JavaScript Map. So if you have the following content and wanted to extract `author` you can use this example below. 9 | 10 | ## Example Markdown 11 | 12 | ```markdown 13 | ## The Raven 14 | 15 | From [author:: Edgar Allan Poe], written in (published:: 1845) 16 | 17 | Once upon a midnight dreary, while I pondered, weak and weary, 18 | Over many a quaint and curious volume of forgotten lore— 19 | ``` 20 | 21 | ## Query to return the author field value 22 | 23 | In this query we are using `fields->get('author')` to return the value in the Map the corresponds to the `author` key. This is the same as JavaScript, just replace the `.` in the normal JavaScript approach with the `->`. 24 | 25 | 26 | ````markdown 27 | ```qatt 28 | postRenderFormat: html 29 | query: | 30 | SELECT TOP 1 *, 31 | fields->get('author') AS Author 32 | from dataview_pages 33 | where path = '0 Inbox/Dataview Tags Example.md' 34 | template: | 35 |
{{stringify result}}
36 | ``` 37 | ```` 38 | 39 | 40 | ## Rendered Output 41 | 42 | ```markdown 43 |
[
44 |   {
45 |     "Author": "Edgar Allan Poe",
46 |     "path": "0 Inbox/Dataview Tags Example.md",
47 |     "fields": {},
48 |     "frontmatter": {
49 |       "created": "2023-07-30T16:47:59-07:00",
50 |       "modified": "2023-07-30T16:47:59-07:00",
51 |       "aliases": null,
52 |       "tags": "inbox"
53 |     },
54 |     "tags": {},
55 |     "aliases": {},
56 |     "links": [],
57 |     "lists": [],
58 |     "ctime": "2023-07-31T10:52:50.925-07:00",
59 |     "mtime": "2023-07-31T10:52:51.628-07:00",
60 |     "size": 657
61 |   }
62 | ]
63 | ``` 64 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:0-20", 7 | // Features to add to the dev container. More info: https://containers.dev/features. 8 | "features": { 9 | "ghcr.io/devcontainers/features/powershell:1": { 10 | "version": "latest" 11 | } 12 | }, 13 | "customizations": { 14 | "vscode": { 15 | "extensions": [ 16 | "dbaeumer.vscode-eslint", 17 | "DavidAnson.vscode-markdownlint", 18 | "EditorConfig.EditorConfig", 19 | "PKief.material-icon-theme", 20 | "VisualStudioExptTeam.intellicode-api-usage-examples", 21 | "ms-vscode.PowerShell", 22 | "streetsidesoftware.code-spell-checker", 23 | "HenryHeDiff.obsidian-code", 24 | "brpaz.vscode-obsidianmd", 25 | "nhoizey.gremlins", 26 | "oouo-diogo-perdigao.docthis", 27 | "stkb.rewrap", 28 | "stackbreak.comment-divider", 29 | "samverschueren.linter-xo", 30 | "yzhang.markdown-all-in-one", 31 | "github.vscode-github-actions" 32 | ] 33 | } 34 | }, 35 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 36 | // "forwardPorts": [], 37 | // Use 'postCreateCommand' to run commands after the container is created. 38 | "postCreateCommand": "pnpm install && pnpm build" 39 | // Configure tool-specific properties. 40 | // "customizations": {}, 41 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 42 | // "remoteUser": "root" 43 | } 44 | -------------------------------------------------------------------------------- /src/Integrations/DataviewService.ts: -------------------------------------------------------------------------------- 1 | 2 | import {Plugin} from 'obsidian'; 3 | import {Service} from '@ophidian/core'; 4 | import {LoggingService} from 'lib/LoggingService'; 5 | import {getAPI, isPluginEnabled, type PageMetadata} from 'obsidian-dataview'; 6 | 7 | /** 8 | * This service handles the integration with the Dataview plugin. 9 | * 10 | * @export 11 | * @class DataviewService 12 | * @extends {Service} 13 | */ 14 | export class DataviewService extends Service { 15 | plugin = this.use(Plugin); 16 | logger = this.use(LoggingService).getLogger('Qatt.DataviewService'); 17 | 18 | /** 19 | * Flag to indicate if the Dataview plugin is enabled. 20 | * 21 | * @memberof DataviewService 22 | */ 23 | public dataViewEnabled = false; 24 | 25 | onload(): void { 26 | if (isPluginEnabled(this.plugin.app)) { 27 | this.dataViewEnabled = true; 28 | } 29 | } 30 | 31 | /** 32 | * Returns a map of all the Dataview pages. 33 | * 34 | * @return {*} {Map} 35 | * @memberof DataviewService 36 | */ 37 | public getDataviewPages(): Map { 38 | if (!this.dataViewEnabled) { 39 | return new Map(); 40 | } 41 | 42 | const dataviewApi = getAPI(this.plugin.app); 43 | if (!dataviewApi) { 44 | return new Map(); 45 | } 46 | 47 | return dataviewApi.index.pages; 48 | } 49 | 50 | /** 51 | * Returns an array of all the Dataview pages. 52 | * 53 | * @return {*} {any[]} 54 | * @memberof DataviewService 55 | */ 56 | public getDataviewPagesArray(): any[] { 57 | const dataViewApi = getAPI(this.plugin.app); 58 | if (dataViewApi) { 59 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument 60 | return dataViewApi ? Array.from(dataViewApi.pages().values) : []; 61 | } 62 | 63 | return []; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /docs/data-tables/obsidian-lists.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 11 3 | 4 | parent: Data Tables 5 | title: Obsidian Lists 6 | --- 7 | 8 | # Obsidian Lists Table 9 | 10 | Table Name: `obsidian_lists` 11 | 12 | | Column Name | Type | Description | 13 | | ----------- | ------- | ----------------------------------------------------------------------------- | 14 | | parent | number | The ID of the parent list item, negative if at root of note and same as line. | 15 | | task | string | Value between the square braces if the list item is a task. | 16 | | content | string | The full content of the line including the list indicator ('-','*') | 17 | | line | number | The line that the list is found on the note. | 18 | | column | number | The column that the list item starts on. | 19 | | path | string | Path of the page the list item is on. | 20 | | modified | number | Time where parent note was last modified. | 21 | | heading | string | The heading that the task is under on the page. | 22 | | note_name | string | The name of the not the task is located on. | 23 | | isTopLevel | boolean | If true it is a top level list item and not nested. | 24 | | text | string | The text part of the list minus the list indicator. | 25 | | isTask | boolean | If it has the braces for a markdown task this will be true. | 26 | | checked | boolean | If the tasks has a value between braces this is true. | 27 | | status | string | Value between the square braces if the list item is a task. | -------------------------------------------------------------------------------- /docs/examples-tutorials/active-tasks-grouped-by-day-live.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: Examples / Tutorials 4 | title: Active tasks grouped by day 5 | exclude: true 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | This example uses the `obsidian_markdown_tasks` table to get all the tasks that have a due date and then uses the `#group` helper in handlebars to render the results. 10 | 11 | It shows moment being used to validate and parse the dates for output. I also shows a more complex output where we are rendering the results direct to html with attributes and callbacks to enable more integration in Obsidian. The `postRenderFormat` property tells the rendering engine not to do any post processing to convert markdown to HTML as the output is already in HTML. 12 | 13 | ## Tasks for query 14 | 15 | - [ ] Task number 1 📅 2023-11-11 📜 16 | - [ ] Task number 2 📅 2023-11-11 📜 17 | - [ ] Task number 3 📅 2023-10-11 📜 18 | - [ ] Task number 4 📅 2023-10-12 📜 19 | 20 | ## Live Result 21 | 22 | ```qatt 23 | postRenderFormat: html 24 | query: | 25 | SELECT 26 | IIF(moment(dueDate, 'YYYY-MM-DD', true).isValid(), moment(dueDate)->format("MMMM Do, YYYY"), 'No Due Date') AS Month, 27 | page, 28 | text, 29 | status, 30 | line, 31 | tags, 32 | doneDate, 33 | priority 34 | FROM obsidian_tasks 35 | WHERE status != 'x' 36 | and text LIKE '%📜%' 37 | and moment(dueDate, 'YYYY-MM-DD', true).isValid() 38 | ORDER BY dueDate asc, priority desc 39 | template: | 40 | {{#group result by="Month"}} 41 |

{{ value }}

42 |
    43 | {{#each items}} 44 |
  • {{ taskCheckbox this }} {{#if (isHighPriority priority)}}{{/if}} {{#micromark inline="true"}} {{text}} [[{{page}}|📝]] {{/micromark}} {{#if (isHighPriority priority)}}{{/if}}
  • 45 | {{/each}} 46 |
47 | {{/group}} 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/TaskCheckbox.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-taskcheckbox' options='file=templates/hb-helpers/taskcheckbox.md' 4 | title: taskCheckbox 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `taskCheckbox`-helper will ... 9 | 10 | ```handlebars 11 | {{taskCheckbox}} 12 | ``` 13 | 14 | when used with this context: 15 | 16 | ```json 17 | { 18 | path: 'notepages/school/My Cool Page', 19 | name: 'My Cool Page is here!' 20 | } 21 | ``` 22 | 23 | will result in: 24 | 25 | ````markdown 26 | ... 27 | ```` 28 | 29 | // << docs-handlebars-helper-taskcheckbox 30 | */ 31 | /* 32 | // >> id='docs-examples-handlebars-taskcheckbox' options='file=examples-tutorials/handlebars/taskcheckbox.md' 33 | title: taskCheckbox Helper 34 | --- 35 | 36 | This uses a simple query to help show what the `taskCheckbox`-helper does when rendering. 37 | 38 | ### Example 39 | 40 | ````markdown 41 | ```qatt 42 | query: | 43 | SELECT 'something to render in a code block. ' AS code 44 | template: | 45 | {{#each result}} 46 | {{taskCheckbox 'text'}} 47 | {{/each}} 48 | ``` 49 | ```` 50 | 51 | ### Live in Vault 52 | 53 | ```qatt 54 | query: | 55 | SELECT 'something to render in a code block. ' AS code 56 | template: | 57 | {{#each result}} 58 | {{taskCheckbox 'text'}} 59 | {{/each}} 60 | ``` 61 | 62 | // << docs-examples-handlebars-taskcheckbox 63 | */ 64 | export function taskCheckbox(value: any) { 65 | let checked = ''; 66 | let classList = 'task-list-item-checkbox'; 67 | let nextStatus = 'x'; 68 | const currentStatus: string = value.status as string; 69 | 70 | if (value.status !== ' ') { 71 | checked = 'checked'; 72 | classList += ' is-checked'; 73 | nextStatus = ' '; 74 | } 75 | 76 | const checkBoxHtml = ``; 77 | return new Handlebars.SafeString(checkBoxHtml); 78 | } 79 | -------------------------------------------------------------------------------- /src/Query/Functions/UpdatePropertyFromList.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='alasql-function-updatepropertyfromlist-snippet' options='file=queries/sql-functions/updatepropertyfromlist.md' 5 | title: updatePropertyFromList(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `updatePropertyFromList` function will return a link that when clicked will prompt the user to select a value from a list. When the user selects a value the property will be updated on the file. 10 | 11 | ````markdown 12 | ```qatt 13 | query: SELECT TOP 1 updatePropertyFromList(frontmatter->priority, path, @[1, 2, 3], 'priority') AS updatePriority FROM obsidian_notes 14 | template: | 15 | {{#each result}}{{updatePriority}}{{/each}} 16 | ``` 17 | ```` 18 | 19 | will result in: 20 | 21 | ```text 22 | 3 23 | ``` 24 | // << alasql-function-updatepropertyfromlist-snippet 25 | */ 26 | export function registerFunctionUpdatePropertyFromList(): void { 27 | alasql.fn.updatePropertyFromList = function (currentValue: string, path: string, options: string[], propertyName: string) { 28 | const suggesterOptions = `['${options.join('\', \'')}']`; 29 | 30 | // Escape single quotes from path. 31 | path = path.replace(/'/g, '\\\''); 32 | 33 | let html = ' { f.${propertyName} = newValue; });}`; 41 | html += '}; fun();'; 42 | html += '">'; 43 | html += currentValue; 44 | html += ''; 45 | 46 | return html; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /docs/data-tables/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 7 3 | 4 | title: Data Tables 5 | has_children: true 6 | --- 7 | To make it simpler to query data some in memory data tables have been created. 8 | 9 | | Table Name | Description | 10 | | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | 11 | | [qatt.ReferenceCalendar](qatt-referencecalendar) | The reference calendar is used to help with date based management, you can directly query the table or use it to join to other data sources. | 12 | | [obsidian_notes](obsidian-notes) | Uses a cached collection of the markdown notes with some properties made available at the top level of the note object as columns. | 13 | | [obsidian_lists](obsidian-lists) | Uses a cached collection of all list items from all notes in the vault. | 14 | | [obsidian_tasks](obsidian-tasks) | Uses a cached collection of all task items from all notes in the vault. | 15 | | [PAGEPROPERTY](pageproperty) | Allows a query to work against a page property that is a collection like `tags` | 16 | | [dataview_pages](dataview/dataview-pages) | This is same as using pages as a data source in Dataview | 17 | | [dataview_tasks](dataview/dataview-tasks) | This is a in memory collection of tasks based off Dataview pages. Details below. | 18 | | [dataview_lists](dataview/dataview-lists) | This is a in memory collection of all list items including list items that contain task markdown based off DataView pages. Details Below | 19 | -------------------------------------------------------------------------------- /src/handlers/EventHandler.ts: -------------------------------------------------------------------------------- 1 | import {type EventRef, debounce, Plugin} from 'obsidian'; 2 | import {LoggingService, type Logger} from 'lib/LoggingService'; 3 | import {Service} from '@ophidian/core'; 4 | 5 | export class EventHandler extends Service { 6 | plugin = this.use(Plugin); 7 | logger = this.use(LoggingService).getLogger('Qatt.EventHandler'); 8 | public debouncePeriod = 1000; 9 | 10 | private creationEvent: EventRef | undefined; 11 | private deletionEvent: EventRef | undefined; 12 | private modificationEvent: EventRef | undefined; 13 | private renameEvent: EventRef | undefined; 14 | 15 | public setup(): void { 16 | this.updateRefreshSettings(); 17 | this.updateCreationEvent(); 18 | this.updateDeletionEvent(); 19 | this.updateModificationEvent(); 20 | this.updateRenameEvent(); 21 | } 22 | 23 | updateCreationEvent(): void { 24 | this.creationEvent = this.plugin.app.vault.on('create', () => { 25 | this.debouncedRefresh(); 26 | }); 27 | this.plugin.registerEvent(this.creationEvent); 28 | } 29 | 30 | updateDeletionEvent(): void { 31 | this.deletionEvent = this.plugin.app.vault.on('delete', () => { 32 | this.debouncedRefresh(); 33 | }); 34 | this.plugin.registerEvent(this.deletionEvent); 35 | } 36 | 37 | updateModificationEvent(): void { 38 | this.modificationEvent = this.plugin.app.vault.on('modify', () => { 39 | this.debouncedRefresh(); 40 | }); 41 | this.plugin.registerEvent(this.modificationEvent); 42 | } 43 | 44 | updateRenameEvent(): void { 45 | this.renameEvent = this.plugin.app.vault.on('rename', () => { 46 | this.debouncedRefresh(); 47 | }); 48 | this.plugin.registerEvent(this.renameEvent); 49 | } 50 | 51 | private debouncedRefresh: () => void = () => null; 52 | private updateRefreshSettings() { 53 | this.debouncedRefresh = debounce( 54 | () => { 55 | // Aging out the old handlers. 56 | // this.logger.info('Triggering qatt:refresh-codeblocks.'); 57 | // this.plugin.app.workspace.trigger('qatt:refresh-codeblocks'); 58 | }, 59 | this.debouncePeriod, 60 | true, 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: "Query All The Things" 7 | text: "" 8 | tagline: Obsidian plugin to allow you to query your notes, lists, tasks and more using SQL and render using handlebars 9 | image: 10 | src: /qatt_logo_v2.png 11 | alt: VitePress 12 | actions: 13 | - theme: brand 14 | text: Getting Started 15 | link: /first-query 16 | - theme: alt 17 | text: Examples 18 | link: /examples-tutorials 19 | - theme: alt 20 | text: Codeblock Reference 21 | link: /codeblock 22 | 23 | features: 24 | - title: Query Note, Lists and Tasks 25 | details: Use the standard SQL query language to query your notes for information, you vault becomes a database. 26 | - title: Render Your Way 27 | details: Using Handlebars you can render the results of your queries how you want, even to separate files for long term retention outside of Obsidian. 28 | - title: Query Markdown Tables, CSV and JSON 29 | details: Via the configuration you can local local or remote Markdown files with tables, CSV files or full JSON object to query and join alongside your data. 30 | 31 | --- 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/examples-tutorials/ttrpg/monster-search-live.md: -------------------------------------------------------------------------------- 1 | --- 2 | searching_type: aasimar 3 | selected_entity: Aasimar Redeemer 4 | --- 5 | 6 | ```qatt 7 | query: | 8 | SELECT TOP 5 9 | FROM (SELECT traits->[join](',') AS MergedTraits, * FROM creatures_b1) 10 | WHERE pageProperty('searching_type')->toLowerCase() IN MergedTraits; 11 | SELECT DISTINCT Traits FROM (SELECT traits->0 AS Traits FROM creatures_b1 12 | UNION ALL 13 | SELECT traits->1 AS Traits FROM creatures_b1 14 | UNION ALL 15 | SELECT traits->2 AS Traits FROM creatures_b1 16 | UNION ALL 17 | SELECT traits->3 AS Traits FROM creatures_b1 18 | UNION ALL 19 | SELECT traits->4 AS Traits FROM creatures_b1 20 | UNION ALL 21 | SELECT traits->5 AS Traits FROM creatures_b1 22 | UNION ALL 23 | SELECT traits->6 AS Traits FROM creatures_b1 24 | UNION ALL 25 | SELECT traits->7 AS Traits FROM creatures_b1 26 | UNION ALL 27 | SELECT traits->8 AS Traits FROM creatures_b1 28 | UNION ALL 29 | SELECT traits->9 AS Traits FROM creatures_b1) 30 | WHERE Traits; 31 | SELECT * 32 | FROM creatures_b1 33 | WHERE pageProperty('selected_entity') = name; 34 | template: | 35 | Type: `INPUT[inlineSelect(option('Any'){{#each result.[1]}},option({{Traits}}){{/each}}):searching_type]` 36 | 37 | | Name | HP | AC | Level | Source | 38 | | --- | --- | --- | --- | --- | 39 | {{#each result.[0]}} 40 | |{{name}}|{{defenses.hp.[0].hp}}|{{defenses.ac.std}}|{{level}}|{{source}}| 41 | {{/each}} 42 | 43 | ## Select Name for Details 44 | `INPUT[inlineSelect(defaultValue('Select Entity'),option('Select Entity'){{#each result.[0]}},option({{name}}){{/each}}):selected_entity]` 45 | 46 | {{#each result.[2]}} 47 | 48 | {{codeBlockHeader 'statblock'}} 49 | image: [[Wikilink To Image]] 50 | name: {{name}} 51 | size: string 52 | type: string 53 | subtype: string 54 | alignment: {{defenses.hp.[0].hp}} 55 | hp: {{trim defenses.hp.[0].hp}} 56 | ac: {{defenses.ac.std}} 57 | speed: {{speed.walk}} 58 | stats: [{{abilityMods.str}}, {{abilityMods.dex}}, {{abilityMods.con}}, {{abilityMods.int}}, {{abilityMods.wis}}, {{abilityMods.cha}}] 59 | {{codeBlockFooter}} 60 | 61 | {{/each}} 62 | 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Micromark.ts: -------------------------------------------------------------------------------- 1 | import {markdown2html} from 'PostRender/MicromarkPostRenderer'; 2 | import Handlebars, {type HelperOptions} from 'handlebars'; 3 | /* 4 | // >> id='docs-handlebars-helper-micromark' options='file=templates/hb-helpers/micromark.md' 5 | title: micromark 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `micromark`-helper renders markdown as HTML using the micromark library. It has one setting which will remove the wrapping `

` tag from the output if inline is set to true. 10 | 11 | ```handlebars 12 | {{{#micromark inline="true"}}} {{{task}}} [[{{{page}}}|📝]] {{{/micromark}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | task: "This is a **thing** to do", 20 | page: "folder/SomePage.md" 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | This is a thing to do 28 | ```` 29 | 30 | If the inline property is not set, then the output will be wrapped in a `

` tag and result in: 31 | 32 | ``` 33 |

This is a thing to do

34 | ``` 35 | 36 | // << docs-handlebars-helper-micromark 37 | */ 38 | /* 39 | // >> id='docs-examples-handlebars-micromark' options='file=examples-tutorials/handlebars/micromark.md' 40 | title: micromark Helper 41 | --- 42 | 43 | This uses a simple query to help show what the `micromark`-helper does when rendering. 44 | 45 | ### Example 46 | 47 | ````markdown 48 | ```qatt 49 | query: | 50 | SELECT 'This is a **thing** to do' AS code 51 | template: | 52 | {{#each result}} 53 | {{{#micromark inline="true"}}}{{{code}}}{{{/micromark}}} 54 | {{/each}} 55 | ``` 56 | ```` 57 | 58 | ### Live in Vault 59 | 60 | ```qatt 61 | query: | 62 | SELECT 'This is a **thing** to do' AS code 63 | template: | 64 | {{#each result}} 65 | {{{#micromark inline="true"}}}{{{code}}}{{{/micromark}}} 66 | {{/each}} 67 | ``` 68 | 69 | // << docs-examples-handlebars-micromark 70 | */ 71 | export function micromark(this: any, options: HelperOptions) { 72 | const inline = options.hash?.inline === 'true'; 73 | const parsedChildTemplate = markdown2html(options.fn(this), inline); 74 | return new Handlebars.SafeString(parsedChildTemplate); 75 | } 76 | -------------------------------------------------------------------------------- /src/lib/SuggesterModal.ts: -------------------------------------------------------------------------------- 1 | import {type FuzzyMatch, FuzzySuggestModal} from 'obsidian'; 2 | 3 | // From https://github.com/SilentVoid13/Templater/blob/9bccec9e34e485be729793ce472e2ed41c09b53a/src/utils/Error.ts#L3 4 | export class SuggesterModalError extends Error { 5 | constructor(message: string, public console_message?: string) { 6 | super(message); 7 | this.name = this.constructor.name; 8 | Error.captureStackTrace(this, this.constructor); 9 | } 10 | } 11 | 12 | // Original from https://github.com/SilentVoid13/Templater/blob/9bccec9e34e485be729793ce472e2ed41c09b53a/src/core/functions/internal_functions/system/SuggesterModal.ts#L4 13 | export class SuggesterModal extends FuzzySuggestModal { 14 | private resolve: (value: T) => void; 15 | private reject: (reason?: SuggesterModalError) => void; 16 | private submitted = false; 17 | 18 | constructor( 19 | private readonly text_items: string[] | ((item: T) => string), 20 | private readonly items: T[], 21 | placeholder: string, 22 | limit?: number, 23 | ) { 24 | super(app); 25 | this.setPlaceholder(placeholder); 26 | if (limit) { 27 | this.limit = limit; 28 | } 29 | } 30 | 31 | getItems(): T[] { 32 | return this.items; 33 | } 34 | 35 | onClose(): void { 36 | if (!this.submitted) { 37 | this.reject(new SuggesterModalError('Cancelled prompt')); 38 | } 39 | } 40 | 41 | selectSuggestion( 42 | value: FuzzyMatch, 43 | evt: MouseEvent | KeyboardEvent, 44 | ): void { 45 | this.submitted = true; 46 | this.close(); 47 | this.onChooseSuggestion(value, evt); 48 | } 49 | 50 | getItemText(item: T): string { 51 | if (this.text_items instanceof Function) { 52 | return this.text_items(item); 53 | } 54 | 55 | return ( 56 | this.text_items[this.items.indexOf(item)] || 'Undefined Text Item' 57 | ); 58 | } 59 | 60 | onChooseItem(item: T): void { 61 | this.resolve(item); 62 | } 63 | 64 | async openAndGetValue( 65 | resolve: (value: T) => void, 66 | reject: (reason?: SuggesterModalError) => void, 67 | ): Promise { 68 | this.resolve = resolve; 69 | this.reject = reject; 70 | this.open(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Render/HandlebarsRenderer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 2 | import {Service} from '@ophidian/core'; 3 | import {Plugin, type TFile} from 'obsidian'; 4 | import Handlebars from 'handlebars'; 5 | import {LoggingService} from 'lib/LoggingService'; 6 | import {type QattCodeBlock} from 'QattCodeBlock'; 7 | import {type IRenderer} from 'Render/IRenderer'; 8 | import * as handlebarsHelpers from 'Render/HandlebarsHelpers'; 9 | 10 | export class HandlebarsRenderer extends Service implements IRenderer { 11 | defaultTemplate = '{{stringify result}}'; 12 | plugin = this.use(Plugin); 13 | logger = this.use(LoggingService).getLogger('Qatt.HandlebarsRenderer'); 14 | 15 | /** 16 | * This will register all the default handlebars helpers that make working 17 | * with the templates easier in Obsidian. 18 | * 19 | * @return {*} {void} 20 | * @memberof HandlebarsRenderer 21 | */ 22 | onload(): void { 23 | this.logger.info('Setting up inbuilt Handlebars helpers'); 24 | 25 | // Iterate through all the exported helpers in the HandlebarsHelpers/index.ts file 26 | for (const handlebarsHelper of Object.entries(handlebarsHelpers)) { 27 | Handlebars.registerHelper(handlebarsHelper[0], handlebarsHelper[1]); 28 | } 29 | 30 | this.logger.info('HandlebarsRenderer loaded'); 31 | } 32 | 33 | public async renderTemplate(codeblockConfiguration: QattCodeBlock, result: any) { 34 | if (codeblockConfiguration.logLevel) { 35 | this.logger.setLogLevel(codeblockConfiguration.logLevel); 36 | } 37 | 38 | let template = this.defaultTemplate; 39 | 40 | if (codeblockConfiguration.templateFile) { 41 | const templateFile = this.plugin.app.vault.getAbstractFileByPath(codeblockConfiguration.templateFile); 42 | const content = (await this.plugin.app.vault.cachedRead(templateFile as TFile)); 43 | template = content; 44 | } else if (codeblockConfiguration.template) { 45 | template = codeblockConfiguration.template ?? ''; 46 | } 47 | 48 | this.logger.debug('rendering compiled template:', template); 49 | const compliedTemplate = Handlebars.compile(template); 50 | 51 | return compliedTemplate({result}); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /docs/data-tables/legacy-tables/obsidian-markdown-tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 92 3 | 4 | parent: Data Tables 5 | title: Obsidian Markdown Tasks 6 | --- 7 | 8 | # Obsidian Markdown Tasks (obsidian_markdown_tasks) 9 | 10 | Please use [obsidian_tasks](../obsidian-tasks) going forward, this table will be eventually removed. 11 | 12 | | Column Name | Type | Description | 13 | | -------------- | -------- | ----------------------------------------------------------------- | 14 | | path | string | Full path to the page the task is located on. | 15 | | modified | number | Time where parent note was last modified. | 16 | | task | string | | 17 | | status | string | The value of the character between the braces for the checkbox. | 18 | | content | string | The full tasks string with no parsing or stripping of characters. | 19 | | text | string | The text only part of the task, without list and checkbox prefix. | 20 | | line | number | The line number the task can be found on for the page. | 21 | | tags | string[] | Collection of tags. | 22 | | tagsNormalized | string[] | Collection of tags that have been converted to lowercase. | 23 | | dueDate | string | When the task is due ['📅', 'due::'] | 24 | | doneDate | string | When the task is done ['✅', 'completion::'] | 25 | | startDate | string | When the task can be started ['🛫', 'start::'] | 26 | | createDate | string | When the task is created ['➕', 'created::'] | 27 | | scheduledDate | string | When the task is scheduled next ['⏳', 'scheduled::'] | 28 | | doDate | string | When to do the task ['💨', 'do::'] | 29 | | priority | number | Priority of task based on indicator ['⏫🔼🔽', 'priority::'] | 30 | | cleanTask | string | The task string with all metadata removed. | 31 | -------------------------------------------------------------------------------- /.config/esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import process from 'process'; 2 | import esbuild from 'esbuild'; 3 | import builtins from 'builtin-modules'; 4 | import copyFilePlugin from 'esbuild-plugin-copy-file'; 5 | 6 | const banner = `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source visit the plugins github repository 9 | */ 10 | `; 11 | 12 | // If OBSIDIAN_ROOT is set in the environment then copy the files ove to make debugging faster. 13 | let copyFilesConfig = { 14 | before: { 15 | // copy before bundling 16 | }, 17 | after: { 18 | './dist/manifest.json': './manifest.json', 19 | }, 20 | }; 21 | 22 | if (process.env.OBSIDIAN_ROOT !== undefined) { 23 | console.log(`OBSIDIAN_ROOT set to ${process.env.OBSIDIAN_ROOT}`); 24 | copyFilesConfig.after[`${process.env.OBSIDIAN_ROOT}/.obsidian/plugins/qatt/manifest.json`] = './manifest.json'; 25 | copyFilesConfig.after[`${process.env.OBSIDIAN_ROOT}/.obsidian/plugins/qatt/main.js`] = './dist/main.js'; 26 | copyFilesConfig.after[`${process.env.OBSIDIAN_ROOT}/.obsidian/plugins/qatt/styles.css`] = './dist/styles.css'; 27 | } else { 28 | console.log(`OBSIDIAN_ROOT is not set`); 29 | } 30 | 31 | const prod = process.argv[2] === 'production'; 32 | 33 | const context = await esbuild.context({ 34 | banner: { 35 | js: banner, 36 | }, 37 | bundle: true, 38 | entryPoints: ['main.ts'], 39 | external: [ 40 | 'obsidian', 41 | 'electron', 42 | '@codemirror/autocomplete', 43 | '@codemirror/collab', 44 | '@codemirror/commands', 45 | '@codemirror/language', 46 | '@codemirror/lint', 47 | '@codemirror/search', 48 | '@codemirror/state', 49 | '@codemirror/view', 50 | '@lezer/common', 51 | '@lezer/highlight', 52 | '@lezer/lr', 53 | ...builtins, 54 | ], 55 | format: 'cjs', 56 | logLevel: 'info', 57 | minify: prod, 58 | outfile: 'dist/main.js', 59 | plugins: [copyFilePlugin(copyFilesConfig)], 60 | sourcemap: prod ? false : 'inline', 61 | target: 'es2018', 62 | treeShaking: true, 63 | }); 64 | 65 | if (prod) { 66 | await context.rebuild(); 67 | process.exit(0); 68 | } else { 69 | await context.watch(); 70 | } 71 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/ObsidianHtmlInternalLink.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-obsidianhtmlinternallink' options='file=templates/hb-helpers/obsidianhtmlinternallink.md' 4 | title: obsidianHtmlInternalLink 5 | --- 6 | 7 | The `obsidianHtmlInternalLink` helper takes a path and a label and returns a link 8 | matches the default HTML that obsidian uses for internal links. If ths files ends 9 | in `.md` then the extension is removed from the link. 10 | 11 | ```handlebars 12 | {{{obsidianHtmlInternalLink internalPath basename}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | path: "notepages/school/My Cool Page", 20 | name: "My Cool Page is here!" 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ```html 27 | My Cool Page is here! 28 | ``` 29 | 30 | // << docs-handlebars-helper-obsidianhtmlinternallink 31 | */ 32 | /* 33 | // >> id='docs-examples-handlebars-obsidianhtmlinternallink' options='file=examples-tutorials/handlebars/obsidianhtmlinternallink.md' 34 | title: obsidianHtmlInternalLink Helper 35 | --- 36 | # {{ $frontmatter.title }} 37 | 38 | This uses a simple query to help show what the obsidianHtmlInternalLink helper does when rendering. 39 | 40 | ### Example 41 | 42 | ````markdown 43 | ```qatt 44 | query: | 45 | SELECT TOP 1 path, basename FROM obsidian_notes 46 | template: | 47 | {{#each result}} 48 | {{obsidianHtmlInternalLink path basename}} 49 | {{/each}} 50 | ``` 51 | ```` 52 | 53 | ### Live in Vault 54 | 55 | ```qatt 56 | query: | 57 | SELECT TOP 1 path, basename FROM obsidian_notes 58 | template: | 59 | {{#each result}} 60 | {{obsidianHtmlInternalLink path basename}} 61 | {{/each}} 62 | ``` 63 | 64 | // << docs-examples-handlebars-obsidianhtmlinternallink 65 | */ 66 | export function obsidianHtmlInternalLink(link: string, label: string) { 67 | return new Handlebars.SafeString(`${label}`); 68 | } 69 | -------------------------------------------------------------------------------- /docs/data-tables/dataview/dataview-tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 25 3 | 4 | parent: Data Tables 5 | title: Dataview Tasks 6 | --- 7 | # Dataview Tasks (dataview_tasks) 8 | 9 | If a property is not found in the task body it will be set to undefined. This table 10 | is backed by Dataview and will be refreshed when Dataview is refreshed. 11 | 12 | | Column Name | Type | Description | 13 | | -------------- | ------------ | ------------------------------------------------------------------------------------------------------- | 14 | | page | string | The full path including the page name and extension | 15 | | task | string | The full text of the task, any tags or dates are still in this. This may be multiple lines of markdown. | 16 | | status | string | The text in between the brackets of the '[ ]' task indicator ('[X]' would yield 'X', for example.) | 17 | | line | number | The line that this list item starts on in the file. | 18 | | tags | string array | Array of tags | 19 | | tagsNormalized | string array | Array of tags all in lowercase | 20 | | dueDate | string | Due date of the task as string. | 21 | | doneDate | string | Done date of the task as string. | 22 | | startDate | string | Start date of the task as string. | 23 | | createDate | string | Create date of the task as string. | 24 | | scheduledDate | string | Schedule date of the task as string. | 25 | | priority | number | Priority of the task with 1 as highest and 3 as lowest | 26 | -------------------------------------------------------------------------------- /docs/first-query.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | title: 🐣 Your First Query 4 | --- 5 | 6 | # {{ $frontmatter.title }} 7 | 8 | The main query language backing the plugin is SQL based. There are fantastic resources out there to help you work with and understand SQL. The engine supports the large majority of statements including the ability for you to make your own tables in memory. 9 | 10 | To get started the plugin expects that you have a codeblock with `qatt` as the type. Below is the simplest starting query which lists the latest page that was created. All the configuration in the code block is based off the YAML structure so all guidance from [https://yaml.org/](https://yaml.org/) is valid. 11 | 12 | In the example below you have the `query`, as noted this is based off SQL so sites like [SQL Tutorial (w3schools.com)](https://www.w3schools.com/sql/) will be helpful if you are not familiar with the language or its been a while. This query will select the first record found when ordered by the files created time where the date is in descending order so the most recent will be displayed first. The FROM like indicates it is from the internal markdown files collection stored by Obsidian natively. 13 | 14 | 15 | ````markdown 16 | ```qatt 17 | query: | 18 | SELECT TOP 5 * 19 | FROM obsidian_notes 20 | ORDER BY stat->mtime DESC 21 | template: | 22 | {{#each result}} 23 | - [[{{path}}\|{{basename}}]] 24 | {{/each}} 25 | ``` 26 | ```` 27 | 28 | 29 | 30 | 31 | Once the query above has returned a result the template will then take the result and render an output by iterating over all the rows in the result and where `path` and `basename` have a value a link will be generated using a markdown syntax. 32 | 33 | Once a query result has been rendered by the template and backing template engine, in this case Handlebars, it will be passed to Obsidian to be shown in the UI. All output in Obsidian is expected to be in HTML. To make this simpler by default `micromark` is enabled as a post processor. The output from the template will be processed and any markdown found will be converted to HTML for output. 34 | 35 | ### Live Example if opened in Vault 36 | 37 | ```qatt 38 | query: | 39 | SELECT TOP 5 * 40 | FROM obsidian_notes 41 | ORDER BY stat->mtime DESC 42 | template: | 43 | {{#each result}} 44 | - [[{{path}}\|{{basename}}]] 45 | {{/each}} 46 | ``` 47 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/TaskCheckboxWithAppend.ts: -------------------------------------------------------------------------------- 1 | import Handlebars, {type HelperOptions} from 'handlebars'; 2 | /* 3 | // >> id='docs-handlebars-helper-taskcheckboxwithappend' options='file=templates/hb-helpers/taskcheckboxwithappend.md' 4 | title: taskCheckboxWithAppend 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | The `taskCheckboxWithAppend`-helper will ... 9 | 10 | ```handlebars 11 | {{taskCheckboxWithAppend}} 12 | ``` 13 | 14 | when used with this context: 15 | 16 | ```json 17 | { 18 | path: 'notepages/school/My Cool Page', 19 | name: 'My Cool Page is here!' 20 | } 21 | ``` 22 | 23 | will result in: 24 | 25 | ````markdown 26 | ... 27 | ```` 28 | 29 | // << docs-handlebars-helper-taskcheckboxwithappend 30 | */ 31 | /* 32 | // >> id='docs-examples-handlebars-taskcheckboxwithappend' options='file=examples-tutorials/handlebars/taskcheckboxwithappend.md' 33 | title: taskCheckboxWithAppend Helper 34 | --- 35 | 36 | This uses a simple query to help show what the `taskCheckboxWithAppend`-helper does when rendering. 37 | 38 | ### Example 39 | 40 | ````markdown 41 | ```qatt 42 | query: | 43 | SELECT 'something to render in a code block. ' AS code 44 | template: | 45 | {{#each result}} 46 | {{taskCheckboxWithAppend 'text'}} 47 | {{/each}} 48 | ``` 49 | ```` 50 | 51 | ### Live in Vault 52 | 53 | ```qatt 54 | query: | 55 | SELECT 'something to render in a code block. ' AS code 56 | template: | 57 | {{#each result}} 58 | {{taskCheckboxWithAppend 'text'}} 59 | {{/each}} 60 | ``` 61 | 62 | // << docs-examples-handlebars-taskcheckboxwithappend 63 | */ 64 | export function taskCheckboxWithAppend(value: any) { 65 | let checked = ''; 66 | let classList = 'task-list-item-checkbox'; 67 | let nextStatus = 'x'; 68 | const currentStatus: string = value.status as string; 69 | const appendValue: string = value.append as string; 70 | 71 | if (value.status !== ' ') { 72 | checked = 'checked'; 73 | classList += ' is-checked'; 74 | nextStatus = ' '; 75 | } 76 | 77 | const checkBoxHtml = ``; 78 | return new Handlebars.SafeString(checkBoxHtml); 79 | } 80 | -------------------------------------------------------------------------------- /docs/data-tables/obsidian-tasks.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 12 3 | 4 | parent: Data Tables 5 | title: Obsidian Tasks 6 | --- 7 | 8 | # Obsidian Tasks Table 9 | 10 | Table Name: `obsidian_tasks` 11 | 12 | | Column Name | Type | Description | 13 | | -------------- | -------- | ----------------------------------------------------------------- | 14 | | path | string | Full path to the note the task is located on. | 15 | | note_name | string | The name of the not the task is located on. | 16 | | modified | number | Time where parent note was last modified. | 17 | | task | string | The value of the character between the braces for the checkbox. | 18 | | status | string | The value of the character between the braces for the checkbox. | 19 | | content | string | The full tasks string with no parsing or stripping of characters. | 20 | | text | string | The text only part of the task, without list and checkbox prefix. | 21 | | line | number | The line number the task can be found on for the page. | 22 | | heading | string | The heading that the task is under on the page. | 23 | | tags | string[] | Collection of tags. | 24 | | tagsNormalized | string[] | Collection of tags that have been converted to lowercase. | 25 | | dueDate | string | When the task is due ['📅', 'due::'] | 26 | | doneDate | string | When the task is done ['✅', 'completion::'] | 27 | | startDate | string | When the task can be started ['🛫', 'start::'] | 28 | | createDate | string | When the task is created ['➕', 'created::'] | 29 | | scheduledDate | string | When the task is scheduled next ['⏳', 'scheduled::'] | 30 | | doDate | string | When to do the task ['💨', 'do::'] | 31 | | priority | number | Priority of task based on indicator ['⏫🔼🔽', 'priority::'] | 32 | | cleanTask | string | The task string with all metadata removed. | 33 | | blockLink | string | manually specified block Link for the task | 34 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs-v2.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a VitePress site to GitHub Pages 2 | # 3 | name: Deploy VitePress site to Pages 4 | 5 | on: 6 | # Runs on pushes targeting the `main` branch. Change this to `master` if you're 7 | # using the `master` branch as the default branch. 8 | push: 9 | branches: [main] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 15 | permissions: 16 | contents: read 17 | pages: write 18 | id-token: write 19 | 20 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 21 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 22 | concurrency: 23 | group: pages 24 | cancel-in-progress: false 25 | 26 | jobs: 27 | # Build job 28 | build: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | with: 34 | fetch-depth: 0 # Not needed if lastUpdated is not enabled 35 | 36 | - uses: pnpm/action-setup@v3 # Uncomment this if you're using pnpm 37 | with: 38 | version: 9 39 | 40 | # - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun 41 | - name: Setup Node 42 | uses: actions/setup-node@v4 43 | with: 44 | node-version: 20 45 | cache: pnpm # or pnpm / yarn 46 | 47 | - name: Setup Pages 48 | uses: actions/configure-pages@v4 49 | 50 | - name: Install dependencies 51 | run: pnpm install 52 | 53 | - name: Build generated documentation 54 | run: pnpm run build:docs 55 | 56 | - name: Build with VitePress 57 | run: pnpm run docs:build # or pnpm docs:build / yarn docs:build / bun run docs:build 58 | 59 | - name: Upload artifact 60 | uses: actions/upload-pages-artifact@v3 61 | with: 62 | path: docs/.vitepress/dist 63 | 64 | # Deployment job 65 | deploy: 66 | environment: 67 | name: github-pages 68 | url: ${{ steps.deployment.outputs.page_url }} 69 | needs: build 70 | runs-on: ubuntu-latest 71 | name: Deploy 72 | steps: 73 | - name: Deploy to GitHub Pages 74 | id: deployment 75 | uses: actions/deploy-pages@v4 76 | -------------------------------------------------------------------------------- /src/typings/obsidian-ex.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/filename-case */ 2 | 3 | /* eslint-disable @typescript-eslint/naming-convention */ 4 | /* eslint-disable @typescript-eslint/no-empty-interface */ 5 | import _ from 'obsidian'; 6 | import {type DataviewApi} from 'obsidian-dataview'; 7 | 8 | export interface EventRef { 9 | 10 | } 11 | declare module 'obsidian' { 12 | interface MetadataCache { 13 | trigger (...args: Parameters): void; 14 | trigger (name: string, ...data: any[]): void; 15 | on (name: 'dataview:index-ready' | 'dataview:refresh-views', callback: () => void, ctx?: any): EventRef; 16 | } 17 | 18 | interface App { 19 | appId: string; 20 | plugins: { 21 | enabledPlugins: Set; 22 | }; 23 | } 24 | 25 | interface Plugin { 26 | updateWindowLevelFunctions (): void; 27 | announceUpdate (): void; 28 | } 29 | 30 | interface Menu { 31 | dom: HTMLElement; 32 | items: MenuItem[]; 33 | onMouseOver: (evt: MouseEvent) => void; 34 | } 35 | 36 | interface MenuItem { 37 | callback: () => void; 38 | dom: HTMLElement; 39 | setSubmenu: () => Menu; 40 | disabled: boolean; 41 | setWarning: (warning: boolean) => void; 42 | } 43 | 44 | export interface PluginManifest { 45 | releases: Array>; 46 | } 47 | 48 | interface Workspace { 49 | /** Sent to rendered dataview components to tell them to possibly refresh */ 50 | on (name: 51 | 'qatt:refresh-codeblocks' | 52 | 'dataview:refresh-views' | 53 | 'qatt:notes-store-update' | 54 | 'qatt:dataview-store-update' | 55 | 'qatt:all-notes-loaded' | 56 | 'qatt:force-cache-reload' | 57 | 'qatt:data-refreshtables-completed' | 58 | 'qatt:data-local-database-setup-completed' | 59 | 'qatt:main-settings-first-load-completed' | 60 | 'qatt:main-settings-reload-completed', callback: () => void, ctx?: any): EventRef; 61 | } 62 | } 63 | 64 | declare global { 65 | interface Window { 66 | 67 | DataviewAPI?: DataviewApi; 68 | _qatt: any; 69 | qattUpdateOriginalTask: (page: string, line: number, currentStatus: string, nextStatus: string) => void; 70 | qattUpdateOriginalTaskWithAppend: (page: string, line: number, currentStatus: string, nextStatus: string, append: string) => void; 71 | qattUpdateOriginalTaskWithDoneDate: (page: string, line: number, currentStatus: string, nextStatus: string) => void; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Render/HandlebarsHelpers/Obsidian.ts: -------------------------------------------------------------------------------- 1 | import {MarkdownPreviewView, Component} from 'obsidian'; 2 | import Handlebars, {type HelperOptions} from 'handlebars'; 3 | /* 4 | // >> id='docs-handlebars-helper-obsidian' options='file=templates/hb-helpers/obsidian.md' 5 | title: obsidian 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `obsidian`-helper renders markdown as HTML using the obsidian markdown engine. It has one setting which will remove the wrapping `

` tag from the output if inline is set to true. 10 | 11 | ```handlebars 12 | {{{#obsidian inline="true"}}} {{{task}}} [[{{{page}}}|📝]] {{{/obsidian}}} 13 | ``` 14 | 15 | when used with this context: 16 | 17 | ```json 18 | { 19 | task: "This is a **thing** to do", 20 | page: "folder/SomePage.md" 21 | } 22 | ``` 23 | 24 | will result in: 25 | 26 | ````markdown 27 | This is a thing to do 28 | ```` 29 | 30 | If the inline property is not set, then the output will be wrapped in a `

` tag and result in: 31 | 32 | ``` 33 |

This is a thing to do

34 | ``` 35 | 36 | // << docs-handlebars-helper-obsidian 37 | */ 38 | /* 39 | // >> id='docs-examples-handlebars-obsidian' options='file=examples-tutorials/handlebars/obsidian.md' 40 | title: obsidian Helper 41 | --- 42 | 43 | This uses a simple query to help show what the `obsidian`-helper does when rendering. 44 | 45 | ### Example 46 | 47 | ````markdown 48 | ```qatt 49 | query: | 50 | SELECT 'This is a **thing** to do' AS code 51 | template: | 52 | {{#each result}} 53 | {{{#obsidian inline="true"}}}{{{code}}}{{{/obsidian}}} 54 | {{/each}} 55 | ``` 56 | ```` 57 | 58 | ### Live in Vault 59 | 60 | ```qatt 61 | query: | 62 | SELECT 'This is a **thing** to do' AS code 63 | template: | 64 | {{#each result}} 65 | {{{#obsidian inline="true"}}}{{{code}}}{{{/obsidian}}} 66 | {{/each}} 67 | ``` 68 | 69 | // << docs-examples-handlebars-obsidian 70 | */ 71 | export function obsidian(this: any, options: HelperOptions) { 72 | const inline = options.hash?.inline === 'true'; 73 | 74 | const element = document.createElement('span'); 75 | MarkdownPreviewView.renderMarkdown(options.fn(this), element, '', new Component()).catch(error => { 76 | console.error(error); 77 | }); 78 | 79 | if (inline) { 80 | return new Handlebars.SafeString(element.innerHTML.replace(/

|<\/p>/g, '')); 81 | } 82 | 83 | return new Handlebars.SafeString(element.innerHTML); 84 | } 85 | -------------------------------------------------------------------------------- /docs/output-generation.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 6 3 | 4 | title: Output Generation 5 | --- 6 | # {{ $frontmatter.title }} 7 | 8 | This page is to help you understand the process the plugin uses to generate the rendered output using the configuration in the code block. 9 | 10 | 1. When a render request is made for a page in Obsidian the plugin is registered to handle any code blocks with the `qatt` tag applied to it. 11 | 2. The configuration is taken and parsed assuming it is valid YAML syntax. From this the configuration is taken for the query, rendering and other settings used for that code block. 12 | 3. From the configuration the [logLevel](codeblock.md#logLevel) is parsed and used for all the logging of just that codeblock. 13 | 4. The plugin then registers for events related to pages being modified in the vault. If that happens it will run the query and render again. 14 | 5. The query is then run using the [query](codeblock.md#query) value against the specified [queryEngine](codeblock.md#queryEngine). By default this is AlaSQL. 15 | 6. If there are errors in the query, the messages are rendered for the user to fix. 16 | 7. The results from the query are then taken by the render engine specified in the default configuration or specified in the codeblock using the [renderEngine](codeblock.md#renderEngine) property. 17 | 8. The result of the render engine is then taken and the default post renderer or the post renderer specified by the [postRenderFormat](codeblock.md#postRenderFormat) takes the output and makes any changes. 18 | 9. The output from the post renderer is then placed in the inner HTML block of the code block and shown for the user. 19 | 20 | ## Key things to think about 21 | 22 | 1. All the output has to be rendered by Obsidian/Electron at the end. It is always expected to be HTML. So if you are using the rendering engine to output markdown you need to use `micromark` or `markdown` in the post processing. 23 | 2. The query engine is querying a collection of objects for the most part. This is slightly different than the normal approach using SQL where it queries tables with columns and rows. This does allow you to use JavaScript notation and dig into the object properties which can increase the power of your queries. When drilling into objects or using functions replace the `.` used in JavaScript with `->`. 24 | 3. Rendering is using [Handlebars](templates/index.md), there are the default helpers as well as some custom ones, this can increase over time. 25 | -------------------------------------------------------------------------------- /src/Query/Functions/UpdatePropertyFromPrompt.ts: -------------------------------------------------------------------------------- 1 | import alasql from 'alasql'; 2 | 3 | /* 4 | // >> id='alasql-function-updatepropertyfromprompt-snippet' options='file=queries/sql-functions/updatepropertyfromprompt.md' 5 | title: updatePropertyFromPrompt(value) 6 | --- 7 | # {{ $frontmatter.title }} 8 | 9 | The `updatePropertyFromPrompt` function will return a link that when clicked will prompt the user to enter a value. 10 | The user can then enter a value and the property will be updated on the file. 11 | 12 | ````markdown 13 | ```qatt 14 | query: SELECT TOP 1 updatePropertyFromPrompt('Enter email alias', frontmatter->email, path, 'email') AS updateEmail FROM obsidian_notes 15 | template: | 16 | {{#each result}}{{updateEmail}}{{/each}} 17 | ``` 18 | ```` 19 | 20 | will result in: 21 | 22 | ```text 23 | alias 24 | ``` 25 | 26 | The links by default will be dotted and not underlined. This can be changed by adding a snippet for the CSS class `qatt-link` to a CSS file. 27 | // << alasql-function-updatepropertyfromprompt-snippet 28 | */ 29 | export function registerFunctionUpdatePropertyFromPrompt(): void { 30 | alasql.fn.updatePropertyFromPrompt = function (promptText: string, currentValue: string, path: string, propertyName: string, multiLine: boolean) { // eslint-disable-line max-params 31 | if (multiLine === undefined) { 32 | multiLine = false; 33 | } 34 | 35 | // Escape single quotes from path. 36 | path = path.replace(/'/g, '\\\''); 37 | 38 | const multiLineValue = multiLine ? 'true' : 'false'; 39 | 40 | let html = ' { f.${propertyName} = newValue; });}`; 48 | html += '}; fun();'; 49 | html += '">'; 50 | html += currentValue; 51 | html += ''; 52 | 53 | return html; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /tests/AlaSQLQuery.test.ts: -------------------------------------------------------------------------------- 1 | 2 | /* eslint-disable @typescript-eslint/no-floating-promises */ 3 | import {describe, it} from 'node:test'; 4 | import assert from 'node:assert'; 5 | 6 | describe('alasql helpers', () => { 7 | it('parse wiki link with path and alias', () => { 8 | const result = parseWikiLinkFromText('sdf sd fsd fsdf [[the/link/to/basename|Display Name]] sdf sd fsd fsd f'); 9 | 10 | console.log(JSON.stringify(result)); 11 | assert.strictEqual(result, 'the/link/to/basename|Display Name'); 12 | const linkAndDisplay = splitOnUnescapedPipe(result); 13 | assert.strictEqual(linkAndDisplay[0], 'the/link/to/basename'); 14 | assert.strictEqual(linkAndDisplay[1], 'Display Name'); 15 | }); 16 | 17 | it('parse wiki link with path', () => { 18 | const re = /\[\[([^[\]]*?)\]\]/u; 19 | const result = re.exec('[[Projects/Painting The House]]'); 20 | console.log(JSON.stringify(result)); 21 | assert.strictEqual(result?.length, 2); 22 | if (result) { 23 | assert.strictEqual(result[1], 'Projects/Painting The House'); 24 | const linkAndDisplay = splitOnUnescapedPipe(result[1]); 25 | assert.strictEqual(linkAndDisplay[0], 'Projects/Painting The House'); 26 | } 27 | }); 28 | 29 | it('has display name check', () => { 30 | const result = parseWikiLinkFromText('[[Projects/Painting The House|Painting The House]]'); 31 | const linkAndDisplay = splitOnUnescapedPipe(result!); 32 | assert.strictEqual(linkAndDisplay.length === 2 && linkAndDisplay[1] !== undefined, true); 33 | 34 | const result2 = parseWikiLinkFromText('[[Projects/Painting The House]]'); 35 | const linkAndDisplay2 = splitOnUnescapedPipe(result2!); 36 | assert.strictEqual(linkAndDisplay2.length === 2 && linkAndDisplay2[1] === undefined, true); 37 | }); 38 | 39 | /** Split on unescaped pipes in an inner link. */ 40 | function splitOnUnescapedPipe(link: string): [string, string | undefined] { 41 | let pipe = -1; 42 | while ((pipe = link.indexOf('|', pipe + 1)) >= 0) { 43 | if (pipe > 0 && link[pipe - 1] === '\\') { 44 | continue; 45 | } 46 | 47 | return [link.slice(0, Math.max(0, pipe)).replace(/\\\|/g, '|'), link.slice(Math.max(0, pipe + 1))]; 48 | } 49 | 50 | return [link.replace(/\\\|/g, '|'), undefined]; 51 | } 52 | 53 | function parseWikiLinkFromText(text: string): string | undefined { 54 | const re = /\[\[([^[\]]*?)\]\]/u; 55 | 56 | const result = re.exec(text); 57 | if (result) { 58 | return result[1]; 59 | } 60 | } 61 | }); 62 | -------------------------------------------------------------------------------- /docs/examples-tutorials/ttrpg/monster-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | parent: TTRPG Examples 4 | grand_parent: Examples / Tutorials 5 | title: Monster Search 6 | --- 7 | 8 | Search through your Fantasy Statblocks bestiary by monster CR and type using a local or external JSON file. 9 | 10 | ## How to add 11 | 12 | 1. Make sure that Meta-Bind, Fantasy Statblocks and Query All the Things are is installed 13 | 2. In the settings for Query All the Things 14 | 3. Make sure Dataview JS queries are turned on 15 | 4. Paste the code from `monster_search.js` inside a `dataviewjs` code block (triple \`s) 16 | 5. Add the following properties to the note's frontmatter. They shouldn't need any values to be given. 17 | - searching_cr 18 | - searching_type 19 | - searching_open_bestiary 20 | 6. Happy searching! 21 | 22 | ## Options and usage 23 | 24 | The creatures are returned in a table with their name, HP, AC, CR, and source, in no particular order. If there is a note with the same name as a creature, the name column will link to that note. If there isn't one, upon clicking it, a Fantasy Statblocks bestiary panel will be opened and it will be shown there. If there was already one open, it will just show it there. 25 | 26 | The filters described below work like an 'AND' - if you search for CR 4 and Undead, you will only get CR4 undead creatures. 27 | 28 | ### Filtering by CR 29 | Type the CR of the creature in the 'cr' search box, e.g 1/8, 1/4, 1/2, or a whole number like 5. This will filter the results to creatures of that CR only. If no creatures match that CR, it will find the closest CR to the given number (either up or down, whichever is closer) which _do_ contain monsters, and return those. 30 | 31 | ### Filtering by type 32 | Select the type of the creature in the 'type' select. Only creatures of this type will be returned. 33 | 34 | ### Selecting a random creature 35 | By default, one creature from the results is selected at random, to let you quickly choose a random creature from the list for inspiration. Its name will be listed in bold, and appear as `> Name <` (between ><). If you want to get a new creature from the same list, you can click the 'Select random' button to get a new one. If you don't care about the random selection, you can just ignore it. 36 | 37 | ### Opening bestiary panel 38 | If 'open bestiary panel' is toggled on, whenever a search is made, the creature randomly selected will be shown in a Fantasy Statblocks bestiary panel. One will be opened if there isn't one open already, and the right bar will always be shown if it was hidden. If toggled off, this won't happen. 39 | -------------------------------------------------------------------------------- /src/Data/JsonLoaderService.ts: -------------------------------------------------------------------------------- 1 | 2 | import {useSettings} from '@ophidian/core'; 3 | import {LoggingService} from 'lib/LoggingService'; 4 | import {SettingsTabField, SettingsTabHeading, useSettingsTab} from 'Settings/DynamicSettingsTabBuilder'; 5 | import {BaseLoaderService} from 'Data/BaseLoaderService'; 6 | 7 | export interface IJsonLoaderSettings { 8 | jsonFiles: string; 9 | jsonLoaderSettingsOpen: boolean; 10 | 11 | } 12 | 13 | export const JsonLoaderSettingsDefaults: IJsonLoaderSettings = { 14 | jsonFiles: '', 15 | jsonLoaderSettingsOpen: false, 16 | }; 17 | 18 | export class JsonLoaderService extends BaseLoaderService { 19 | logger = this.use(LoggingService).getLogger('Qatt.JsonLoaderService'); 20 | settingsTab = useSettingsTab(this); 21 | jsonLoaderSettingsOpen: boolean; 22 | 23 | settings = useSettings( 24 | this, 25 | JsonLoaderSettingsDefaults, 26 | async (settings: IJsonLoaderSettings) => { 27 | this.jsonLoaderSettingsOpen = settings.jsonLoaderSettingsOpen; 28 | await this.settingsUpdateLoad(settings.jsonFiles); 29 | }, 30 | async (settings: IJsonLoaderSettings) => { 31 | this.jsonLoaderSettingsOpen = settings.jsonLoaderSettingsOpen; 32 | await this.settingsInitialLoad(settings.jsonFiles); 33 | }, 34 | ); 35 | 36 | showSettings() { 37 | const tab = this.settingsTab; 38 | const {settings} = this; 39 | 40 | const onToggle = async (value: boolean) => { 41 | await settings.update(settings => { 42 | settings.jsonLoaderSettingsOpen = value; 43 | }); 44 | }; 45 | 46 | const settingsSection = tab.addHeading(new SettingsTabHeading({open: this.jsonLoaderSettingsOpen, text: 'JSON Loader Settings', level: 'h2', class: 'settings-heading'}), onToggle); 47 | 48 | const onChange = async (value: string) => { 49 | await settings.update(settings => { 50 | settings.jsonFiles = value; 51 | }); 52 | }; 53 | 54 | const postRenderSetting = tab.addTextAreaInput( 55 | new SettingsTabField({ 56 | name: 'JSON file to load on start', 57 | description: 'Add the files you want added on load, one per line. The table name will be the name of the file minus the extension.', 58 | value: this.importFiles, 59 | }), 60 | onChange, 61 | settingsSection, 62 | ); 63 | } 64 | 65 | public importCallback = async (content: string, tableName: string) => { 66 | const data = JSON.parse(content) as any[]; 67 | 68 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 69 | return data; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Query All The Things 2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | ***Execute flexible SQL base queries against your data in [Obsidian](https://obsidian.md) and render it how you want using Handlebars templates.*** 18 | 19 | --- 20 | 21 | ## Features 22 | 23 | - Use SQL based queries that are extensible and handle JSON and objects. 24 | - Query any data collection found in the Obsidian API. 25 | - Query data stored in DataView as well as cached view of DataView Data like tasks. 26 | - Render using handlebar templates in HTML or Markdown 27 | - Use custom handle bar helpers and/or provide your own. 28 | 29 | --- 30 | 31 | ## Roadmap and Issues 32 | 33 | Look at the [Qatt Project](https://github.com/users/sytone/projects/4) to see what is in progress or planned. Please make a issue if you have a problem or want to add/request a new feature. Open to PRs at any point. 34 | 35 | --- 36 | 37 | ## Getting started 38 | 39 | Documentation on installing the plugin and using it can be found at [https://sytone.github.io/obsidian-queryallthethings/](https://sytone.github.io/obsidian-queryallthethings/) 40 | 41 | ## Getting Started - I don't need documentation 42 | 43 | Well, in short after you have installed the plugin make a code block like the following example, this will list all your tasks that are not done and group them by the month when they are due. 44 | 45 | If you want more details.... Read the documentation, or reverse engineer the code base. Your Choice! 46 | 47 | Note: This plugin currently has a soft dependency on DataView, make sure it is installed if you want to use the dataview backed tables. 48 | 49 | ````markdown 50 | ```qatt 51 | query: | 52 | SELECT TOP 5 * FROM obsidian_markdown_notes ORDER BY stat->mtime DESC 53 | template: | 54 | {{#each result}} 55 | - [[{{path}}\|{{basename}}]] 56 | {{/each}} 57 | ``` 58 | ```` 59 | 60 | --- 61 | --------------------------------------------------------------------------------