mirror of
https://github.com/meld-cp/obsidian-build.git
synced 2026-07-22 07:30:25 +00:00
Compare commits
No commits in common. "master" and "0.1.3" have entirely different histories.
29 changed files with 287 additions and 486 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -22,7 +22,4 @@ data.json
|
|||
.DS_Store
|
||||
|
||||
# Test vault
|
||||
test-vault
|
||||
|
||||
# Other
|
||||
desktop.ini
|
||||
test-vault
|
||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Cleon Pinto
|
||||
Copyright (c) 2021 Michael Brenan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ Write and execute (sandboxed) JavaScript to render templates, query DataView and
|
|||
|
||||
Basically, turn a note into a small, simple, runnable thing.
|
||||
|
||||
<a href="https://www.buymeacoffee.com/cleon"><img src="https://img.buymeacoffee.com/button-api/?text=&emoji=&slug=meld-build&button_colour=FFDD00&font_colour=000000&font_family=Arial&outline_colour=000000&coffee_colour=ffffff"></a>
|
||||
|
||||
## Quick Start
|
||||
- Install and enable the plugin
|
||||
- Paste the Markdown below into a new note.
|
||||
|
|
@ -32,6 +30,9 @@ await $.ui.message( `From this day forth you shall be known as ${ans}` );
|
|||
- [Guess The Number Game (Using Markers)](/docs/examples/guess-the-number-marker.md)
|
||||
- [Simple Invoice Builder](/docs/examples/invoice-builder.md)
|
||||
|
||||
## Known Issues
|
||||
- If you quickly try to run a note just after some changes, there's a chance that the Obsidian cache hasn't been updated yet. The workaround for now is to wait a second or two before running a changed note.
|
||||
|
||||
## Manually installing the plugin
|
||||
|
||||
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/meld-build/`.
|
||||
|
|
|
|||
6
dist/manifest.json
vendored
6
dist/manifest.json
vendored
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "meld-build",
|
||||
"name": "Meld Build",
|
||||
"version": "0.1.9",
|
||||
"minAppVersion": "1.0.1",
|
||||
"version": "0.1.3",
|
||||
"minAppVersion": "1.1.9",
|
||||
"description": "Write and execute (sandboxed) JavaScript to render templates, query DataView and create dynamic notes.",
|
||||
"author": "meld-cp",
|
||||
"authorUrl": "https://github.com/meld-cp/",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
docs/api.md
35
docs/api.md
|
|
@ -1,11 +1,11 @@
|
|||
# Meld-Build API
|
||||
|
||||
## The Context Object (`$`)
|
||||
- `$.data.<name>`
|
||||
- `$.data[<name>]`
|
||||
- Used to access imported Markdown table data.
|
||||
- The `<name>` will be the previous header to the table in the note.
|
||||
- e.g. if you had a header `# My Customers` before a markdown table, `$.data.my_customers` would return the data of that table.
|
||||
- Use `await $.log($.data);` to discover the data object.
|
||||
- e.g. if you had a header `# My Customers` before a markdown table, `$.data.my_customers` would return an array of that table.
|
||||
- `$.blocks[]`
|
||||
- An array of non-`meld-build` codeblocks
|
||||
- Can be used as templates
|
||||
|
|
@ -75,8 +75,8 @@ const ans4 = await $.ui.ask( 'Title', 'question 4', ['1', '2', '3'] );
|
|||
// show a message
|
||||
await $.ui.message( `Your answers were:\n\n${ans1}, ${ans2}, ${ans3}, ${ans4}` );
|
||||
|
||||
// show a 2 line message with title
|
||||
await $.ui.message( 'A Title', 'A titled message\nLine 2' );
|
||||
// show a message with title
|
||||
await $.ui.message( 'A Title', 'A titled <b>message</b>' );
|
||||
await $.ui.message( 'Math', 13 * 67 );
|
||||
|
||||
// rebuild the current view, may be needed if you have an embedded note which was modified
|
||||
|
|
@ -96,8 +96,8 @@ Read and write vault files.
|
|||
- The loaded data is also returned
|
||||
- `const dataurl = await $.io.load_data_url( path, mimetype? )`
|
||||
- Encode the contents of a file into a dataurl for use in a template
|
||||
- Optionally specify the mime type.
|
||||
- Auto mime type mappings are available for the following common file extensions: `.jpeg`, `.jpg`, `.png`, `.gif`, `.svg`, `.css`
|
||||
- Optionaly specify the mime type.
|
||||
- Auto mime type mappings are available for the following file extensions: `.jpg`, `.png`, `.gif`, `.svg`, `.css`
|
||||
- `await $.io.output( file, content, open? )`
|
||||
- (Over)Writes the `file` with given `content`
|
||||
- Tries to open the `file` if `open` is true (false by default)
|
||||
|
|
@ -146,10 +146,6 @@ Markers can be used to replace sections of a note with dynamic values. Sections
|
|||
- `$.markers.target_file( file? )`
|
||||
- Sets the target file to `file`
|
||||
- `file` is optional and defaults to the current note
|
||||
- `await $.markers.load()`
|
||||
- Loads target file markers into memory
|
||||
- `$.markers.get( name )`
|
||||
- Gets the marker value named `name` from memory
|
||||
- `$.markers.set( name, value )`
|
||||
- Sets the marker value named `name` in memory
|
||||
- `$.markers.clear()`
|
||||
|
|
@ -157,7 +153,7 @@ Markers can be used to replace sections of a note with dynamic values. Sections
|
|||
- `markers = await $.markers.fetch()`
|
||||
- Returns the markers found in the target file
|
||||
- `changes = await $.markers.apply( clearUnknownMarkerValues? )`
|
||||
- Applies set marker values to the target file
|
||||
- Applys set marker values to the target file
|
||||
- `clearUnknownMarkerValues` is optional, set it to `false` if you want to prevent unset markers being blanked out.
|
||||
|
||||
### Examples
|
||||
|
|
@ -176,18 +172,12 @@ Markers can be used to replace sections of a note with dynamic values. Sections
|
|||
//$.markers.target_file( 'Marker Target.md' );
|
||||
|
||||
// returns list of markers found in the target file
|
||||
//const markers = await $.markers.fetch();
|
||||
const markers = await $.markers.fetch();
|
||||
//console.log({markers});
|
||||
|
||||
// loads maker values into memory from target file
|
||||
await $.markers.load();
|
||||
|
||||
// removes marker values from memory
|
||||
//$.markers.clear();
|
||||
|
||||
// gets a marker value from memory
|
||||
const value = $.markers.get( 'marker1' );
|
||||
|
||||
// sets a marker value in memory
|
||||
$.markers.set( 'marker1', Math.random() );
|
||||
|
||||
|
|
@ -197,15 +187,6 @@ const result = await $.markers.apply();
|
|||
```
|
||||
````
|
||||
|
||||
## Markdown Helper (`$.md`)
|
||||
|
||||
- `$.md.table( headers:[], rows:[[]])`
|
||||
- Build a markdown table
|
||||
- e.g. `$.md.table( ['header 1', 'header2'], rows:[ ['row 1 col 1', 'row 1 col 2'], ['row 2 col 1', 'row 2 col 2'], ])`
|
||||
- `$.md.table( data )`
|
||||
- Build a markdown table from imported data
|
||||
- e.g. `$.md.table( $.data.customers )`
|
||||
|
||||
## Assert (`$.assert`)
|
||||
|
||||
You can use `$.assert` to stop the run and show a message if a test fails.
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ run = Let The Games Begin
|
|||
help =
|
||||
```
|
||||
|
||||
Guess Number: %%round=%%%%=round%%
|
||||
Your last guess was: %%last=%%%%=last%%, **%%msg=%%%%=msg%%**
|
||||
Guess Number: %%round=%%7%%=round%%
|
||||
Your last guess was: %%last=%%47%%=last%%, **%%msg=%%🥳 YUS! You win!%%=msg%%**
|
||||
|
||||
```js meld-build
|
||||
const min = 1;
|
||||
|
|
@ -52,6 +52,7 @@ while ( !done ){
|
|||
}
|
||||
|
||||
$.markers.set('last', guessNum );
|
||||
//console.log($.markers.newValues);
|
||||
|
||||
if( guessNum < number ){
|
||||
// guess was too low
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ const customers = [
|
|||
{ id: 101, name: 'Some Other Co', address: ['123 Some Other Rd', 'Some City'] },
|
||||
]
|
||||
|
||||
// Define the invoices and their lines
|
||||
// Define the invoices their lines
|
||||
const invoices = [
|
||||
{
|
||||
id: '22001', customer:100, date: '2022-12-01', status: 'open',
|
||||
work: [
|
||||
{ date: '2022-11-13', start: 7.00, end: 16.50, rate: 25.45, desc: 'I did the thing' },
|
||||
{ date: '2022-11-16', start: 10.75, end: 12.00, rate: 25.45, desc: 'I did the other thing' },
|
||||
{ inv: '22001', date: '2022-11-13', start: 7.00, end: 16.50, rate: 25.45, desc: 'I did the thing' },
|
||||
{ inv: '22001', date: '2022-11-16', start: 10.75, end: 12.00, rate: 25.45, desc: 'I did the other thing' },
|
||||
]
|
||||
},
|
||||
]
|
||||
|
|
@ -109,7 +109,7 @@ const customer = customers.filter( e => e.id==inv.customer ).at(0);
|
|||
const template = $.blocks.at(0);
|
||||
|
||||
// render the template passing in the customer and invoice
|
||||
const html = $.render( template, { customer, inv } );
|
||||
const html = await $.render( template, { customer, inv } );
|
||||
|
||||
// create an html invoice file and open it in the default browser, from there it can be saved as a PDF
|
||||
await $.io.output( `inv ${inv.id}.html`, html, true );
|
||||
|
|
|
|||
|
|
@ -25,27 +25,6 @@ Within the current note:
|
|||
|
||||
---
|
||||
|
||||
## Tagging and Grouping `meld-build` codeblocks
|
||||
|
||||
It is possible to tag codeblocks for targeted runs using the `meld-build-toolbar`.
|
||||
|
||||
For instance, the following codeblock is tagged with `init`
|
||||
````md
|
||||
```js meld-build init
|
||||
await $.ui.message('This is the init codeblock');
|
||||
```
|
||||
````
|
||||
|
||||
Multiple codeblocks with the same tag will be concatenated together before running.
|
||||
|
||||
To skip a codeblock all together, use the `skip` tag like this:
|
||||
````md
|
||||
```js meld-build skip
|
||||
await $.ui.message("This won't run");
|
||||
```
|
||||
````
|
||||
---
|
||||
|
||||
## Toolbar
|
||||
|
||||
To make running `meld-build` codeblocks easier you can add the following codeblock to show a toolbar.
|
||||
|
|
@ -55,25 +34,7 @@ To make running `meld-build` codeblocks easier you can add the following codeblo
|
|||
```
|
||||
````
|
||||
|
||||
By default you will see a `Run` and a `Help` button.
|
||||
|
||||
You can configure the labels of buttons like this:
|
||||
````md
|
||||
```meld-build-toolbar
|
||||
run = Run Me!
|
||||
help = SOS
|
||||
```
|
||||
````
|
||||
|
||||
To define run buttons which target codeblock tags you can do something like:
|
||||
````md
|
||||
```meld-build-toolbar
|
||||
run|init = Run the Init Code
|
||||
run|main = Run Code tagged with main
|
||||
run = Run the Non-tagged Code
|
||||
```
|
||||
````
|
||||
|
||||
_TODO: add screenshot_
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -87,7 +48,7 @@ Here's an example:
|
|||
const mytemplate = 'Hello {{name}}';
|
||||
const mydata = { name:'World' };
|
||||
|
||||
const result = $.render( mytemplate, mydata );
|
||||
const result = await $.render( mytemplate, mydata );
|
||||
|
||||
await $.ui.message( result );
|
||||
```
|
||||
|
|
@ -97,11 +58,11 @@ There are other ways to load templates too, for example, using the content of co
|
|||
|
||||
Say your note looks like this:
|
||||
````md
|
||||
# My Runnable Note
|
||||
# My Runable Note
|
||||
|
||||
The template codeblock:
|
||||
```
|
||||
Greetings {{name}}, {{message}}
|
||||
```html
|
||||
<p>Greetings <strong>{{name}}</strong>, {{message}}</p>
|
||||
```
|
||||
|
||||
The meld-build block to run:
|
||||
|
|
@ -109,14 +70,14 @@ The meld-build block to run:
|
|||
const template = $.blocks.at(0); // gets the first non-meld-build block in the note
|
||||
const data = { name:'John', message:'How are you?' };
|
||||
|
||||
const result = $.render( template, data );
|
||||
const result = await $.render( template, data );
|
||||
|
||||
await $.ui.message( result );
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
Running this note will show a message with the following text: 'Greetings John, How are you?'
|
||||
Running this note will show a message with the following text: 'Greetings **John**, How are you?'
|
||||
|
||||
See the `$.io.import` and `$.io.load` [API](api.md) functions for other ways to load templates.
|
||||
|
||||
|
|
@ -129,10 +90,10 @@ It is possible to mark sections of a note with start and end markers. These sec
|
|||
For example, when the following note is run, the `replace me` will be replaced with a random number.
|
||||
|
||||
````md
|
||||
%%my marker=%%replace me%%=my marker%%
|
||||
%%my marker=%%replace me%%=my marker%%
|
||||
|
||||
```js meld-build
|
||||
$.markers.set( 'my marker', Math.random() );
|
||||
$.markers.set('my marker', Math.random() );
|
||||
await $.markers.apply();
|
||||
```
|
||||
````
|
||||
|
|
@ -143,13 +104,14 @@ See the [API](api.md) or another [example](examples/guess-the-number-marker.md)
|
|||
|
||||
## Accessing the DataView plugin API
|
||||
|
||||
If you are familiar with the [DataView](https://github.com/blacksmithgu/obsidian-dataview) plugin and have it installed, you can access it's [js api](https://blacksmithgu.github.io/obsidian-dataview/api/code-reference/) via the `$.dv.` interface.
|
||||
If you are fimilar with the [DataView](https://github.com/blacksmithgu/obsidian-dataview) plugin and have it installed, you can access it's [js api](https://blacksmithgu.github.io/obsidian-dataview/api/code-reference/) via the `$.dv.` interface.
|
||||
|
||||
For example:
|
||||
````md
|
||||
```js meld-build
|
||||
// use DataView to fetch all notes within the vault
|
||||
const pages = await $.dv.pages();
|
||||
...
|
||||
```
|
||||
````
|
||||
|
||||
|
|
@ -167,9 +129,35 @@ await $.io.output( 'My List.md', md );
|
|||
|
||||
---
|
||||
|
||||
## Skipping `meld-build` blocks
|
||||
|
||||
If you have multiple `meld-build` codeblocks in a note, you can choose to ignore some by adding `skip` after `meld-build`.
|
||||
|
||||
For example, running the following will display the number 0.
|
||||
````md
|
||||
# My Runable Note
|
||||
|
||||
## code block 1
|
||||
```js meld-build
|
||||
let x = 0;
|
||||
```
|
||||
|
||||
## code block 2, skipped
|
||||
```js meld-build skip
|
||||
x = 2;
|
||||
```
|
||||
|
||||
## code block 3
|
||||
```js meld-build
|
||||
await $.ui.message(x);
|
||||
```
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
## Other
|
||||
- It's recommended to turn `on` the `Files & Links > Detect all file extensions` option in Obsidian. This will make working with files for templating easier.
|
||||
- Hint: add `//@hide_when_reading` to a `JavaScript` codeblock to hide it in Reading mode.
|
||||
|
||||
---
|
||||
|
||||
## More Information
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "meld-build",
|
||||
"name": "Meld Build",
|
||||
"version": "0.1.9",
|
||||
"minAppVersion": "1.0.1",
|
||||
"version": "0.1.2",
|
||||
"minAppVersion": "1.1.9",
|
||||
"description": "Write and execute (sandboxed) JavaScript to render templates, query DataView and create dynamic notes.",
|
||||
"author": "meld-cp",
|
||||
"authorUrl": "https://github.com/meld-cp/",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "meld-build-obsidian-plugin",
|
||||
"version": "0.1.9",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meld-build-obsidian-plugin",
|
||||
"version": "0.1.9",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/handlebars": "^4.1.0",
|
||||
|
|
@ -1845,9 +1845,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/obsidian-dataview": {
|
||||
"version": "0.5.53",
|
||||
"resolved": "https://registry.npmjs.org/obsidian-dataview/-/obsidian-dataview-0.5.53.tgz",
|
||||
"integrity": "sha512-t9sivTBVUZ2JA5qv3DWzzMx7/fSJKyYxGnTMEgmmlbp7AeY5apfytUbAKb9CJeIBBGkNgdkgjDzy12VniABhRQ==",
|
||||
"version": "0.5.52",
|
||||
"resolved": "https://registry.npmjs.org/obsidian-dataview/-/obsidian-dataview-0.5.52.tgz",
|
||||
"integrity": "sha512-0i5urpMRBjTtBhZuTUFvy35sQoCsM1rZg2jfDSbYu0dRnKtJcMzKSItBCNnCvPLabGtf5AG+wTaLjRszHkchkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@codemirror/language": "git+https://github.com/lishid/cm-language.git",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "meld-build-obsidian-plugin",
|
||||
"version": "0.1.9",
|
||||
"version": "1.0.0",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
export class ToolbarButton {
|
||||
id: string;
|
||||
label?: string;
|
||||
params: string[];
|
||||
|
||||
constructor(id: string, label?: string, params?: string[]) {
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
this.params = params ?? [];
|
||||
}
|
||||
|
||||
public static parse(line: string): ToolbarButton | null {
|
||||
const pair = line.split('=');
|
||||
if (pair.length == 2) {
|
||||
|
||||
const buttonParts = pair[0].split('|').map(e => e.trim());
|
||||
const id = buttonParts.shift() ?? '';
|
||||
const params = buttonParts;
|
||||
|
||||
const label = pair[1].trim();
|
||||
|
||||
return new ToolbarButton(id, label, params);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,10 @@ export class CodeBlockInfo{
|
|||
|
||||
export class CodeBlockInfoHelper {
|
||||
|
||||
public static isRunnable( info: CodeBlockInfo ):boolean{
|
||||
public static isRunable( info: CodeBlockInfo ):boolean{
|
||||
return ['js', 'javascript'].contains( info.language )
|
||||
&& info.params.at(0) === 'meld-build'
|
||||
&& !info.params.contains( 'skip' )
|
||||
&& info.params.at(1) !== 'skip'
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import { RunLogger } from 'src/run-logger';
|
|||
import { TRunContext } from "src/run-context";
|
||||
import { NamedCodeBlock } from "./named-code-block";
|
||||
import { CodeBlockInfoHelper } from "./code-block-info";
|
||||
import { AssertRunContextImplementation } from "./rci-assert";
|
||||
import { UiRunContextImplementation } from "./rci-ui";
|
||||
import { IoRunContextImplementation } from "./rci-io";
|
||||
import { MarkerRunContextImplementation } from "./rci-markers";
|
||||
import { MarkdownHelperRunContextImplementation } from "./rci-markdown";
|
||||
import { AssertRunContextImplemention } from "./rci-assert";
|
||||
import { UiRunContextImplemention } from "./rci-ui";
|
||||
import { IoRunContextImplemention } from "./rci-io";
|
||||
import { MarkerRunContextImplemention } from "./rci-markers";
|
||||
|
||||
export class Compiler{
|
||||
|
||||
|
|
@ -105,17 +104,17 @@ export class Compiler{
|
|||
// code blocks
|
||||
const allCodeBlocks = await pzr.fetchCodeBlocks( view );
|
||||
|
||||
// runnable code blocks
|
||||
const runnableCodeBlocks = allCodeBlocks
|
||||
.filter( cb => CodeBlockInfoHelper.isRunnable( cb.info ) )
|
||||
// runable code blocks
|
||||
const runableCodeBlocks = allCodeBlocks
|
||||
.filter( cb => CodeBlockInfoHelper.isRunable( cb.info ) )
|
||||
.filter( cb => runGroupTag == undefined || cb.info.params.contains( runGroupTag ) )
|
||||
;
|
||||
if ( runnableCodeBlocks.length == 0 ){
|
||||
if ( runableCodeBlocks.length == 0 ){
|
||||
return null;
|
||||
}
|
||||
|
||||
// source code
|
||||
const sourceCode = runnableCodeBlocks.map( e => e.content ).join('\n');
|
||||
const sourceCode = runableCodeBlocks.map( e => e.content ).join('\n');
|
||||
|
||||
// consumable blocks
|
||||
const consumableBlocks = allCodeBlocks
|
||||
|
|
@ -143,27 +142,15 @@ export class Compiler{
|
|||
return '';
|
||||
},
|
||||
|
||||
assert: new AssertRunContextImplementation(),
|
||||
assert: new AssertRunContextImplemention(),
|
||||
|
||||
ui: new UiRunContextImplementation(),
|
||||
ui: new UiRunContextImplemention(),
|
||||
|
||||
io: new IoRunContextImplementation(
|
||||
view.app.vault,
|
||||
view.app.workspace,
|
||||
log,
|
||||
data,
|
||||
consumableBlocks
|
||||
),
|
||||
io: new IoRunContextImplemention( log, data, consumableBlocks ),
|
||||
|
||||
markers: new MarkerRunContextImplementation(
|
||||
view.app.vault,
|
||||
log,
|
||||
view.file.path
|
||||
),
|
||||
markers: new MarkerRunContextImplemention(log, view.file.path ),
|
||||
|
||||
dv: dvGetAPI(),
|
||||
|
||||
md: new MarkdownHelperRunContextImplementation( log )
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,2 @@
|
|||
export const CODE_BLOCK_LANG_TOOLBAR = 'meld-build-toolbar';
|
||||
|
||||
export const URL_HELP = 'https://github.com/meld-cp/obsidian-build/blob/master/docs/user-guide.md';
|
||||
|
||||
export const EXTENSION_MIMETYPE_MAP = new Map<string,string>([
|
||||
['jpeg', 'image/jpeg'],
|
||||
['jpg', 'image/jpeg'],
|
||||
['png', 'image/png'],
|
||||
['gif', 'image/gif'],
|
||||
['svg', 'image/svg+xml'],
|
||||
['css', 'text/css'],
|
||||
]);
|
||||
export const URL_HELP = 'https://github.com/meld-cp/obsidian-build/blob/master/docs/user-guide.md';
|
||||
|
|
@ -1,26 +1,5 @@
|
|||
export class DataSet extends Array<DataSetRow> {
|
||||
|
||||
public columns:string[] = [];
|
||||
|
||||
constructor( columnNames:string[] ){
|
||||
super();
|
||||
|
||||
//console.debug( "DataSet:", {columnNames} );
|
||||
|
||||
if ( Array.isArray( columnNames ) && columnNames.length > 0 ){
|
||||
this.columns = columnNames.map( cn => cn.trim() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extractAsColumnName( str:string ) : string {
|
||||
return str.trim().replaceAll(/\W/ig, '_').toLowerCase().trim();
|
||||
}
|
||||
|
||||
public getPropertyName ( colName:string ) : string | undefined {
|
||||
return this.extractAsColumnName( colName );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface IDataSetCollection {
|
||||
|
|
@ -35,13 +14,12 @@ export class DataSetRow implements IRowDataValueCollection{
|
|||
|
||||
[column: string]: unknown;
|
||||
|
||||
constructor( dataSet:DataSet, values: unknown[] ){
|
||||
const columns = dataSet.columns;
|
||||
for ( let colIdx = 0; colIdx < columns.length; colIdx++ ) {
|
||||
const rawColName = columns[colIdx];
|
||||
const propColName = dataSet.getPropertyName(rawColName);
|
||||
if ( propColName !== undefined ){
|
||||
this[propColName] = values.at(colIdx);
|
||||
constructor(columnNames:string[], data: unknown[]){
|
||||
for (let colIdx = 0; colIdx < columnNames.length; colIdx++) {
|
||||
const value = data.at(colIdx);
|
||||
const colName = columnNames[colIdx].trim();
|
||||
if ( colName != '' ){
|
||||
this[colName] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
src/main.ts
57
src/main.ts
|
|
@ -3,9 +3,18 @@ import * as HB from 'handlebars';
|
|||
import { RunLogger } from 'src/run-logger';
|
||||
import { Compiler } from 'src/compiler';
|
||||
import { CODE_BLOCK_LANG_TOOLBAR, URL_HELP } from 'src/constants';
|
||||
import { ToolbarButton } from './ToolbarButton';
|
||||
|
||||
interface MeldBuildPluginSettings {
|
||||
mySetting: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: MeldBuildPluginSettings = {
|
||||
mySetting: 'default'
|
||||
}
|
||||
|
||||
export default class MeldBuildPlugin extends Plugin {
|
||||
settings: MeldBuildPluginSettings;
|
||||
|
||||
|
||||
private async codeblockProcessor(el: HTMLElement, ctx: MarkdownPostProcessorContext): Promise<void> {
|
||||
const els = el.querySelector('.language-js');
|
||||
|
|
@ -15,13 +24,15 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
}
|
||||
|
||||
if ( els.getText().contains( '//@hide_when_reading' ) ){
|
||||
//console.debug('Hiding element',{el});
|
||||
console.debug('Hiding element',{el});
|
||||
el.hide();
|
||||
}
|
||||
}
|
||||
|
||||
async onload() {
|
||||
|
||||
await this.loadSettings();
|
||||
|
||||
await this.reloadActiveViewsWithToolbars();
|
||||
|
||||
this.registerHandlebarHelpers();
|
||||
|
|
@ -96,7 +107,7 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
}
|
||||
|
||||
private async reloadActiveViewsWithToolbars(){
|
||||
this.app.workspace.iterateAllLeaves( leaf =>{
|
||||
app.workspace.iterateAllLeaves( leaf =>{
|
||||
const view = leaf.view;
|
||||
if ( view instanceof MarkdownView ){
|
||||
|
||||
|
|
@ -104,7 +115,7 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
//console.debug( `Meld-Build::Rebuilding view for file '${view.file.path}'` );
|
||||
console.debug( `Meld-Build::Rebuilding view for file '${view.file.path}'` );
|
||||
|
||||
(view.leaf as any).rebuildView();
|
||||
}
|
||||
|
|
@ -112,7 +123,7 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
}
|
||||
|
||||
private async buildAndRunActiveView( runGroupTag?:string ){
|
||||
const view = this.app.workspace.getActiveViewOfType( MarkdownView );
|
||||
const view = app.workspace.getActiveViewOfType( MarkdownView );
|
||||
if (!view){
|
||||
new Notice( 'Unable to run, no active Markdown View found' );
|
||||
return;
|
||||
|
|
@ -124,13 +135,14 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
if ( !( view instanceof MarkdownView ) ){
|
||||
return;
|
||||
}
|
||||
const logger = new RunLogger( view.app.vault );
|
||||
const logger = new RunLogger();
|
||||
try{
|
||||
//await view.save();
|
||||
const compiler = new Compiler();
|
||||
const runner = await compiler.compile( logger, view, runGroupTag );
|
||||
runner?.();
|
||||
}catch(e){
|
||||
console.debug('here');
|
||||
await logger.error(e);
|
||||
new Notice(e);
|
||||
}
|
||||
|
|
@ -162,6 +174,39 @@ export default class MeldBuildPlugin extends Plugin {
|
|||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarButton{
|
||||
id:string;
|
||||
label?: string;
|
||||
params: string[];
|
||||
|
||||
constructor( id:string, label?:string, params?:string[] ){
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
this.params = params ?? [];
|
||||
}
|
||||
|
||||
public static parse( line:string ) : ToolbarButton|null{
|
||||
const pair = line.split( '=' );
|
||||
if( pair.length == 2 ){
|
||||
|
||||
const buttonParts = pair[0].split( '|' ).map( e => e.trim() );
|
||||
const id = buttonParts.shift() ?? '';
|
||||
const params = buttonParts;
|
||||
|
||||
const label = pair[1].trim();
|
||||
|
||||
return new ToolbarButton( id, label, params );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { App, Modal, Setting } from "obsidian";
|
||||
import { Utils } from "src/utils";
|
||||
|
||||
export class AskModal extends Modal {
|
||||
|
||||
|
|
@ -6,6 +7,8 @@ export class AskModal extends Modal {
|
|||
private options:string[] = [];
|
||||
private answerInput:Setting;
|
||||
public answer:string|undefined;
|
||||
private completed = true;
|
||||
//private cancelled = false;
|
||||
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
|
|
@ -23,16 +26,14 @@ export class AskModal extends Modal {
|
|||
this.answer = undefined;
|
||||
this.options = options ?? [];
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
|
||||
this.open();
|
||||
|
||||
this.onClose = () => {
|
||||
this.contentEl.empty();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
this.completed = false;
|
||||
|
||||
this.open();
|
||||
|
||||
while(!this.completed){
|
||||
await Utils.delay(250);
|
||||
}
|
||||
|
||||
return this.answer;
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +60,7 @@ export class AskModal extends Modal {
|
|||
if (ev.key == 'Enter'){
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.answer = answer ?? '';
|
||||
this.answer = answer;
|
||||
this.close();
|
||||
}
|
||||
})
|
||||
|
|
@ -73,12 +74,17 @@ export class AskModal extends Modal {
|
|||
cb
|
||||
.setButtonText('OK')
|
||||
.onClick( ev => {
|
||||
this.answer = answer ?? '';
|
||||
this.answer = answer;
|
||||
this.close();
|
||||
})
|
||||
;
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
override onClose() {
|
||||
const { contentEl } = this;
|
||||
this.completed = true;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
import { App, Modal } from "obsidian";
|
||||
import { Utils } from "src/utils";
|
||||
|
||||
export class MessageModal extends Modal {
|
||||
|
||||
private message:string;
|
||||
private closed = true;
|
||||
|
||||
constructor( app: App ) {
|
||||
super( app );
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
public async execute(
|
||||
|
|
@ -15,29 +17,26 @@ export class MessageModal extends Modal {
|
|||
|
||||
this.titleEl.setText( title );
|
||||
|
||||
this.contentEl.append( ...MessageModal.buildContent( message ) );
|
||||
this.message = message;
|
||||
this.closed = false;
|
||||
|
||||
this.open();
|
||||
|
||||
while(!this.closed){
|
||||
await Utils.delay(250);
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
this.open();
|
||||
this.onClose = () => {
|
||||
this.contentEl.empty();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static buildContent( message: string ) : Node[] {
|
||||
|
||||
const result = Array<Node>();
|
||||
|
||||
const lines = message.split( '\n' );
|
||||
|
||||
lines.forEach( line => {
|
||||
result.push( createDiv( { text: line } ) );
|
||||
} );
|
||||
|
||||
return result;
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
const formattedLines = this.message.split('\n').join('<br/>');
|
||||
contentEl.innerHTML = formattedLines;
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,16 +12,16 @@ export class Parser {
|
|||
const result: NamedCodeBlock[] = [];
|
||||
|
||||
const file = view.file;
|
||||
const fileContent = await view.app.vault.cachedRead(file);
|
||||
const fileCache = view.app.metadataCache.getFileCache( file );
|
||||
const fileContent = await app.vault.read(file);
|
||||
const fileCache = app.metadataCache.getFileCache( file );
|
||||
|
||||
if ( fileCache == null ){
|
||||
//console.debug('Parser::fetchCodeBlocks, fileCache is null');
|
||||
console.debug('Parser::fetchCodeBlocks, fileCache is null');
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( fileCache.sections == undefined ){
|
||||
//console.debug('Parser::fetchCodeBlocks, fileCache.sections is undefined');
|
||||
console.debug('Parser::fetchCodeBlocks, fileCache.sections is undefined');
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -48,9 +48,10 @@ export class Parser {
|
|||
}
|
||||
|
||||
// filter languages
|
||||
console.debug('Parser::fetchCodeBlocks',{lines});
|
||||
const codeBlockInfo = this.extractCodeBlockInfo(lines[0]);
|
||||
if ( codeBlockInfo == null ){
|
||||
//console.debug('Parser::fetchCodeBlocks, codeBlockInfo is null', {content});
|
||||
console.debug('Parser::fetchCodeBlocks, codeBlockInfo is null', {content});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ export class Parser {
|
|||
result.push( codeBlock );
|
||||
|
||||
});
|
||||
|
||||
console.debug('Parser::fetchCodeBlocks', {result});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ export class Parser {
|
|||
//const matches = line.match(/````*/i);
|
||||
|
||||
if ( codeBlockMatch == null || codeBlockMatch.length < 1 ){
|
||||
//console.debug('Parser::extractCodeBlockInfo, codeBlockMatch is null or length < 1', {codeBlockMatch, line});
|
||||
console.debug('Parser::extractCodeBlockInfo, codeBlockMatch is null or length < 1', {codeBlockMatch, line});
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -104,20 +105,20 @@ export class Parser {
|
|||
public loadCsv( csvContent:string ) : DataSet {
|
||||
const lines = csvContent.split('\n').map( e=>e.trim() );
|
||||
if (lines.length == 0){
|
||||
return new DataSet( [] );
|
||||
return new DataSet();
|
||||
}
|
||||
|
||||
const columns = lines.first()?.split(',') ?? [];
|
||||
const columns = lines.first()?.split(',').map( e => this.extractAsColumnName(e) ) ?? [];
|
||||
|
||||
const ds = new DataSet( columns );
|
||||
|
||||
//const rows = new Array<DataSetRow>();
|
||||
const rows = new Array<DataSetRow>();
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const rowData = lines[i].split(',').map( d => this.extractValueFromString(d));
|
||||
ds.push( new DataSetRow(ds, rowData));
|
||||
rows.push( new DataSetRow(columns, rowData));
|
||||
}
|
||||
|
||||
const ds = new DataSet( ...rows );
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
|
@ -181,15 +182,19 @@ export class Parser {
|
|||
}
|
||||
|
||||
private parseMdTable( tableLines:string[] ): DataSet {
|
||||
const data: DataSet = new DataSet();
|
||||
|
||||
const tlines = tableLines.map( e=>e.trim().slice(1,-1).trim());
|
||||
|
||||
const columns = tlines.first()?.split('|') ?? [];
|
||||
|
||||
const data: DataSet = new DataSet( columns );
|
||||
const tlines = tableLines
|
||||
.map( e=>e.trim().slice(1,-1).trim());
|
||||
|
||||
const columns = tlines
|
||||
.first()?.split('|')
|
||||
.map( e=> this.extractAsColumnName(e) )
|
||||
.filter( e=>e.length > 0)
|
||||
?? []
|
||||
;
|
||||
|
||||
for (let i = 2; i < tlines.length; i++) {
|
||||
for (let i = 1; i < tlines.length; i++) {
|
||||
const rowLine = tlines[i];
|
||||
if (rowLine.startsWith('---') ){
|
||||
continue;
|
||||
|
|
@ -198,7 +203,7 @@ export class Parser {
|
|||
continue;
|
||||
}
|
||||
const rowValues : unknown[] = rowLine.split('|').map( e => this.extractValueFromString(e) );
|
||||
data.push( new DataSetRow( data, rowValues ) );
|
||||
data.push( new DataSetRow( columns, rowValues ) );
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
@ -210,8 +215,8 @@ export class Parser {
|
|||
} = {};
|
||||
|
||||
const file = view.file;
|
||||
const fileContent = await view.app.vault.cachedRead(file);
|
||||
const fileCache = view.app.metadataCache.getFileCache( file );
|
||||
const fileContent = await app.vault.read(file);
|
||||
const fileCache = app.metadataCache.getFileCache( file );
|
||||
|
||||
if ( fileCache == null ){
|
||||
return result;
|
||||
|
|
@ -232,6 +237,9 @@ export class Parser {
|
|||
}
|
||||
if ( section.type == 'table' ){
|
||||
|
||||
// const from = editor.offsetToPos(section.position.start.offset);
|
||||
// const to = editor.offsetToPos(section.position.end.offset);
|
||||
// const table = editor.getRange( from, to );
|
||||
const table = fileContent.slice(
|
||||
section.position.start.offset,
|
||||
section.position.end.offset
|
||||
|
|
@ -280,6 +288,10 @@ export class Parser {
|
|||
return str.trim().replaceAll(/\W/ig, '_').toLowerCase();
|
||||
}
|
||||
|
||||
private extractAsColumnName( str:string ) : string {
|
||||
return str.trim().replaceAll(/\W/ig, '_').toLowerCase();
|
||||
}
|
||||
|
||||
private extractValueFromString( str:string ) : string | number | Date {
|
||||
const trimmed = str.trim();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { MessageModal } from "./modal-message";
|
||||
import { TAssertRunContext } from "./run-context";
|
||||
|
||||
export class AssertRunContextImplementation implements TAssertRunContext {
|
||||
export class AssertRunContextImplemention implements TAssertRunContext {
|
||||
|
||||
private async showFailMessage( label:string, msg:string ) : Promise<void>{
|
||||
const title = '❗ Assert Failed' + ( label.length > 0 ? ` - ${label}` : '' );
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { normalizePath, TFile, Vault, Workspace } from "obsidian";
|
||||
import { EXTENSION_MIMETYPE_MAP } from "./constants";
|
||||
import { normalizePath, TFile } from "obsidian";
|
||||
import { DataSet, IDataSetCollection } from "./data-set";
|
||||
import { NamedCodeBlock } from "./named-code-block";
|
||||
import { Parser } from "./parser";
|
||||
|
|
@ -7,24 +6,20 @@ import { TIoRunContext } from "./run-context";
|
|||
import { RunLogger } from "./run-logger";
|
||||
import { Utils } from "./utils";
|
||||
|
||||
export class IoRunContextImplementation implements TIoRunContext {
|
||||
export class IoRunContextImplemention implements TIoRunContext {
|
||||
|
||||
private vault: Vault;
|
||||
private workspace: Workspace;
|
||||
private log: RunLogger;
|
||||
private data:IDataSetCollection;
|
||||
private consumableBlocks:NamedCodeBlock[]
|
||||
|
||||
constructor( vault:Vault, workspace:Workspace, log: RunLogger, data:IDataSetCollection, consumableBlocks:NamedCodeBlock[] ){
|
||||
this.vault = vault;
|
||||
this.workspace = workspace;
|
||||
constructor( log: RunLogger, data:IDataSetCollection, consumableBlocks:NamedCodeBlock[] ){
|
||||
this.log = log;
|
||||
this.data = data;
|
||||
this.consumableBlocks = consumableBlocks;
|
||||
}
|
||||
|
||||
private getAbsoluteFilepathFromActiveFile( path:string ) : string | undefined {
|
||||
const activeFile = this.workspace.getActiveFile();
|
||||
const activeFile = app.workspace.getActiveFile();
|
||||
if (activeFile == null){
|
||||
return;
|
||||
}
|
||||
|
|
@ -32,27 +27,25 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
}
|
||||
|
||||
private async data_loader(filepath:string, name?:string) : Promise<DataSet>{
|
||||
|
||||
let resultDataSet = new DataSet();
|
||||
|
||||
const absFilepath = this.getAbsoluteFilepathFromActiveFile(filepath);
|
||||
if (!absFilepath){
|
||||
return new DataSet([]);
|
||||
return resultDataSet;
|
||||
}
|
||||
|
||||
const file = this.vault.getAbstractFileByPath(absFilepath);
|
||||
|
||||
|
||||
const file = app.vault.getAbstractFileByPath(absFilepath);
|
||||
|
||||
const pzr = new Parser();
|
||||
|
||||
if (file instanceof TFile){
|
||||
|
||||
if ( file.extension == 'csv'){
|
||||
const csvData = await this.vault.cachedRead( file );
|
||||
const dataSet = pzr.loadCsv(csvData);
|
||||
this.data[name??file.basename] = dataSet;
|
||||
return dataSet;
|
||||
if (file.extension == 'csv'){
|
||||
const csvdata = await app.vault.read( file );
|
||||
resultDataSet = pzr.loadCsv(csvdata);
|
||||
this.data[name??file.basename] = resultDataSet;
|
||||
}
|
||||
}
|
||||
|
||||
return new DataSet([]);
|
||||
return resultDataSet;
|
||||
}
|
||||
|
||||
async import(path: string): Promise<boolean> {
|
||||
|
|
@ -62,7 +55,7 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
return false;
|
||||
}
|
||||
|
||||
const file = this.vault.getAbstractFileByPath(absFilepath);
|
||||
const file = app.vault.getAbstractFileByPath(absFilepath);
|
||||
|
||||
if (!(file instanceof TFile)){
|
||||
await this.log.error(`import::File not found: "${path}"`);
|
||||
|
|
@ -70,7 +63,7 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
}
|
||||
|
||||
if ( file.extension == 'md' ){
|
||||
const content = await this.vault.cachedRead( file );
|
||||
const content = await app.vault.read( file );
|
||||
const pzr = new Parser();
|
||||
pzr.applyMarkdownContent(
|
||||
file.basename,
|
||||
|
|
@ -88,13 +81,13 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
async load(path: string): Promise<string | undefined> {
|
||||
const filepath = Utils.getSameFolderFilepath(path);
|
||||
|
||||
const af = this.vault.getAbstractFileByPath(filepath);
|
||||
const af = app.vault.getAbstractFileByPath(filepath);
|
||||
|
||||
if (!(af instanceof TFile)){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await this.vault.cachedRead(af)
|
||||
return await app.vault.read(af)
|
||||
}
|
||||
|
||||
async load_data(path: string, name?: string | undefined): Promise<DataSet> {
|
||||
|
|
@ -104,18 +97,21 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
async load_data_url(path: string, mimetype?: string | undefined): Promise<string | undefined> {
|
||||
const filepath = Utils.getSameFolderFilepath(path);
|
||||
|
||||
const af = this.vault.getAbstractFileByPath(filepath);
|
||||
const af = app.vault.getAbstractFileByPath(filepath);
|
||||
|
||||
if (!(af instanceof TFile)){
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const finalMimeType = mimetype
|
||||
?? EXTENSION_MIMETYPE_MAP.get(af.extension)
|
||||
?? ''
|
||||
;
|
||||
const finalMimeType = mimetype ?? {
|
||||
'jpg':'image/jpeg',
|
||||
'png':'image/png',
|
||||
'gif':'image/gif',
|
||||
'svg':'image/svg+xml',
|
||||
'css':'text/css'
|
||||
}[af.extension] ?? '';
|
||||
|
||||
const base64Data = Utils.toBase64( await this.vault.readBinary(af) );
|
||||
const base64Data = Utils.toBase64( await app.vault.readBinary(af) );
|
||||
|
||||
return `data:${finalMimeType};base64,${base64Data}`;
|
||||
}
|
||||
|
|
@ -123,28 +119,28 @@ export class IoRunContextImplementation implements TIoRunContext {
|
|||
async output(file: string, content: string, open?: boolean | undefined): Promise<void> {
|
||||
const newFilepath = Utils.getSameFolderFilepath(file);
|
||||
|
||||
const af = this.vault.getAbstractFileByPath(newFilepath);
|
||||
const af = app.vault.getAbstractFileByPath(newFilepath);
|
||||
if (af instanceof TFile){
|
||||
await this.vault.trash(af, false);
|
||||
await app.vault.trash(af, false);
|
||||
}
|
||||
|
||||
await this.vault.create( newFilepath, content );
|
||||
await app.vault.create( newFilepath, content );
|
||||
|
||||
//new Notice(`${newFilepath} created`);
|
||||
if (open == true){
|
||||
await this.workspace.openLinkText( newFilepath, '' );
|
||||
await app.workspace.openLinkText( newFilepath, '' );
|
||||
}
|
||||
}
|
||||
|
||||
async open(linktext: string): Promise<void> {
|
||||
await this.workspace.openLinkText( linktext, '' );
|
||||
await app.workspace.openLinkText( linktext, '' );
|
||||
}
|
||||
|
||||
async delete(path: string): Promise<void> {
|
||||
const filepath = Utils.getSameFolderFilepath(path);
|
||||
const af = this.vault.getAbstractFileByPath(filepath);
|
||||
const af = app.vault.getAbstractFileByPath(filepath);
|
||||
if (af instanceof TFile){
|
||||
await this.vault.trash(af, false);
|
||||
await app.vault.trash(af, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
import { DataSet } from "./data-set";
|
||||
import { TMarkdownHelperRunContext, TMarkdownHelperTableCellFormatter } from "./run-context";
|
||||
import { RunLogger } from "./run-logger";
|
||||
|
||||
export class MarkdownHelperRunContextImplementation implements TMarkdownHelperRunContext{
|
||||
|
||||
private log: RunLogger;
|
||||
|
||||
constructor( log: RunLogger ){
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
private buildTable(headings: string[], rows: any[][]): string{
|
||||
return MarkdownTableHelper.buildTable( headings, rows );
|
||||
}
|
||||
|
||||
buildTableFromDataSet(data: DataSet, cellFormatter?:TMarkdownHelperTableCellFormatter): string {
|
||||
const headings = data.columns;
|
||||
const rowValues : any[][] = [];
|
||||
|
||||
for (const row of data) {
|
||||
const values : any[] = [];
|
||||
|
||||
for (const heading of headings) {
|
||||
const prop = data.getPropertyName(heading);
|
||||
let value = prop == undefined ? undefined : row[prop];
|
||||
if (prop != undefined && cellFormatter != null){
|
||||
const formattedValue = cellFormatter(prop, value);
|
||||
if (formattedValue != undefined){
|
||||
value = formattedValue;
|
||||
}
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
rowValues.push( values );
|
||||
}
|
||||
|
||||
return MarkdownTableHelper.buildTable( headings, rowValues );
|
||||
}
|
||||
|
||||
public table(arg1: unknown, arg2?: unknown): string {
|
||||
if (arg1 instanceof DataSet){
|
||||
return this.buildTableFromDataSet(arg1, arg2 as TMarkdownHelperTableCellFormatter);
|
||||
}
|
||||
if ( Array.isArray(arg1) && Array.isArray(arg2) ){
|
||||
return this.buildTable(arg1, arg2);
|
||||
}
|
||||
throw new Error('Invalid args for markdown table');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MarkdownTableHelper {
|
||||
|
||||
|
||||
public static buildTable(headings: string[], rowValues: any[][]): string{
|
||||
|
||||
const headers = MarkdownTableHelper.buildHeaderRows( headings );
|
||||
const rows = MarkdownTableHelper.buildValueRows( rowValues );
|
||||
|
||||
const lines = [
|
||||
'',
|
||||
...headers,
|
||||
...rows,
|
||||
'',
|
||||
]
|
||||
|
||||
//console.debug({lines});
|
||||
|
||||
return lines.join('\n');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static removeHeaderFormat( header: string ) : string {
|
||||
if ( header.startsWith('<') || header.startsWith('>') ){
|
||||
return header.slice(1);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
private static buildFormatRowCell( header: string ): string {
|
||||
let leftMarker = ' ';
|
||||
let rightMarker = ' ';
|
||||
|
||||
if ( header.startsWith('<') ){
|
||||
leftMarker = ':';
|
||||
header = header.slice(1);
|
||||
} else if ( header.startsWith('>') ){
|
||||
rightMarker = ':';
|
||||
header = header.slice(1);
|
||||
}else{
|
||||
leftMarker = ':';
|
||||
rightMarker = ':';
|
||||
header = header;
|
||||
}
|
||||
|
||||
return leftMarker + '-'.repeat( header.length ) + rightMarker;
|
||||
}
|
||||
|
||||
private static buildHeaderRows( formattedHeadings: string[] ) : string[] {
|
||||
var result : string[] = [];
|
||||
result.push( '|' + formattedHeadings.map( header => ` ${MarkdownTableHelper.removeHeaderFormat(header)} ` ).join('|') + '|' );
|
||||
result.push( '|' + formattedHeadings.map( header => MarkdownTableHelper.buildFormatRowCell(header) ).join('|') + '|' );
|
||||
return result;
|
||||
}
|
||||
|
||||
private static buildValueRow( rowValues: any[] ) : string {
|
||||
return '|' + rowValues.map( c => ` ${c} ` ).join('|') + '|';
|
||||
}
|
||||
|
||||
|
||||
private static buildValueRows( valueRows: any[][] ) : string[] {
|
||||
return valueRows.map( rowValues => MarkdownTableHelper.buildValueRow(rowValues) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { TFile, Vault } from "obsidian";
|
||||
import { TFile } from "obsidian";
|
||||
import { MarkerChange, MarkerValue, TMarkerRunContext } from "./run-context";
|
||||
import { RunLogger } from "./run-logger";
|
||||
|
||||
export class MarkerRunContextImplementation implements TMarkerRunContext {
|
||||
export class MarkerRunContextImplemention implements TMarkerRunContext {
|
||||
|
||||
private markerStartPrefix = '%%';
|
||||
private markerStartSuffix = '=%%';
|
||||
|
|
@ -10,9 +10,7 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
private markerEndPrefix = '%%=';
|
||||
private markerEndSuffix = '%%'
|
||||
|
||||
private markerValueRegEx = '((?:.|\\n|\\r)*?)';
|
||||
|
||||
private vault: Vault;
|
||||
|
||||
private log: RunLogger;
|
||||
|
||||
private currentPath: string;
|
||||
|
|
@ -20,8 +18,7 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
|
||||
private newValues = new Map<string,string|null>();
|
||||
|
||||
constructor( vault:Vault, log: RunLogger, currentPath:string ){
|
||||
this.vault = vault;
|
||||
constructor( log: RunLogger, currentPath:string ){
|
||||
this.log = log;
|
||||
this.currentPath = currentPath;
|
||||
this.targetPath = currentPath;
|
||||
|
|
@ -45,23 +42,12 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
this.newValues.clear();
|
||||
}
|
||||
|
||||
async load() : Promise<void>{
|
||||
const markers = await this.fetch();
|
||||
markers.forEach(mk => {
|
||||
this.set(mk.name, mk.value);
|
||||
});
|
||||
}
|
||||
|
||||
get( name: string ) : string|null|undefined {
|
||||
return this.newValues.get( name );
|
||||
}
|
||||
|
||||
set( name: string, value: string|null ): void {
|
||||
this.newValues.set( name, value );
|
||||
}
|
||||
|
||||
private getTargetFileOrThrow(): TFile{
|
||||
const targetFile = this.vault.getAbstractFileByPath( this.targetPath );
|
||||
const targetFile = app.vault.getAbstractFileByPath( this.targetPath );
|
||||
|
||||
if (!(targetFile instanceof TFile)){
|
||||
throw new Error(`Target file path was not found. '${this.targetPath}'`);
|
||||
|
|
@ -71,7 +57,7 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
|
||||
private async getTargetContents() : Promise<string>{
|
||||
const targetFile = this.getTargetFileOrThrow();
|
||||
return await this.vault.cachedRead( targetFile );
|
||||
return await app.vault.read( targetFile );
|
||||
}
|
||||
|
||||
async fetch(): Promise<MarkerValue[]> {
|
||||
|
|
@ -84,24 +70,20 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
const exp = this.escapeRegex(this.markerStartPrefix)
|
||||
+ '(.*?)' // marker name start
|
||||
+ this.escapeRegex(this.markerStartSuffix)
|
||||
+ this.markerValueRegEx // marker value
|
||||
+ '(\n?.*?\n?)' // marker value
|
||||
+ this.escapeRegex(this.markerEndPrefix)
|
||||
+ '(.*?)' // marker name end
|
||||
+ this.escapeRegex(this.markerEndSuffix)
|
||||
;
|
||||
|
||||
//console.debug('MarkerRunContextImplementation::findMarkers', {exp});
|
||||
|
||||
const rgexp = new RegExp(exp, 'gm');
|
||||
//console.debug({exp});
|
||||
const rgexp = new RegExp(exp, 'g');
|
||||
|
||||
const matches = text.matchAll( rgexp );
|
||||
|
||||
const result:MarkerValue[] = [];
|
||||
|
||||
for (const match of matches) {
|
||||
|
||||
//console.debug({match});
|
||||
|
||||
for (const match of matches) {
|
||||
//console.debug({match});
|
||||
if( match.index == undefined ){
|
||||
continue;
|
||||
}
|
||||
|
|
@ -137,7 +119,7 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
if ( this.newValues.has( marker.name ) ){
|
||||
continue;
|
||||
}
|
||||
//console.log('apply::clearing marker value', {marker});
|
||||
console.log('apply::clearing marker value', {marker});
|
||||
this.newValues.set( marker.name, '' );
|
||||
}
|
||||
}
|
||||
|
|
@ -147,22 +129,23 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
for ( const key of this.newValues.keys() ) {
|
||||
const currentMarkers = markers.filter( e => e.name == key );
|
||||
if ( currentMarkers.length == 0 ){
|
||||
//console.debug('apply::marker isn\'t in file',{key, markers, targetFileContent});
|
||||
console.debug('apply::marker isn\'t in file',{key, markers, targetFileContent});
|
||||
continue; // marker isn't in file
|
||||
}
|
||||
|
||||
|
||||
const newValue = this.newValues.get(key) ?? '';
|
||||
|
||||
// build replacement regex
|
||||
const findExp = this.escapeRegex(this.markerStartPrefix + key + this.markerStartSuffix)
|
||||
+ this.markerValueRegEx // any old marker value
|
||||
+ this.escapeRegex(this.markerEndPrefix + key +this.markerEndSuffix)
|
||||
+ '(\n?.*?\n?)' // any old marker value
|
||||
+ this.escapeRegex(this.markerEndPrefix + key +this.markerEndSuffix)
|
||||
;
|
||||
const replacement = `${this.markerStartPrefix}${key}${this.markerStartSuffix}${newValue}${this.markerEndPrefix}${key}${this.markerEndSuffix}`;
|
||||
|
||||
//console.log({findExp, replacement, currentMarkers});
|
||||
|
||||
const findRegex = new RegExp(findExp, 'gm');
|
||||
const findRegex = new RegExp(findExp, 'g');
|
||||
|
||||
targetFileContent = targetFileContent.replaceAll( findRegex, replacement );
|
||||
|
||||
|
|
@ -177,10 +160,10 @@ export class MarkerRunContextImplementation implements TMarkerRunContext {
|
|||
//console.debug({targetFileContent});
|
||||
const targetFile = this.getTargetFileOrThrow();
|
||||
|
||||
await this.vault.modify( targetFile, targetFileContent );
|
||||
await app.vault.modify( targetFile, targetFileContent );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { AskModal } from "./modal-ask";
|
|||
import { MessageModal } from "./modal-message";
|
||||
import { TUiRunContext } from "./run-context";
|
||||
|
||||
export class UiRunContextImplementation implements TUiRunContext {
|
||||
export class UiRunContextImplemention implements TUiRunContext {
|
||||
|
||||
notice(
|
||||
message: string | DocumentFragment,
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ export type TRunContext = {
|
|||
|
||||
dv: DataviewApi | undefined;
|
||||
|
||||
md: TMarkdownHelperRunContext;
|
||||
|
||||
}
|
||||
|
||||
export type TUiRunContext = {
|
||||
|
|
@ -75,10 +73,8 @@ export type TMarkerRunContext = {
|
|||
|
||||
clear() : void;
|
||||
|
||||
get( name:string ): string|null|undefined;
|
||||
set( name:string, value:string|null ): void;
|
||||
|
||||
load() : Promise<void>;
|
||||
|
||||
fetch() : Promise<MarkerValue[]>;
|
||||
|
||||
apply( clearUnknownMarkerValues?:boolean ) : Promise<MarkerChange[]>;
|
||||
|
|
@ -118,9 +114,3 @@ export class MarkerChange{
|
|||
}
|
||||
}
|
||||
|
||||
export type TMarkdownHelperTableCellFormatter = (propName:string, value:any) => string|undefined;
|
||||
|
||||
export type TMarkdownHelperRunContext = {
|
||||
table( headings:Array<string>, rows:Array<Array<any>> ) : string;
|
||||
table( data:DataSet, cellFormatter?:TMarkdownHelperTableCellFormatter ) : string;
|
||||
}
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
import { TFile, Vault } from "obsidian";
|
||||
import { TFile } from "obsidian";
|
||||
import { Utils } from "src/utils";
|
||||
|
||||
export class RunLogger {
|
||||
|
||||
private vault:Vault;
|
||||
private file: TFile|undefined;
|
||||
|
||||
constructor( vault:Vault ){
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
private console_info( ...params: any[] ){
|
||||
window.console.info( 'meld-build', ...params );
|
||||
}
|
||||
|
|
@ -58,7 +53,7 @@ export class RunLogger {
|
|||
|
||||
logLine += '\n';
|
||||
|
||||
await this.vault.append( this.file, logLine );
|
||||
await app.vault.append( this.file, logLine );
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -70,14 +65,14 @@ export class RunLogger {
|
|||
|
||||
const filepath = Utils.getSameFolderFilepath(filename);
|
||||
|
||||
const af = this.vault.getAbstractFileByPath(filepath);
|
||||
const af = app.vault.getAbstractFileByPath(filepath);
|
||||
if ( af instanceof TFile ){
|
||||
this.file = af;
|
||||
if ( clear == true ){
|
||||
this.vault.modify( this.file, '' );
|
||||
app.vault.modify( this.file, '' );
|
||||
}
|
||||
}else{
|
||||
this.file = await this.vault.create( filepath, '' );
|
||||
this.file = await app.vault.create( filepath, '' );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ export class Utils{
|
|||
return normalizePath( parentPath + finalFilename );
|
||||
}
|
||||
|
||||
public static delay(ms: number) : Promise<()=>void> {
|
||||
return new Promise( resolve => setTimeout(resolve, ms) );
|
||||
}
|
||||
|
||||
public static toBase64( buf: ArrayBuffer ) : string {
|
||||
return arrayBufferToBase64( buf );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
{
|
||||
"0.1.1": "1.0.1",
|
||||
"0.1.2": "1.0.1",
|
||||
"0.1.3": "1.0.1",
|
||||
"0.1.4": "1.0.1",
|
||||
"0.1.5": "1.0.1",
|
||||
"0.1.6": "1.0.1",
|
||||
"0.1.7": "1.0.1",
|
||||
"0.1.8": "1.0.1",
|
||||
"0.1.9": "1.0.1"
|
||||
}
|
||||
"0.1.3": "1.1.9",
|
||||
"0.1.2": "1.1.9",
|
||||
"0.1.1": "1.1.9"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue