├── test └── vault │ ├── NotMarkdown.png │ ├── .obsidian │ ├── hotkeys.json │ ├── plugins │ │ ├── obsidian-dynamic-embed │ │ │ ├── .hotreload │ │ │ └── manifest.json │ │ ├── hot-reload │ │ │ ├── manifest.json │ │ │ └── README.md │ │ └── dataview │ │ │ ├── manifest.json │ │ │ └── styles.css │ ├── appearance.json │ ├── app.json │ ├── community-plugins.json │ ├── core-plugins.json │ └── workspace │ ├── script.md │ ├── Item.md │ └── Project.md ├── .eslintignore ├── styles.css ├── versions.json ├── .editorconfig ├── .gitignore ├── manifest.json ├── tsconfig.json ├── README.md ├── version-bump.mjs ├── .eslintrc ├── package.json ├── LICENSE ├── esbuild.config.mjs └── main.ts /test/vault/NotMarkdown.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/vault/.obsidian/hotkeys.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | npm node_modules 2 | build -------------------------------------------------------------------------------- /test/vault/script.md: -------------------------------------------------------------------------------- 1 | ```dataview 2 | LIST FROM [[]] 3 | ``` -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/obsidian-dynamic-embed/.hotreload: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/vault/.obsidian/appearance.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseFontSize": 16 3 | } -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* Sets all the text color to red! */ 2 | body { 3 | /* color: red; */ 4 | } 5 | -------------------------------------------------------------------------------- /test/vault/.obsidian/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "legacyEditor": false, 3 | "livePreview": true, 4 | "vimMode": true 5 | } -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.0": "0.9.7", 3 | "1.0.1": "0.12.0", 4 | "1.0.2": "0.12.0", 5 | "1.1.0": "0.12.0" 6 | } -------------------------------------------------------------------------------- /test/vault/Item.md: -------------------------------------------------------------------------------- 1 | # Part 1 2 | [[Project]] 3 | This is an item 4 | #itemtag 5 | 6 | # Part 2 7 | [[Project]] 8 | This is an item 2 9 | #itemtag2 -------------------------------------------------------------------------------- /test/vault/.obsidian/community-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "obsidian-sample-plugin", 3 | "dataview", 4 | "hot-reload", 5 | "obsidian-dynamic-embed" 6 | ] -------------------------------------------------------------------------------- /test/vault/Project.md: -------------------------------------------------------------------------------- 1 | ```dataview 2 | LIST FROM [[]] 3 | ``` 4 | ```dynamic-embed 5 | [[script]] 6 | ``` 7 | 8 | 9 | ```dynamic-embed 10 | [[NotMarkdown.png]] 11 | ``` -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | tab_width = 4 10 | -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/hot-reload/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "hot-reload", 3 | "name": "Hot Reload", 4 | "version": "0.1.8", 5 | "minAppVersion": "0.11.13", 6 | "description": "Automatically reload in-development plugins when their files are changed", 7 | "isDesktopOnly": true 8 | } 9 | -------------------------------------------------------------------------------- /test/vault/.obsidian/core-plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "file-explorer", 3 | "global-search", 4 | "switcher", 5 | "graph", 6 | "backlink", 7 | "page-preview", 8 | "note-composer", 9 | "command-palette", 10 | "editor-status", 11 | "markdown-importer", 12 | "word-count", 13 | "open-with-default-app", 14 | "file-recovery" 15 | ] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # npm 5 | node_modules 6 | 7 | # Don't include the compiled main.js file in the repo. 8 | # They should be uploaded to GitHub releases instead. 9 | main.js 10 | 11 | # Exclude sourcemaps 12 | *.map 13 | 14 | # obsidian 15 | data.json 16 | 17 | # Exclude macOS Finder (System Explorer) View States 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-dynamic-embed", 3 | "name": "Dynamic Embed", 4 | "version": "1.1.0", 5 | "minAppVersion": "0.12.0", 6 | "description": "Dynamicly interpreted inline embeds.", 7 | "author": "Ivaylo Dabravin ", 8 | "authorUrl": "https://github.com/dybrawin/obsidian-dynamic-embed", 9 | "isDesktopOnly": false 10 | } -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/dataview/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "dataview", 3 | "name": "Dataview", 4 | "version": "0.4.26", 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 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/obsidian-dynamic-embed/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-dynamic-embed", 3 | "name": "Dynamic Embed", 4 | "version": "1.0.2", 5 | "minAppVersion": "0.12.0", 6 | "description": "Dynamicly interpreted inline embeds.", 7 | "author": "Ivaylo Dabravin ", 8 | "authorUrl": "https://github.com/dybrawin/obsidian-dynamic-embed", 9 | "isDesktopOnly": false 10 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "lib": [ 14 | "DOM", 15 | "ES5", 16 | "ES6", 17 | "ES7" 18 | ] 19 | }, 20 | "include": [ 21 | "**/*.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsidian Dynamic Embed 2 | 3 | Embed snippets, templates and any linkables by delegating the current scope to the embedded file, treating them as content instead of references, contrary to the integrated tag `![[]]`. 4 | 5 | ## Example 6 | Import the contents of file ***`Script-note-template file name link.md`***, existing in the active vault. 7 | Note that the link syntax does **not** support heading/block links (e.g. `[[file#heading1]]`) 8 | ~~~ 9 | ```dynamic-embed 10 | [[Script-note-template file name link]] 11 | ``` 12 | ~~~ -------------------------------------------------------------------------------- /version-bump.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | 3 | const targetVersion = process.env.npm_package_version; 4 | 5 | // read minAppVersion from manifest.json and bump version to target version 6 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); 7 | const { minAppVersion } = manifest; 8 | manifest.version = targetVersion; 9 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); 10 | 11 | // update versions.json with target version and minAppVersion from manifest.json 12 | let versions = JSON.parse(readFileSync("versions.json", "utf8")); 13 | versions[targetVersion] = minAppVersion; 14 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": [ 6 | "@typescript-eslint" 7 | ], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 19 | "@typescript-eslint/ban-ts-comment": "off", 20 | "no-prototype-builtins": "off", 21 | "@typescript-eslint/no-empty-function": "off" 22 | } 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-dynamic-embed", 3 | "version": "1.1.0", 4 | "description": "Dynamicly interpreted inline embeds for Obsidian (https://obsidian.md)", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/node": "^16.11.6", 16 | "@typescript-eslint/eslint-plugin": "^5.2.0", 17 | "@typescript-eslint/parser": "^5.2.0", 18 | "builtin-modules": "^3.2.0", 19 | "esbuild": "0.13.12", 20 | "obsidian": "^0.13.26", 21 | "tslib": "2.3.1", 22 | "typescript": "4.4.4" 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ivaylo Dimitrov Dabravin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from 'builtin-modules' 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === 'production'); 13 | 14 | esbuild.build({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ['main.ts'], 19 | bundle: true, 20 | external: [ 21 | 'obsidian', 22 | 'electron', 23 | '@codemirror/autocomplete', 24 | '@codemirror/closebrackets', 25 | '@codemirror/collab', 26 | '@codemirror/commands', 27 | '@codemirror/comment', 28 | '@codemirror/fold', 29 | '@codemirror/gutter', 30 | '@codemirror/highlight', 31 | '@codemirror/history', 32 | '@codemirror/language', 33 | '@codemirror/lint', 34 | '@codemirror/matchbrackets', 35 | '@codemirror/panel', 36 | '@codemirror/rangeset', 37 | '@codemirror/rectangular-selection', 38 | '@codemirror/search', 39 | '@codemirror/state', 40 | '@codemirror/stream-parser', 41 | '@codemirror/text', 42 | '@codemirror/tooltip', 43 | '@codemirror/view', 44 | ...builtins], 45 | format: 'cjs', 46 | watch: !prod, 47 | target: 'es2016', 48 | logLevel: "info", 49 | sourcemap: prod ? false : 'inline', 50 | treeShaking: true, 51 | outfile: 'main.js', 52 | }).catch(() => process.exit(1)); 53 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Plugin, MarkdownRenderer } from "obsidian"; 2 | 3 | export default class DynamicEmbed extends Plugin { 4 | static codeBlockKeyword = "dynamic-embed"; 5 | static containerClass = "dynamic-embed"; 6 | static errorClass = "dynamic-embed-error"; 7 | 8 | static displayError = (parent: HTMLElement, text: String) => { 9 | parent.createEl("pre", { text: "Dynamic Embed: Error: " + text, cls: [DynamicEmbed.containerClass, DynamicEmbed.errorClass] }); 10 | } 11 | 12 | async onload() { 13 | this.registerMarkdownCodeBlockProcessor(DynamicEmbed.codeBlockKeyword, async (source, el, ctx) => { 14 | const pattern = /\[\[([^\[\]]+?)\]\]/u; 15 | const fileNameMatch = pattern.exec(source); 16 | 17 | 18 | if (!fileNameMatch) { 19 | DynamicEmbed.displayError(el, "Bad file link"); 20 | return; 21 | } 22 | const fileName = fileNameMatch[1]; 23 | const matchingFile = this.app.metadataCache.getFirstLinkpathDest(fileName, ''); 24 | 25 | if (!matchingFile) { 26 | DynamicEmbed.displayError(el, "File link not found"); 27 | return; 28 | } 29 | 30 | if (matchingFile.extension !== "md") { 31 | DynamicEmbed.displayError(el, "Bad file extension found, expected markdown"); 32 | return; 33 | } 34 | 35 | const fileContents = await this.app.vault.cachedRead(matchingFile); 36 | const container = el.createDiv({ cls: [DynamicEmbed.containerClass] }); 37 | MarkdownRenderer.renderMarkdown(fileContents, container, ctx.sourcePath, this); 38 | }); 39 | } 40 | } -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/dataview/styles.css: -------------------------------------------------------------------------------- 1 | /* List View Default Styling; rendered internally as a table. */ 2 | .table-view-table { 3 | width: 100%; 4 | } 5 | 6 | .table-view-table > thead > tr, .table-view-table > tbody > tr { 7 | margin-top: 1em; 8 | margin-bottom: 1em; 9 | text-align: left; 10 | } 11 | 12 | .table-view-table > thead > tr > th { 13 | font-weight: 700; 14 | font-size: larger; 15 | border-top: none; 16 | border-left: none; 17 | border-right: none; 18 | border-bottom: solid; 19 | 20 | max-width: 100%; 21 | } 22 | 23 | .table-view-table > tbody > tr > td { 24 | text-align: left; 25 | border: none; 26 | font-weight: 400; 27 | 28 | max-width: 100%; 29 | } 30 | 31 | /** Rendered value styling for any view. */ 32 | .dataview-result-list-root-ul { 33 | padding: 0em !important; 34 | margin: 0em !important; 35 | } 36 | 37 | /** Generic grouping styling. */ 38 | .dataview.result-group { 39 | padding-left: 8px; 40 | } 41 | 42 | /** Inline Field Rendering. */ 43 | 44 | .dataview.inline-field-key { 45 | border-top-left-radius: 250px; 46 | border-bottom-left-radius: 250px; 47 | padding-left: 16px; 48 | padding-right: 8px; 49 | font-family: var(--font-monospace); 50 | background-color: var(--background-primary-alt); 51 | color: var(--text-nav-selected); 52 | } 53 | 54 | .dataview.inline-field-value { 55 | border-top-right-radius: 250px; 56 | border-bottom-right-radius: 250px; 57 | padding-left: 8px; 58 | padding-right: 16px; 59 | font-family: var(--font-monospace); 60 | background-color: var(--background-secondary-alt); 61 | color: var(--text-nav-selected); 62 | } 63 | 64 | .dataview.inline-field-standalone-value { 65 | border-radius: 250px; 66 | padding-left: 16px; 67 | padding-right: 16px; 68 | font-family: var(--font-monospace); 69 | background-color: var(--background-secondary-alt); 70 | color: var(--text-nav-selected); 71 | } 72 | -------------------------------------------------------------------------------- /test/vault/.obsidian/workspace: -------------------------------------------------------------------------------- 1 | { 2 | "main": { 3 | "id": "27256edcd15def3d", 4 | "type": "split", 5 | "children": [ 6 | { 7 | "id": "bfa3c03b46ea2de3", 8 | "type": "leaf", 9 | "state": { 10 | "type": "markdown", 11 | "state": { 12 | "file": "Project.md", 13 | "mode": "source", 14 | "source": false 15 | } 16 | } 17 | } 18 | ], 19 | "direction": "vertical" 20 | }, 21 | "left": { 22 | "id": "65ed6980c31d7828", 23 | "type": "split", 24 | "children": [ 25 | { 26 | "id": "3972900b8b9deefd", 27 | "type": "tabs", 28 | "children": [ 29 | { 30 | "id": "5e88e5f6aa4a7a35", 31 | "type": "leaf", 32 | "state": { 33 | "type": "file-explorer", 34 | "state": {} 35 | } 36 | }, 37 | { 38 | "id": "f4b2c50dff2f58ad", 39 | "type": "leaf", 40 | "state": { 41 | "type": "search", 42 | "state": { 43 | "query": "", 44 | "matchingCase": false, 45 | "explainSearch": false, 46 | "collapseAll": false, 47 | "extraContext": false, 48 | "sortOrder": "alphabetical" 49 | } 50 | } 51 | } 52 | ] 53 | } 54 | ], 55 | "direction": "horizontal", 56 | "width": 300 57 | }, 58 | "right": { 59 | "id": "8d381f215da97166", 60 | "type": "split", 61 | "children": [ 62 | { 63 | "id": "8bb6fb3f58b44e2c", 64 | "type": "tabs", 65 | "children": [ 66 | { 67 | "id": "df7e5e70709c49f8", 68 | "type": "leaf", 69 | "state": { 70 | "type": "backlink", 71 | "state": { 72 | "file": "Project.md", 73 | "collapseAll": false, 74 | "extraContext": false, 75 | "sortOrder": "alphabetical", 76 | "showSearch": false, 77 | "searchQuery": "", 78 | "backlinkCollapsed": false, 79 | "unlinkedCollapsed": true 80 | } 81 | } 82 | } 83 | ] 84 | } 85 | ], 86 | "direction": "horizontal", 87 | "width": 300 88 | }, 89 | "active": "bfa3c03b46ea2de3", 90 | "lastOpenFiles": [ 91 | "Project.md", 92 | "script.md", 93 | "NotMarkdown.md", 94 | "Item.md" 95 | ] 96 | } -------------------------------------------------------------------------------- /test/vault/.obsidian/plugins/hot-reload/README.md: -------------------------------------------------------------------------------- 1 | # Hot-Reload Plugin for Obsidian.md Plugins 2 | [From pjeby/hot-reload](https://github.com/pjeby/hot-reload) 3 | 4 | If you develop plugins for [Obsidian.md](https://Obsidian.md/), you may be familiar with the frequent need to either restart/reload Obsidian, or else go into its plugin settings to disable and re-enable the plugin you're working on. 5 | 6 | Well, you don't need to do that any more. Just install this plugin, and let it do all the work. When enabled, this plugin automatically watches for changes to the `main.js` or `styles.css` of any plugin whose directory includes a `.git` subdirectory or a file called `.hotreload`, and then automatically disables and re-enables that plugin once changes have stopped for about three-quarters of a second. A notice will appear briefly in Obsidian when the reload occurs. (The verbose logs in the developer tools will also reflect this.) 7 | 8 | The plugin also watches for changes to `manifest.json` files, and the addition or removal of `.git` or `.hotreload`, and updates its watch lists accordingly. So there's no configuration needed to add or remove plugins from the hot reload list: just put your new plugin(s) under revision control or create a `.hotreload` file in them. 9 | 10 | (And, since Obsidian only includes `main.js` and `styles.css` in plugin downloads, you don't have to worry about `.hotreload` escaping into the wild: when other people download your plugin from the Obsidian marketplace, it won't be hot-reloaded, even if they're using the hot-reload plugin, too.) 11 | 12 | Note, however, that if you have this plugin enabled, then it *can* enable plugins that are not currently enabled in the settings, so long as you've checked them out using git or have added a `.hotreload` file. (For development, this is actually a good thing, because when you make a change that breaks the plugin load process, all you need to do is save your file(s) again, and hotreload will try to enable it again, saving you from having to reload or go back to the settings again.) 13 | 14 | Finally, note that while this plugin takes care of the grunt work of reloading your plugin, please keep in mind that it's your *plugin's* job to properly clean up after itself. If you're not making good use of `onunload()` and the various `registerX()` methods to ensure all your changes unload properly, then you may leave Obsidian in an unstable state, forcing you to restart or reload to restore the app to a working state. 15 | 16 | ### Installation 17 | 18 | This is an Obsidian plugin like any other, and must be cloned or unzipped into your vault's `.obsidian/plugins/` directory, then enabled in the Obsidian configuration. It's not registered as a standard community plugin for downloading or updating within Obsidian, because it's intended for developer use only, and because it can enable other plugins. --------------------------------------------------------------------------------