Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow views to be in markdown files #2202

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ecool
Copy link

@ecool ecool commented Jan 5, 2024

Just a simple change to allow for views to be in a markdown note.

Main reason was to allow for editing the view inside of obsidian without having to use an external editor.

Simply use code blocks. Each block of the same type is added together (js / css).

Change searches for the markdown note first, the simple js file, and then the complex js and css files.

If there is anything I should change, please let me know.

This was tested with the data and views from the https://github.com/s-blu/obsidian_dataview_example_vault

Note View File: views/TestView.md

/**
 * Script needs following input:
 * pages - list of pages that should be used as data source. if no color function is given, page.day must be set
 * year - year to render as a number
 * color - either a string that represents the color that should be used if a page with page.day for this day is available
 *         or a function that gets the date to fill as a argument and needs to return a string to use as color
 * tooltipFn - (optional) a function that gets the date to fill as argument and needs to return a string
 */
const emptyColor = 'rgba(255,255,255,0.1)';

let date = dv.luxon.DateTime.utc(input.year);
const calendar = [];
for (let i = 1; i <= 12; i++) {
  calendar[i] = [];
}

while (date.year == input.year) {
  calendar[date.month].push(getDayEl(date, determineColor(date), generateTooltip(date)));

  date = addOneDay(date);
}

// == Render calendar ==
calendar.forEach((month, i) => {
  const monthEl = `<span class="monthLabel">${dv.luxon.DateTime.utc(input.year, i).toFormat('MMM')}</span>`;

  dv.el('div', monthEl + month.reduce((acc, curr) => `${acc} ${curr}`, ''), { cls: 'month' });
});

function addOneDay(date) {
  return dv.luxon.DateTime.fromMillis(date + dv.duration('1d'));
}

function getDayEl(date, color, tooltip) {
  return `<span class="day" style="--background: ${color}" title="${tooltip}"></span>`;
}

function determineColor(date) {
  if (input.color && typeof input.color === 'function') {
    return input.color(date);
  } else {
    const page = input.pages.find(p => p.file.day.startOf('day').equals(date.startOf('day')));
    return page ? input.color : emptyColor;
  }
}

function generateTooltip(date) {
  let tooltip = '';
  if (input.tooltipFn) {
    tooltip = input.tooltipFn(date);
  } else {
    tooltip = date.toFormat('yyyy-MM-dd');
  }

  return tooltip;
}
.month {
  height: 1.2em;
}

.monthLabel {
  display: inline-block;
  min-width: 30px;
  font-size: small;
}

.day {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background-color: var(--background);
  display: inline-block;
  font-size: 4pt;
}

.day:hover {
  outline: 1px solid rgba(255, 255, 255, 0.3);
}

Note Test File: Test View Example.md

await dv.view("views/TestView", 
	{
		pages: dv.pages('"10 Example Data/dailys"').where(p => p.praying === "yes"),
		year: 2022,
		color: "green"
	})

@ecool ecool changed the title allow views to be in markdown files feat: allow views to be in markdown files Jan 5, 2024
@holroy
Copy link
Contributor

holroy commented Mar 4, 2024

Kind of interesting concept, which I personally has circumvented the need of by using plugins like VSCode Editor (and partially CSS Editor, but there I've also done some extra trickery to allow that other plugin to shine).

However, your code as it stands wouldn't allow for the entire range of code fences around code blocks which at least could be ```+ or ~~~+, that is three or more of either a backtick or a tilde. I'm using both, and there are needs for using both when declaring various code blocks, so we should respect the users choice of code fences.

I reckon using the predefined sections of type "code" found in the metadataCache would be a better approach for locating and dealing with code blocks in general. This would be a safer approach, and would move some of the logic into the main Obsidian API responsible for generating those sections in the first place.

See this forum post, http://forum.obsidian.md/t//77077, for a pure javascript accessing of these code sections, which should of course be translated into proper TypeScript before being included in a PR.

@blacksmithgu
Copy link
Owner

I would second using the metadata cache to find the positions of the codeblocks and extract them that way - after a brief early stint, I generally try to keep my markdown parsing as tame as possible when parsing so the less dependence we have on it the better.

@GottZ
Copy link
Contributor

GottZ commented Mar 25, 2024

I agree 100% with blacksmithgu
tho, if parsing markdown was a thing, the following would actually be required:

to be spec compliant, I'd recommend changing the regexes a bit:

it will also change the output group id to 2 for the content but it ensures to stay spec compliant with markdown.

why does it matter?
well.. if you use ```` for the block boundary, you can use ``` safely within.

this could be perfectly valid:

````js
dv.span("```\nsuch code,\nso block\n```");
````

example of how real this actually is:
image
image

this would also be a valid javascript block:

```js such code so wow
console.log("foo");
```

image

but in this case, I think nobody really makes use of text after the language specifier so it could be omited.

in that case, for completion, the regexp would be this:

(`{3,}|~{3,})(?:js|javascript)(?:\b[^\n]+)?\n(?<code>[\s\S]*?)\n?\1

and this:

(`{3,}|~{3,})css(?:\b[^\n]+)?\n(?<code>[\s\S]*?)\n?\1

image
image
https://regex101.com/r/fM0MTS/1

this regexp also introduced a named matching group.

to be even more spec compliant, I'd actually suggest

(`{3,}|~{3,})(?<language>[^\s]*)(?:\b[^\n]+)?\n(?<code>[\s\S]*?)\n?\1

so it doesn't match blocks within blocks.
image
image
https://regex101.com/r/IxDjhe/1

this would require iterating through the result and checking the languages tho.

@GottZ
Copy link
Contributor

GottZ commented Mar 25, 2024

additionally I'd recommend moving the complexViewPath check above the markdown check.
if there is a .js and / or a .css file present, markdown should not be parsed.
(the markdown could as well just be an example of implementation. that's actually how I structure my own views)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants