Compare commits

...

72 Commits

Author SHA1 Message Date
Jani Eväkallio
265ab19e31 Prototype basic publish command (just pushes all changes to git) 2020-07-16 11:57:47 +01:00
Ankit Tiwari
3ef95628f5 Remove posttest command 2020-07-16 13:33:18 +05:30
Ankit Tiwari
626f64aec0 Propagate the error from writeFileToDisk method 2020-07-16 13:32:30 +05:30
Ankit Tiwari
e36c285764 Refactor renameFile to use path module 2020-07-16 13:30:26 +05:30
Ankit Tiwari
20ca92f451 Make fs tests no blocking by using promises 2020-07-16 12:12:50 +05:30
Ankit Tiwari
4557150378 Use Promise.resolve(null) 2020-07-16 11:50:41 +05:30
Ankit Tiwari
deb382af2d Mock fs for tests 2020-07-16 11:08:29 +05:30
Ankit Tiwari
34c775f185 Merge branch 'master' of github.com:foambubble/foam into cli/apply-text-edit 2020-07-16 10:34:59 +05:30
Ankit Tiwari
22876cd5f0 Refactor (PR changes)
1.  Renamed initializeNoteGraph.ts to initialize-note-graph.ts to be consistent with naming

2.  Refactored code in janitor and migrate commands
2020-07-16 10:31:55 +05:30
allcontributors[bot]
2d51ce5893 docs: add digiguru as a contributor (#129)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-15 23:34:59 +01:00
dependabot[bot]
adfef15404 Bump lodash from 4.17.15 to 4.17.19 in /packages/foam-core (#126)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-15 22:33:23 +01:00
dependabot[bot]
e92dd7a121 Bump lodash from 4.17.15 to 4.17.19 (#128)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-15 22:03:05 +01:00
allcontributors[bot]
950016dcc7 docs: add francishamel as a contributor (#127)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-15 21:35:52 +01:00
Francis Hamel
6b99a8bd23 Add open daily note base feature (#79)
* Add dateformat package dependency

* Implement Open Daily Note feature

- Activate command
- Add default keybindings for the command
- Use settings to configure the filename format, the file extension,
the title format and the directory where the note should be created
2020-07-15 21:32:01 +01:00
allcontributors[bot]
cfc94ab693 docs: add nstafie as a contributor (#125)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-15 21:29:10 +01:00
digiguru
5b471b32e6 GitLab recipe (#117)
* Typo

* Adds gitlab recipe

* Update docs/gitlab-pages.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/gitlab-pages.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/gitlab-pages.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/gitlab-pages.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

* Update docs/gitlab-pages.md

Co-authored-by: Joe Previte <jjprevite@gmail.com>

Co-authored-by: Joe Previte <jjprevite@gmail.com>
2020-07-15 21:28:29 +01:00
allcontributors[bot]
847f5ccf56 docs: add SanketDG as a contributor (#116)
* docs: update docs/index.md [skip ci]

* docs: update readme.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-15 21:26:48 +01:00
Nicholas Stafie
0342285a7b Clarify how the repository should be opened in VS Code (#123)
* Clarify how the repository should be opened in VS Code

* Change formatting and wording slightly

Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-15 21:24:38 +01:00
chirag-singhal
9c1350f64b removed excess white space 2020-07-15 22:26:00 +05:30
chirag-singhal
808a453641 removed extra test files 2020-07-15 22:23:59 +05:30
chirag-singhal
63521c91e7 minor fixes 2020-07-15 22:21:41 +05:30
chirag-singhal
c652385a97 Merge branch 'cli/apply-text-edit' of https://github.com/foambubble/foam into cli/apply-text-edit 2020-07-15 22:18:36 +05:30
chirag-singhal
ee7fa79e63 updated foam janitor command 2020-07-15 22:18:17 +05:30
chirag-singhal
ea0a659119 added foam migrate command 2020-07-15 22:12:39 +05:30
Ankit Tiwari
b0e587ddb8 Merge branch 'cli/apply-text-edit' of github.com:foambubble/foam into cli/apply-text-edit 2020-07-15 22:05:07 +05:30
Ankit Tiwari
04d86bc45a Add foam-core as dependency 2020-07-15 22:05:02 +05:30
chirag-singhal
76b421e800 used github slagger instead of dashify 2020-07-15 21:51:04 +05:30
Ankit Tiwari
d8c42bd8c4 Replace dashify by github-slugger 2020-07-15 21:43:32 +05:30
chirag-singhal
093275ac0c fixed core tests 2020-07-15 21:34:14 +05:30
chirag-singhal
c2e998927d Solves issue with roam migration 2020-07-15 21:30:20 +05:30
chirag-singhal
0ace092c34 added tests files for migration 2020-07-15 21:15:30 +05:30
Jani Eväkallio
dd6569b264 Refine terminology descriptively 2020-07-15 16:26:19 +01:00
Mathieu Dutour
37359a021d Merge pull request #121 from aravindballa/patch-1
Typo in eleventy doc
2020-07-15 17:04:48 +02:00
Aravind Balla
6b97e5a066 fix: typo in eleventy doc 2020-07-15 18:09:28 +05:30
chirag-singhal
cb6ad7809f ignore link refrences with no file 2020-07-15 16:55:07 +05:30
chirag-singhal
896e894aff added glob as dependency 2020-07-15 11:42:16 +05:30
chirag-singhal
c0f84f66f1 minor fixes 2020-07-15 11:41:04 +05:30
chirag-singhal
d7de46274d check if given path is valid directory 2020-07-15 11:32:31 +05:30
Sanket Dasgupta
10af405da5 Add recipe for pasting images from clipboard (#114) 2020-07-14 22:19:18 +01:00
Janne Ojanaho
4ebec70d50 link ref definition proposal (#82)
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-14 22:17:23 +01:00
Jani Eväkallio
5bd331edab Add mathjax support to math-support page 2020-07-14 20:50:17 +01:00
Ankit Tiwari
c9bba037b0 Use note.eol to append line endings 2020-07-15 00:29:58 +05:30
Ankit Tiwari
942d9b480f Store endOfLine inside Note 2020-07-15 00:20:32 +05:30
allcontributors[bot]
655ea856ce docs: add juanfrank77 as a contributor (#113)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-14 18:46:53 +01:00
Juan F Gonzalez
e977963e1a Add recipe for publishing with netlify (#95)
Co-authored-by: Juan Francisco Gonzalez <jf.gonzalez@globant.com>
Co-authored-by: Jani Eväkallio <jani.evakallio@gmail.com>
2020-07-14 17:50:19 +01:00
Ankit Tiwari
a5f8050d9c Add ora spinner 2020-07-14 22:18:48 +05:30
allcontributors[bot]
4a1a825e12 docs: add TaiChi-IO as a contributor (#111)
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-07-14 17:43:58 +01:00
Ankit Tiwari
c5bd48d86e PR changes 2020-07-14 21:53:04 +05:30
chirag-singhal
9e07b0f19a merge branch janitor/link-reference-definitions 2020-07-14 21:14:23 +05:30
chirag-singhal
bb8d0dabba added tests for generateHeading in janitor 2020-07-14 20:44:54 +05:30
chirag-singhal
b113cafeba added generate Heading function to janitor 2020-07-14 20:40:01 +05:30
Ankit Tiwari
7c041e0fc8 kebab case file names while running janitor 2020-07-14 20:29:39 +05:30
Jani Eväkallio
e35f29cee2 Jot down some raw notes 2020-07-14 15:55:11 +01:00
TaiChi-IO
4d33ad485b Math support recipe (#109) 2020-07-14 15:50:36 +01:00
Ankit Tiwari
f6c3ecf369 Implement basic foam-cli janitor command 2020-07-14 19:58:17 +05:30
Ankit Tiwari
9e452aa9c9 Move noteGraph scaffolding to utils 2020-07-14 19:06:25 +05:30
Ankit Tiwari
52f1dc45a6 Implement generateHeading janitor method
Co-authored-by: CHIRAG SINGHAL <csinghal208@gmail.com>
2020-07-14 18:50:43 +05:30
Ankit Tiwari
92e4510c2d Merge branch 'janitor/link-reference-definitions' into cli/apply-text-edit 2020-07-14 18:33:37 +05:30
Ankit Tiwari
78586be4a3 Add partial tests for writeFileToDisk method 2020-07-14 17:31:22 +05:30
chirag-singhal
79a5621f31 Add no change in link definitions test to generateLinkReferences janitor method 2020-07-14 17:18:17 +05:30
Ankit Tiwari
9367e8e495 Implement first version of applyText method 2020-07-14 17:03:56 +05:30
Ankit Tiwari
b611b1bb07 export TextEdit interface 2020-07-14 17:03:12 +05:30
chirag-singhal
b987ae7a3f Add update link definitions test to generateLinkReferences janitor method 2020-07-14 16:36:47 +05:30
chirag-singhal
6fa858f8d4 Add remove link definitions test to generateLinkReferences janitor method 2020-07-14 16:17:56 +05:30
Ankit Tiwari
e57db48f0e Setup Jest 2020-07-14 16:05:49 +05:30
Jani Eväkallio
3e20dc3356 Add partial tests for generateLinkReferenceDefinitions 2020-07-14 10:06:57 +01:00
Jani Eväkallio
d65f724b56 Implement first version of generateLinkReferenceDefinitions janitor method 2020-07-14 10:06:23 +01:00
Jani Eväkallio
6bd9aaa949 Export stringifyMarkdownLinkReferenceDefinition from foam-core 2020-07-14 10:05:47 +01:00
Jani Eväkallio
d905972f61 Add Note.end and Note.definitions to foam-core tests 2020-07-14 10:04:38 +01:00
Jani Eväkallio
3511ce30e3 Use stringifyMarkdownLinkReferenceDefinition in foam-vscode
This commit also applies prettier to previously badly formatted files, so the diff is larger than necessary
2020-07-14 10:03:32 +01:00
Jani Eväkallio
215dea151f Add Note.definitions and Note.end 2020-07-14 10:02:49 +01:00
Jani Eväkallio
d7b930ff1e Add NoteLink.position to keep track of link ranges (#108) 2020-07-14 08:04:25 +01:00
58 changed files with 3090 additions and 474 deletions

View File

@@ -186,7 +186,63 @@
"contributions": [
"doc"
]
},
{
"login": "TaiChi-IO",
"name": "TaiChi-IO",
"avatar_url": "https://avatars3.githubusercontent.com/u/65092992?v=4",
"profile": "https://github.com/TaiChi-IO",
"contributions": [
"doc"
]
},
{
"login": "juanfrank77",
"name": "Juan F Gonzalez ",
"avatar_url": "https://avatars1.githubusercontent.com/u/12146882?v=4",
"profile": "https://github.com/juanfrank77",
"contributions": [
"doc"
]
},
{
"login": "SanketDG",
"name": "Sanket Dasgupta",
"avatar_url": "https://avatars3.githubusercontent.com/u/8980971?v=4",
"profile": "https://sanketdg.github.io",
"contributions": [
"doc"
]
},
{
"login": "nstafie",
"name": "Nicholas Stafie",
"avatar_url": "https://avatars1.githubusercontent.com/u/10801854?v=4",
"profile": "https://github.com/nstafie",
"contributions": [
"doc"
]
},
{
"login": "francishamel",
"name": "Francis Hamel",
"avatar_url": "https://avatars3.githubusercontent.com/u/36383308?v=4",
"profile": "https://github.com/francishamel",
"contributions": [
"code"
]
},
{
"login": "digiguru",
"name": "digiguru",
"avatar_url": "https://avatars1.githubusercontent.com/u/619436?v=4",
"profile": "http://digiguru.co.uk",
"contributions": [
"code",
"doc"
]
}
],
"contributorsPerLine": 7
"contributorsPerLine": 7,
"skipCi": true
}

View File

@@ -0,0 +1,28 @@
---
layout: default
---
{{ content }}
<script type="text/javascript">
// Hack: Replace page-link with "Page Title"
document
.querySelectorAll(".markdown-body a[title]:not([href^=http])")
.forEach((a) => {
a.innerText = a.title;
});
document.querySelectorAll(".github-only").forEach((el) => {
el.remove();
});
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
inlineMath: [['$','$']]
}
});
</script>

View File

@@ -0,0 +1,19 @@
# Eleventy and Netlify
You can use [foam-eleventy-template](https://github.com/juanfrank77/foam-eleventy-template) to generate a static site with [Eleventy](https://www.11ty.dev/), and host it online on [Netlify](https://www.netlify.com/).
With this template you can
- Have control over what to publish and what to keep private
- Customize the styling of the site to your own liking
## Publishing your foam
When you're ready to publish, import the GitHub repository you created with **foam-eleventy-template** into your Netlify account. (Create one if you don't have it already.)
Once that's done, all you have to do is make changes to your workspace in VS COde and push them to the main branch on GitHub. Netlify will recognize the changes, deploy them automatically and give you a link where your Foam is published.
That's it!
You can now see it online and use that link to share it with your friends, so that they can see it too.

View File

@@ -20,7 +20,7 @@
- Can be either [[wiki-links]] or relative `[markdown](links.md)` style
- We need to know about the edges (connections) as well as nodes
- What link points to what other file, etc.
- Needs to have the exact link text, e.g. even if `[[some-page]]` or `[[some-page.md]]` or `[[Some Page]]` point to the same document (`./some-page.md`), we need to know which format was used, so link reference definitions can be generated correctly
- Needs to have the exact link text, e.g. even if `[[some-page]]` or `[[some-page.md]]` or `[[Some Page]]` point to the same document (`./some-page.md`), we need to know which format was used, so [[link-reference-definitions]] can be generated correctly
- Treat each file as semi-structured data
- Title, headings, lists, paragraphs, images, links, data, code
- Also, possible Foam-specific meta stuff, like "backlinks" or "block references".
@@ -61,7 +61,7 @@ Here are some example use cases that the core should support. They don't need to
- Adding and editing page content
- [[materialized-backlinks]]
- [[wiki-links]] reference definitions
- [[link-reference-definitions]] for [[wiki-links]]
- [Frontmatter](https://jekyllrb.com/docs/front-matter/)
- Finding all documents with `#tag`
- Finding all documents with instances of `[[link]]`

View File

@@ -14,25 +14,7 @@ Here are a few specific constraints, mainly because our tooling is a bit fragmen
- **File name should have extension `.md` or `.markdown`**
- This is a temporary limitation and will be lifted in future versions.
- At least `.mdx` will be supported, but ideally we'll support any file that you can map to `Markdown` language mode in VS Code
- **In addition to normal Markdown Links syntax you can use `[[media-wiki]]` links.**
- When you do, the [foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode) extension will automatically generate [Markdown Link Reference Definitions](https://spec.commonmark.org/0.29/#link-reference-definitions) at the bottom of the file.
- Here's an example:
- [[wiki-links]]
- [[github-pages]]
- This will generate the following references:
```md
[wiki-links]: wiki-links "Wiki Links"
[github-pages]: github-pages "Github Pages"
```
- The three components are `[link-label]: link-target "Link Title"
- **link label:** The link text to match in the surrounding markdown document. This matches the inner bracket of the double-bracketed `[[wiki-link]]` notation
- **link destination** The target of the matched link
- Right now we generate link destinations without file extension. This is a choice, see [discussion here](https://foambubble.github.io/foam/wiki-links#why-dont-wiki-links-work-on-github).
- **"Link Title"** Optional title for link (The Foam template has a snippet of JavaScript to replace this on the website at runtime)
- Open the [raw markdown](https://raw.githubusercontent.com/foambubble/foam/master/foam-file-format.md) to see them at the bottom of this file
- In the near future, these can be batch generated for all workspace files (WIP)
- For the time being, if you want to get [[wiki-links]] support across all files, you'll need to generate the link reference definitions yourself.
- If you end up writing a batch job for this, please share your solution, as this is something we'll need to implement soon!
- **In addition to normal Markdown Links syntax you can use `[[media-wiki]]` links.** See [[wiki-links]] for more details.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"

51
docs/gitlab-pages.md Normal file
View File

@@ -0,0 +1,51 @@
# GitLab Pages
You don't have to use GitHub to serve Foam pages. You can also use GitLab.
## Setup a project
### Generate the directory from GitHub
Generate a solution using the [Foam template].
Change the remote to GitLab, or copy all the files into a new GitLab repo.
### Add a _config.yaml
Add another file to the root directory (the one with `readme.md` in it) called `_config.yaml` (no extension)
```yaml
title: My Awesome Foam Project
baseurl: "" # the subpath of your site, e.g. /blog
url: "/" # the base hostname & protocol for your site
theme: jekyll-theme-minimal
```
You can choose a theme if you want from places like [Jekyll Themes](https://jekyllthemes.io/)
### Add a Gemlock file
Add another file to the root directory (the one with `readme.md` in it) called `Gemfile` (no extension)
```ruby
source "https://rubygems.org"
gem "jekyll"
gem "jekyll-theme-minimal"
gem "jekyll-optional-front-matter"
```
Commit the file and push it to gitlab.
## Setup CI/CD
1. From the project home in GitLab click `Set up CI/CD`
2. Choose `Jekyll` as your template from the template dropdown
3. Click `commit`
4. Now when you go to CI / CD > Pipelines, you should see the code running
## Troubleshooting
- *Could not locate Gemfile* - You didn't follow the steps above to [#Add a Gemlock file]
- *Conversion error: Jekyll::Converters::Scss encountered an error while converting* You need to reference a theme.
- *Pages are running in CI/CD, but I only ever see `test`, and never deploy* - Perhaps you've renamed the main branch (from master) - check the settings in `.gitlab-ci.yml` and ensure the deploy command is running to the branch you expect it to.
- *I deployed, but my .msd files don't seem to be being converted into .html files* - You need a gem that GitHub installs by default - check `gem "jekyll-optional-front-matter"` appears in the `Gemfile`

View File

@@ -0,0 +1,5 @@
# Images from your Clipboard
You can directly link and paste images that are copied to the clipboard using the [Paste
Image](https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image)
extension.

View File

@@ -3,7 +3,6 @@
Uncategorised thoughts, to be added
- Release notes
- Automatic updates
- Markdown Preview
- It's possible to customise the markdown preview styling. **Maybe make it use local foam workspace styles for live preview of the site??**
- See: https://marketplace.visualstudio.com/items?itemName=bierner.markdown-preview-github-styles
@@ -11,10 +10,14 @@ Uncategorised thoughts, to be added
- Investigate other similar extensions:
- [Unotes](https://marketplace.visualstudio.com/items?itemName=ryanmcalister.Unotes)
- [vscode-memo](https://github.com/svsool/vscode-memo)
- [gistpad wiki](https://github.com/jevakallio/gistpad/tree/master/src/repos/wiki)
- Developer documentation
- GistPad has a good vs code contrib primer: https://github.com/jevakallio/gistpad/blob/master/CONTRIBUTING.md
- VS Code Notebooks API
- https://code.visualstudio.com/api/extension-guides/notebook
- Snippets in template
- Foam as a (VS Code) language
- Syntax highlighting
- Autocompletion
- Get rid of mediawiki links in favor of write time tooling
- Snippets
- Future architecture
- Could we do publish-related settings as a pre-push git hook, e.g. generating footnote labels

View File

@@ -62,10 +62,12 @@ These instructions assume you have a GitHub account, and you have Visual Studio
<a class="github-button" href="https://github.com/foambubble/foam-template/generate" data-icon="octicon-repo-template" data-size="large" aria-label="Use this template foambubble/foam-template on GitHub">Use this template</a>
(If you want to keep your thoughts to yourself, remember to set the repository private, or if you don't want to use GitHub to host your workspace at all, choose **Download as ZIP** instead of **Use this template**.)
*If you want to keep your thoughts to yourself, remember to set the repository private, or if you don't want to use GitHub to host your workspace at all, choose **Download as ZIP** instead of **Use this template**.*
2. [Clone the repository locally](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) and open it in VS Code.
*Open the repository as a folder using the `File > Open...` menu item. In VS Code, "open workspace" refers to [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces).*
3. When prompted to install recommended extensions, click **Install all** (or **Show Recommendations** if you want to review and install them one by one)
After setting up the repository, open `.vscode/settings.json` and edit, add or remove any settings you'd like for your Foam workspace.
@@ -106,29 +108,37 @@ If that sounds like something you're interested in, I'd love to have you along o
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4?s=60" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4?s=60" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/TaiChi-IO"><img src="https://avatars3.githubusercontent.com/u/65092992?v=4" width="60px;" alt=""/><br /><sub><b>TaiChi-IO</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=TaiChi-IO" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/juanfrank77"><img src="https://avatars1.githubusercontent.com/u/12146882?v=4" width="60px;" alt=""/><br /><sub><b>Juan F Gonzalez </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=juanfrank77" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://sanketdg.github.io"><img src="https://avatars3.githubusercontent.com/u/8980971?v=4" width="60px;" alt=""/><br /><sub><b>Sanket Dasgupta</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4" width="60px;" alt=""/><br /><sub><b>Nicholas Stafie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nstafie" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/francishamel"><img src="https://avatars3.githubusercontent.com/u/36383308?v=4" width="60px;" alt=""/><br /><sub><b>Francis Hamel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=francishamel" title="Code">💻</a></td>
<td align="center"><a href="http://digiguru.co.uk"><img src="https://avatars1.githubusercontent.com/u/619436?v=4" width="60px;" alt=""/><br /><sub><b>digiguru</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Documentation">📖</a></td>
</tr>
</table>

View File

@@ -0,0 +1,142 @@
# Link Reference Definition Improvements
## Current Problems
### File-by-file Insertion
For the time being, if you want to get [[wiki-links]] into all files within the workspace, you'll need to generate the link reference definitions yourself file-by-file (with the assistance of Foam).
### Wikilinks don't work on GitHub
> **TL;DR;** [workaround](#workaround) in the end of the chapter.
If you click any of the wiki-links on GitHub web UI (such as the `README.md` of a project), you'll notice that the links break with a 404 error.
At the time of writing (June 28 2020) this is a known, but unsolved error. To understand why this is the case, we need to understand what we are trading off.
So, why don't they work on GitHub?
The three components of [[link-reference-definitions]] are link label, link destination and Link Title.
The issue is the middle **link destination** component. It's configured to point to the file name **without file extension**, i.e. "file-name" instead of "file-name.md". This is to make the GitHub Pages rendering work, because if we generated the links to `file-name.md`, the links would point to the raw markdown files instead of their generated HTML versions.
| Environment | `file-name` | `file-name.md` |
| ---------------- | ----------- | -------------- |
| **VS Code** | Works | Works |
| **GitHub pages** | Works | Breaks |
| **GitHub UI** | Breaks | Works |
So as you can see, we've prioritised GitHub Pages over GitHub Web UI for the time being.
Ideally, we'd like a solution that works with both, but it's not defined yet (see [[link-reference-definitions]] for more details)
#### Workaround
For the time being, you can use relative `[markdown links](markdown-link.md)` syntax.
**Pros:**
- This will work on all platforms.
**Cons:**
- It will break the Markdown Notes [[backlinking]] support
- Less convenient to write
### Finding certain words clutter the VS Code search results
Since link reference definitions have `[//begin]` and `[//end]` guards with explanatory text that use certain words, these words (like "generate") appear in VS Code search results if you happen to search matching strings from the workspace.
## Improvement Proposal
Problem space in essence:
- During edit-time (when modifying the markdown files in an editor)
- link reference definitions are needed if user uses editor extensions that don't understand wikilinks
- link reference definitions may be annoying since they
- add content to files that the user hasn't typed in by themselves
- get out of date if user uses a tool that doesn't autogenerate them
- may clutter the search results
- During build-time (when converting markdown to html for publishing purposes)
- link reference definitions are needed, if the files are published via such tools (or to such platforms) that don't understand wikilinks
- link reference definitions might have to be in different formats depending on the publish target (e.g. Github pages vs Github UI)
The potential solution:
- For edit-time
- Make edit-time link reference definition generation optional via user settings. They should be on by default, and generating valid markdown links with a relative path to a `.md` file.
- Make format of the link reference definition configurable (whether to include '.md' or not)
- Out of recommended extensions, currently only "markdown links" doesn't support them (?). However even its [code](https://github.com/tchayen/markdown-links/blob/master/src/parsing.ts#L25) seems to include wikilink parser, so it might just be a bug?
- For build-time
- To satisfy mutually incompatible constraints between GitHub UI, VSCode UI, and GitHub Pages, we should add a pre-processing/build step for pushing to GitHub Pages.
- This would be a GitHub action (or a local script, ran via foam-cli) that outputs publish-friendly markdown format for static site generators and other publishing tools
- This build step should be pluggable, so that other transformations could be ran during it
- Have publish targets defined in settings, that support both turning the link reference definitions on/off and defining their format (.md or not). Example draft (including also edit-time aspect):
```typescript
// settings json
// see enumerations below for explanations on values
{
"foam": {
"publish": [
{
"name": "Gitlab Mirror", // name of the publish target
"linkTranspilation": "Off",
"linkReferenceDefinitions": "withExtensions"
},
{
"name": "GitHub Pages",
"linkTranspilation": "Off",
"linkReferenceDefinitions": "withoutExtensions"
},
{
"name": "Blog",
"linkTranspilation": "Off",
"linkReferenceDefinitions": "Off"
},
{
"name": "My Amazing PDF book",
"linkTranspilation": "WikiLinksToMarkdown"
}
],
"edit": {
"linkReferenceDefinitions": "Off"
}
}
}
// Defines if and how links in markdown files are somehow converted (in-place) during build time
// Note that this enumeration is not valid edit-time, since we (probably) don't want to change text like this while user is editing it
enum LinkTranspilation {
Off, // links are not transpiled
WikiLinksToMarkdown, // links using wiki-format [[link]] are converted to normal md links: [link](./some/file.md)
// if this is set, not link reference definitions are generated (not needed)
}
// Defines if and how link reference definition section is generated
enum LinkReferenceDefinitions {
Off, // link reference definitions are not generated
WithExtensions, // link reference definitions contain .md (or similar) file extensions
WithoutExtensions // link reference definitions do not contain file extenions
}
```
- With Foam repo, just use edit-time link reference definitions with '.md' extension - this makes the links work in the Github UI
- Have publish target defined for Github pages, that doesn't use '.md' extension, but still has the link reference definitions. Generate the output into gh-pages branch (or separate repo) with automation.
- This naturally requires first removing the existing link reference definitions during the build
- Other
- To clean up the search results, remove link reference definition section guards (assuming that these are not defined by the markdown spec). Use unifiedjs parse trees to identify if there's missing (or surplus) definitions (check if they are identified properly by the library), and just add the needed definitions to the bottom of the file (without guards) AND remove them if they are not needed (anywhere from the file).
Note that the proposal above supports both (build-time) inline transpilation of wikilinks as well as creation reference definitions. Depending on the direction of Foam, also only one of them could be selected. In that case the other could be implemented at later point of time.
UI-wise, the publish targets could be picked in some similar fashion as the run/debug targets in vscode by implementing a separate panel, or maybe through command execution (CTRL+SHIFT+P) - not yet defined at this point.
## Links
- [tracking issue on GitHub](https://github.com/foambubble/foam/issues/16)
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[roadmap]: roadmap "Roadmap"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[backlinking]: backlinking "Backlinking"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,37 @@
# Link Reference Definitions
## Introduction
When you use `[[wiki-links]]`, the [foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode) extension will automatically generate [Markdown Link Reference Definitions](https://spec.commonmark.org/0.29/#link-reference-definitions) at the bottom of the file. This is done to make the content of the file compatible with various Markdown tools (e.g. parsers, static site generators, VS code plugins etc), which don't support `[[wiki-links]]`.
## Example
The following example:
```md
- [[wiki-links]]
- [[github-pages]]
```
...generates the following link reference definitions to the bottom of the file:
```md
[wiki-links]: wiki-links "Wiki Links"
[github-pages]: github-pages "Github Pages"
```
You can open the [raw markdown](https://raw.githubusercontent.com/foambubble/foam/master/foam-file-format.md) to see them at the bottom of this file
## Specification
The three components of a link reference definition are `[link-label]: link-target "Link Title"`
- **link label:** The link text to match in the surrounding markdown document. This matches the inner bracket of the double-bracketed `[[wiki-link]]` notation
- **link destination** The target of the matched link
- Right now we generate link destinations without file extension. This is a choice, see [discussion here](https://foambubble.github.io/foam/wiki-links#why-dont-wiki-links-work-on-github).
- **"Link Title"** Optional title for link (The Foam template has a snippet of JavaScript to replace this on the website at runtime)
See [[link-reference-definition-improvements]] for further discussion on current problems and potential solutions.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[roadmap]: roadmap "Roadmap"
[link-reference-definition-improvements]: link-reference-definition-improvements "Link Reference Definition Improvements"
[//end]: # "Autogenerated link references"

36
docs/math-support.md Normal file
View File

@@ -0,0 +1,36 @@
---
layout: mathjax
---
# Math Support
The published pages don't support math formulas by default. To enable this feature, you can add the following code snippet to the end of `_layouts/page.html`:
```html
<script src="https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
inlineMath: [['$','$']]
}
});
</script>
```
Example of inline math: $e^{i \pi}+1=0$
Example of displayed math:
$$ f_{\mathbf{X}}\left(x_{1}, \ldots, x_{k}\right)=\frac{\exp \left(-\frac{1}{2}(\mathbf{x}-\boldsymbol{\mu})^{\mathrm{T}} \mathbf{\Sigma}^{-1}(\mathbf{x}-\boldsymbol{\mu})\right)}{\sqrt{(2 \pi)^{k}|\mathbf{\Sigma}|}} $$
If you want the index page of your Foam site to render maths, you'll need to add that to `_layouts/home.html` as well, or change the layout of the index page to be "page" instead of "home" by putting this Front Matter on the top of your `readme.md/index.md`:
```
---
layout: page
---
# Your normal title here
```
Reference: [How to support latex in github-pages](https://stackoverflow.com/questions/26275645/how-to-support-latex-in-github-pages)

View File

@@ -14,7 +14,7 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
- [Workflow](#workflow)
- [Creative ideas](#creative-ideas)
- [Other](#other)
## Contribute
- Start by reading [[contribution-guide]]
@@ -23,7 +23,7 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
## Take smart notes
- Introduction to Zettelkasten [[todo]]
## Discover
- Explore your notes using [[graph-visualisation]]
- Discover relationships with [[backlinking]]
@@ -31,15 +31,16 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
## Organise
- Using [[backlinking]] for [[reference-lists]].
## Write
- Link documents with [[wiki-links]]
- Use shortcuts for [[creating-new-notes]]
- Draw [[diagrams-in-markdown]]
- Prettify your links, [[automatically-expand-urls-to-well-titled-links]]
- Style your environment with [[custom-markdown-preview-styles]]
- Paste and link [[images-from-your-clipboard]]
- [Markdown All-in-One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) features [[todo]] [[good-first-task]]
- Manage checklists
- Manage checklists
- Automatic Table of Contents
- Live preview markdown
- _More..._
@@ -54,15 +55,17 @@ Guides, tips and strategies for getting the most out of your Foam workspace!
## Publish
- Publish to [[github-pages]]
- Publish to [[gitlab-pages]]
- Publish your site with [[eleventy-and-netlify]]
- Make the site your own by [[customising-styles]].
- Host your own website [[todo]]
- Math support [[math-support]]
## Collaborate
- Give your team push access to your GitHub repo [[todo]]
- Real-time collaboration via VS Code Live Share [[todo]]
- Accept patches via GitHub PRs [[todo]]
## Workflow
Workflow recipes wanted!
@@ -71,7 +74,7 @@ _See [[contribution-guide]] and [[how-to-write-recipes]]._
## Creative ideas
Creative ideas welcome!
Creative ideas welcome!
- Support [Anki](https://apps.ankiweb.net/) cards from notes like [Remnote](https://www.remnote.io/) [[todo]]
@@ -79,7 +82,7 @@ _See [[contribution-guide]] and [[how-to-write-recipes]]._
## Other
Thought of a recipe but don't see a category for them? Add them here and we'll organise them once we detect a theme.
Thought of a recipe but don't see a category for them? Add them here and we'll organise them once we detect a theme.
_See [[contribution-guide]] and [[how-to-write-recipes]]._
@@ -96,8 +99,11 @@ _See [[contribution-guide]] and [[how-to-write-recipes]]._
[diagrams-in-markdown]: diagrams-in-markdown "Diagrams in Markdown"
[automatically-expand-urls-to-well-titled-links]: automatically-expand-urls-to-well-titled-links "Automatically Expand URLs to Well-Titled Links"
[custom-markdown-preview-styles]: custom-markdown-preview-styles "Custom Markdown Preview Styles"
[images-from-your-clipboard]: images-from-your-clipboard "Images from your Clipboard"
[good-first-task]: good-first-task "Good First Task"
[git-integration]: git-integration "Git integration"
[github-pages]: github-pages "Github Pages"
[eleventy-and-netlify]: eleventy-and-netlify "Eleventy and Netlify"
[customising-styles]: customising-styles "Customising Styles"
[math-support]: math-support "Math Support"
[//end]: # "Autogenerated link references"

View File

@@ -42,6 +42,8 @@ If a roadmap item is a stub, **consider** opening a [GitHub issue](https://githu
- [[materialized-backlinks]]
- [[automatic-git-syncing]]
- [[git-flows-for-teams]]
- [[user-settings]]
- [[link-reference-definitions]]
### Publishing

View File

@@ -4,20 +4,17 @@ It would be good to have some shared terminology to talk about Foam concepts. So
Here's some ideas, these are open for discussion.
## Foam (project)
## Foam, the software project
The set of tools and ideas collected in this organisation.
## Foam workspace
## (Your) Foam
The directory/repository where you keep all your documents.
The directory/repository where you keep all your notes.
Also happens to sound quite a lot like Home. Funny, that.
## Bubble
Individual Foam document, written in Markdown.
Individual Foam note, written in Markdown.
## Foam blog
When you use Foam to publish content to an audience.
_Better ideas welcome._

9
docs/user-settings.md Normal file
View File

@@ -0,0 +1,9 @@
# User Settings (stub)
**[[todo]] This [[roadmap]] item needs more specification work.**
[//begin]: # "Autogenerated link references for markdown compatibility"
[todo]: todo "Todo"
[roadmap]: roadmap "Roadmap"
[//end]: # "Autogenerated link references"

View File

@@ -1,6 +1,6 @@
# Wiki Links
Foam enables you to Link pages together using `[[file-name]]` annotations.
Foam enables you to Link pages together using `[[file-name]]` annotations (i.e. `[[media-wiki]]` links).
- Both `[[file-name]]` and `[[file-name.md]]` work
- Type `[[` and start typing a file name for autocompletion.
@@ -16,72 +16,15 @@ Foam enables you to Link pages together using `[[file-name]]` annotations.
## Markdown compatibility
The [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode) extension automatically generates [markdown link reference definitions](https://spec.commonmark.org/0.29/#link-reference-definitions) at the bottom of the file to make wiki-links compatible with Markdown tools and parsers.
If you look at link references the bottom of any Foam workspace file that uses wiki-links, you should see an automatically generated list of references that look as follows:
```markdown
[wiki-links]: wiki-links "Wiki Links"
[other-page]: other-page "Other Page"
```
These exist to make `[[wiki-links]]` compatible with Markdown-consuming tools such as static site generators, VS Code plugins etc.
## Why don't `[[wiki-links]]` work on GitHub
> **TL;DR;** [workaround](#workaround) in the end.
If you click any of the wiki-links on GitHub web UI (such as the `README.md` of a project), you'll notice that the links break with a 404 error.
At the time of writing (June 28 2020) this is a known, but unsolved error. To understand why this is the case, we need to understand what we are trading off.
So, why don't they work on GitHub?
The three components of a [link reference definitions](https://spec.commonmark.org/0.29/#link-reference-definitions) are:
- **link label:** The link text to match in the surrounding markdown document. This matches the inner bracket of the double-bracketed `[[wiki-link]]` notation
- **link destination** The target of the matched link
- **"Link Title"** Optional title for link (The Foam template has a snippet of JavaScript to replace this on the website at runtime)
The issue is the middle **link destination** component. It's configured to point to the file name **without file extension**, i.e. "file-name" instead of "file-name.md". This is to make the GitHub Pages rendering work, because if we generated the links to `file-name.md`, the links would point to the raw markdown files instead of their generated HTML versions.
| Environment | `file-name` | `file-name.md` |
| ---------------- | ----------- | -------------- |
| **VS Code** | Works | Works |
| **GitHub pages** | Works | Breaks |
| **GitHub UI** | Breaks | Works |
So as you can see, we've prioritised GitHub Pages over GitHub Web U for the time being.
Ideally, we'd like a solution that works with both, but I haven't thought of it yet. Ideas include:
- **Writing a better static side generator that works with `file-name.md` link targets.** This is on the [[roadmap]], but for the time being GitHub Pages support is as must-have.
- **Adding a configuration setting to generate `file-name.md` link targets.** This is fine and I would accept this contribution to [foam-vscode](https://github.com/foambubble/foam/tree/master/packages/foam-vscode), but it doesn't solve the core problem.
An acceptable solution may include one where we don't generate link reference definitions at all, but if we do, ideally, we'd like to generate `file-name.md` links since those are more standards compatible for different markdown tools.
I'm sure there's an elegant-ish solution out there. Ideas and suggestions welcome that the [tracking issue on GitHub](https://github.com/foambubble/foam/issues/16)
### Workaround
For the time being, you can use relative `[markdown links](markdown-link.md)` syntax.
**Pros:**
- This will work on all platforms.
**Cons:**
- It will break the Markdown Notes [[backlinking]] support
- Less convenient to write
The [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode) extension automatically generates [[link-reference-definitions]] at the bottom of the file to make wiki-links compatible with Markdown tools and parsers.
## Read more
- [[foam-file-format]]
- See [[link-reference-definition-improvements]] for further discussion on current problems and potential solutions.
[//begin]: # "Autogenerated link references for markdown compatibility"
[wiki-links]: wiki-links "Wiki Links"
[roadmap]: roadmap "Roadmap"
[backlinking]: backlinking "Backlinking"
[link-reference-definitions]: link-reference-definitions "Link Reference Definitions"
[foam-file-format]: foam-file-format "Foam File Format"
[link-reference-definition-improvements]: link-reference-definition-improvements "Link Reference Definition Improvements"
[//end]: # "Autogenerated link references"

View File

@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};

View File

@@ -0,0 +1,188 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/p6/5y5l8tbs1d32pq9b596lk48h0000gn/T/jest_dx",
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: undefined,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
// coverageProvider: "babel",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/__tests__/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};

View File

@@ -11,17 +11,25 @@
"@oclif/command": "^1",
"@oclif/config": "^1",
"@oclif/plugin-help": "^3",
"foam-core": "^0.2.0",
"ora": "^4.0.4",
"tslib": "^1"
},
"devDependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"@babel/preset-typescript": "^7.10.4",
"@oclif/dev-cli": "^1",
"@types/node": "^10",
"babel-jest": "^26.1.0",
"chai": "^4",
"eslint": "^5.13",
"eslint-config-oclif": "^3.1",
"eslint-config-oclif-typescript": "^0.1",
"foam-core": "^0.2.0",
"globby": "^10",
"jest": "^26.1.0",
"mock-fs": "^4.12.0",
"ts-node": "^8",
"typescript": "^3.3"
},
@@ -54,10 +62,9 @@
"scripts": {
"cli": "./bin/run",
"postpack": "rm -f oclif.manifest.json",
"posttest": "eslint . --ext .ts --config .eslintrc",
"prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme",
"test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
"test": "jest",
"version": "oclif-dev readme && git add README.md"
},
"types": "lib/index.d.ts"
}
}

View File

@@ -0,0 +1,74 @@
import { Command, flags } from '@oclif/command';
import * as ora from 'ora';
import { initializeNoteGraph, generateLinkReferences, generateHeading, getKebabCaseFileName } from 'foam-core';
import { applyTextEdit } from '../utils/apply-text-edit';
import { writeFileToDisk } from '../utils/write-file-to-disk';
import { isValidDirectory } from '../utils';
export default class Janitor extends Command {
static description = 'Updates link references and heading across all the markdown files in the given workspaces';
static examples = [
`$ foam-cli janitor path-to-foam-workspace
Successfully generated link references and heading!
`,
]
static flags = {
help: flags.help({ char: 'h' }),
}
static args = [{ name: 'workspacePath' }]
async run() {
const spinner = ora('Reading Files').start();
const { args, flags } = this.parse(Janitor)
const { workspacePath = './' } = args;
if (isValidDirectory(workspacePath)) {
const graph = await initializeNoteGraph(workspacePath);
const notes = graph.getNotes().filter(Boolean); // removes undefined notes
spinner.succeed();
spinner.text = `${notes.length} files found`;
spinner.succeed();
// exit early if no files found.
if (notes.length === 0) {
this.exit();
}
spinner.text = 'Generating link definitions';
const fileWritePromises = notes.map(note => {
// Get edits
const heading = generateHeading(note);
const definitions = generateLinkReferences(note, graph);
// apply Edits
let file = note.source;
file = heading ? applyTextEdit(file, heading) : file;
file = definitions ? applyTextEdit(file, definitions) : file;
if (heading || definitions) {
return writeFileToDisk(note.path, file);
}
return Promise.resolve(null);
})
await Promise.all(fileWritePromises);
spinner.succeed();
spinner.succeed('Done!');
}
else {
spinner.fail('Directory does not exist!');
}
}
}

View File

@@ -0,0 +1,95 @@
import { Command, flags } from '@oclif/command';
import * as ora from 'ora';
import { initializeNoteGraph, generateLinkReferences, generateHeading, getKebabCaseFileName } from 'foam-core';
import { applyTextEdit } from '../utils/apply-text-edit';
import { writeFileToDisk } from '../utils/write-file-to-disk';
import { renameFile } from '../utils/rename-file';
import { isValidDirectory } from '../utils';
// @todo: Refactor 'migrate' and 'janitor' commands and avoid repeatition
export default class Migrate extends Command {
static description = 'Updates file names, link references and heading across all the markdown files in the given workspaces';
static examples = [
`$ foam-cli migrate path-to-foam-workspace
Successfully generated link references and heading!
`,
]
static flags = {
help: flags.help({ char: 'h' }),
}
static args = [{ name: 'workspacePath' }]
async run() {
const spinner = ora('Reading Files').start();
const { args, flags } = this.parse(Migrate)
const { workspacePath = './' } = args;
if (isValidDirectory(workspacePath)) {
let graph = await initializeNoteGraph(workspacePath);
let notes = graph.getNotes().filter(Boolean); // removes undefined notes
spinner.succeed();
spinner.text = `${notes.length} files found`;
spinner.succeed();
// exit early if no files found.
if (notes.length === 0) {
this.exit();
}
// Kebab case file names
const fileRename = notes.map(note => {
const kebabCasedFileName = getKebabCaseFileName(note.title);
if (kebabCasedFileName) {
return renameFile(note.path, kebabCasedFileName);
}
return Promise.resolve(null);
})
await Promise.all(fileRename);
spinner.text = 'Renaming files';
// Reinitialize the graph after renaming files
graph = await initializeNoteGraph(workspacePath);
notes = graph.getNotes().filter(Boolean); // remove undefined notes
spinner.succeed();
spinner.text = 'Generating link definitions'
const fileWritePromises = await Promise.all(notes.map(note => {
// Get edits
const heading = generateHeading(note);
const definitions = generateLinkReferences(note, graph);
// apply Edits
let file = note.source;
file = heading ? applyTextEdit(file, heading) : file;
file = definitions ? applyTextEdit(file, definitions) : file;
if (heading || definitions) {
return writeFileToDisk(note.path, file);
}
return Promise.resolve(null);
}))
await Promise.all(fileWritePromises);
spinner.succeed();
spinner.succeed('Done!');
}
else {
spinner.fail('Directory does not exist!');
}
}
}

View File

@@ -0,0 +1,63 @@
import {Command, flags} from '@oclif/command'
import { execSync } from 'child_process';
import * as ora from 'ora';
export default class Publish extends Command {
static description = 'Push all changes to git repository';
static examples = [
`$ foam publish -m "Optional log message"`,
]
static flags = {
message: flags.string({
char: 'm',
description: "optional message"
}),
remote: flags.string({
char: 'r',
description: "remote"
}),
branch: flags.string({
char: 'b',
description: "branch"
})
}
async execWithSpinner(command: string, message: string) {
const spinner = ora(message).start();
// @todo handle errors
const response = execSync(command).toString();
spinner.succeed(`${message} Done!`);
return response;
}
async printPublishInfo(remote: string) {
// @todo actually get this data from GH API
const [, remotePath] = execSync(`git remote get-url ${remote}`).toString().trim().split(':');
const [repo, org] = remotePath.split('/').reverse();
console.log('');
console.log(`🎉 Your changes will be available shortly at https://${org}.github.io/${repo.replace('.git', '')}`);
console.log('');
}
async run() {
const {flags} = this.parse(Publish);
// @todo improve
const message = flags.message || 'foam publish';
const remote = flags.remote || 'origin';
const branch = flags.branch || 'master';
await this.execWithSpinner(`git add -A`, 'Staging changes...');
await this.execWithSpinner(`git commit -m "${message}"`, 'Creating a commit...');
await this.execWithSpinner(`git push ${remote} ${branch}`, "Publishing...");
await this.printPublishInfo(remote);
}
}

View File

@@ -0,0 +1,18 @@
import { TextEdit } from 'foam-core';
/**
*
* @param text text on which the textEdit will be applied
* @param textEdit
* @returns {string} text with the applied textEdit
*/
export const applyTextEdit = (text: string, textEdit: TextEdit): string => {
const characters = text.split('');
const startOffset = textEdit.range.start.offset || 0;
const endOffset = textEdit.range.end.offset || 0;
const deleteCount = endOffset - startOffset;
const textToAppend = `${textEdit.newText}`;
characters.splice(startOffset, deleteCount, textToAppend);
return characters.join('');
}

View File

@@ -0,0 +1,4 @@
import * as fs from 'fs';
export const isValidDirectory = (path: string) => fs.existsSync(path) && fs.lstatSync(path).isDirectory();

View File

@@ -0,0 +1,15 @@
import * as fs from 'fs';
import * as path from 'path';
/**
*
* @param fileUri absolute path for the file that needs to renamed
* @param newFileName "new file name" without the extension
*/
export const renameFile = async (fileUri: string, newFileName: string) => {
const dirName = path.dirname(fileUri);
const extension = path.extname(fileUri);
const newFileUri = path.join(dirName, `${newFileName}${extension}`);
return fs.promises.rename(fileUri, newFileUri);
}

View File

@@ -0,0 +1,5 @@
import * as fs from 'fs';
export const writeFileToDisk = async (fileUri: string, data: string) => {
return fs.promises.writeFile(fileUri, data);
}

View File

@@ -0,0 +1,82 @@
import { applyTextEdit } from '../src/utils/apply-text-edit';
describe('applyTextEdit', () => {
it('should return text with applied TextEdit in the end of the string', () => {
const textEdit = {
newText: `\n 4. this is fourth line`,
range: {
start: { line: 3, column: 1, offset: 79 }, end: { line: 3, column: 1, offset: 79 },
},
};
const text = `
1. this is first line
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is second line
3. this is third line
4. this is fourth line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected)
})
it('should return text with applied TextEdit at the top of the string', () => {
const textEdit = {
newText: `\n 1. this is first line`,
range: {
start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 },
},
};
const text = `
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is second line
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected)
});
it('should return text with applied TextEdit in the middle of the string', () => {
const textEdit = {
newText: `\n 2. this is the updated second line`,
range: {
start: { line: 0, column: 0, offset: 26 }, end: { line: 0, column: 0, offset: 53 },
},
};
const text = `
1. this is first line
2. this is second line
3. this is third line
`;
const expected = `
1. this is first line
2. this is the updated second line
3. this is third line
`;
const actual = applyTextEdit(text, textEdit);
expect(actual).toBe(expected)
});
})

View File

@@ -0,0 +1,28 @@
import { renameFile } from '../src/utils/rename-file'
import * as fs from 'fs';
import mockFS from 'mock-fs';
const doesFileExist = (path) => fs.promises.access(path).then(() => true).catch(() => false);
describe('renameFile', () => {
const fileUri = './test/oldFileName.md';
beforeAll(() => {
mockFS({ [fileUri]: '' })
});
afterAll(() => {
mockFS.restore();
});
it('should rename existing file', async () => {
expect(await doesFileExist(fileUri)).toBe(true);
renameFile(fileUri, 'new-file-name');
expect(await doesFileExist(fileUri)).toBe(false);
expect(await doesFileExist('./test/new-file-name.md')).toBe(true);
});
});

View File

@@ -0,0 +1,24 @@
import { writeFileToDisk } from '../src/utils/write-file-to-disk'
import * as fs from 'fs';
import mockFS from 'mock-fs';
describe('writeFileToDisk', () => {
const fileUri = './test-file.md';
beforeAll(() => {
mockFS({ [fileUri]: 'content in the existing file' });
})
afterAll(() => {
fs.unlinkSync(fileUri);
mockFS.restore();
})
it('should overrwrite existing file in the disk with the new data', async () => {
const expected = `content in the new file`;
await writeFileToDisk(fileUri, expected);
const actual = await fs.promises.readFile(fileUri, { encoding: 'utf8' });
expect(actual).toBe(expected);
});
})

View File

@@ -15,6 +15,7 @@
"prepare": "tsdx build"
},
"devDependencies": {
"@types/github-slugger": "^1.3.0",
"@types/graphlib": "^2.1.6",
"@types/lodash": "^4.14.157",
"husky": "^4.2.5",
@@ -23,10 +24,14 @@
"typescript": "^3.9.5"
},
"dependencies": {
"detect-newline": "^3.1.0",
"github-slugger": "^1.3.0",
"glob": "^7.1.6",
"graphlib": "^2.1.8",
"lodash": "^4.17.15",
"lodash": "^4.17.19",
"remark-parse": "^8.0.2",
"remark-wiki-link": "^0.0.4",
"title-case": "^3.0.2",
"unified": "^9.0.0",
"unist-util-visit": "^2.0.2"
},

View File

@@ -3,20 +3,25 @@ import { NoteGraph, Note, NoteLink } from './note-graph';
export {
createNoteFromMarkdown,
createMarkdownReferences,
stringifyMarkdownLinkReferenceDefinition,
} from './markdown-provider';
export { NoteGraph, Note, NoteLink }
export { TextEdit, generateHeading, generateLinkReferences, getKebabCaseFileName } from './janitor'
export { initializeNoteGraph } from './initialize-note-graph'
export { NoteGraph, Note, NoteLink };
export interface FoamConfig {
// TODO
}
export interface Foam {
notes: NoteGraph
notes: NoteGraph;
// config: FoamConfig
}
export const createFoam = (config: FoamConfig) => ({
notes: new NoteGraph(),
config: config,
})
});

View File

@@ -0,0 +1,30 @@
import glob from 'glob';
import { promisify } from 'util';
import fs from 'fs';
import os from 'os';
import detectNewline from 'detect-newline';
import { NoteGraph } from './note-graph';
import { createNoteFromMarkdown } from './markdown-provider';
const findAllFiles = promisify(glob);
export const initializeNoteGraph = async (workspacePath: string) => {
// remove trailing slash from workspacePath if exists
if (workspacePath.substr(-1) == '/') workspacePath = workspacePath.slice(0, -1);
const files = await findAllFiles(`${workspacePath}/**/*.md`, {});
const graph = new NoteGraph();
await Promise.all(
(await files).map(f => {
return fs.promises.readFile(f).then(data => {
const markdown = (data || '').toString();
const eol = detectNewline(markdown) || os.EOL;
graph.setNote(createNoteFromMarkdown(f, markdown, eol));
});
})
);
return graph;
}

View File

@@ -0,0 +1,87 @@
import { Position } from 'unist';
import GithubSlugger from 'github-slugger';
import { Note, NoteGraph } from '../index';
import {
createMarkdownReferences,
stringifyMarkdownLinkReferenceDefinition,
} from '../markdown-provider';
import { getHeadingFromFileName } from '../utils'
const slugger = new GithubSlugger()
export interface TextEdit {
range: Position;
newText: string;
}
export const generateLinkReferences = (note: Note, ng: NoteGraph): TextEdit | null => {
if (!note) {
return null;
}
const newReferences = createMarkdownReferences(ng, note.id).map(
stringifyMarkdownLinkReferenceDefinition
).join('\n');
if (note.definitions.length === 0) {
if (newReferences.length === 0) {
return null;
}
const padding = note.end.column === 1 ? note.eol : `${note.eol}${note.eol}`;
return {
newText: `${padding}${newReferences}`,
range: {
start: note.end,
end: note.end,
},
};
} else {
const first = note.definitions[0];
const last = note.definitions[note.definitions.length - 1];
const oldRefrences = note.definitions.map(stringifyMarkdownLinkReferenceDefinition).join(note.eol);
if (oldRefrences === newReferences) {
return null;
}
return {
// @todo: do we need to ensure new lines?
newText: `${newReferences}`,
range: {
start: first.position!.start,
end: last.position!.end,
},
};
}
};
export const generateHeading = (note: Note): TextEdit | null => {
if (!note) {
return null;
}
// Note: This may not work if the heading is same as the file name
if (note.title !== note.id) {
return null;
}
return {
newText: `# ${getHeadingFromFileName(note.id)}${note.eol}${note.eol}`,
range: {
start: { line: 0, column: 0, offset: 0 },
end: { line: 0, column: 0, offset: 0 }
}
}
};
/**
*
* @param fileName
* @returns null if file name is already in kebab case otherise returns
* the kebab cased file name
*/
export const getKebabCaseFileName = (fileName: string) => {
const kebabCasedFileName = slugger.slug(fileName);
return kebabCasedFileName === fileName ? null : kebabCasedFileName;
}

View File

@@ -4,7 +4,7 @@ import wikiLinkPlugin from 'remark-wiki-link';
import visit, { CONTINUE, EXIT } from 'unist-util-visit';
import { Node, Parent } from 'unist';
import * as path from 'path';
import { Link, Note, NoteGraph } from './note-graph';
import { Note, NoteLink, NoteLinkDefinition, NoteGraph } from './note-graph';
import { dropExtension } from './utils';
let processor: unified.Processor | null = null;
@@ -18,7 +18,7 @@ function parse(markdown: string): Node {
return processor.parse(markdown);
}
export function createNoteFromMarkdown(uri: string, markdown: string): Note {
export function createNoteFromMarkdown(uri: string, markdown: string, eol: string): Note {
const filename = path.basename(uri);
const id = path.parse(filename).name;
const tree = parse(markdown);
@@ -29,29 +29,46 @@ export function createNoteFromMarkdown(uri: string, markdown: string): Note {
}
return title === id ? CONTINUE : EXIT;
});
const links: Link[] = [];
const links: NoteLink[] = [];
const definitions: NoteLinkDefinition[] = [];
visit(tree, node => {
if (node.type === 'wikiLink') {
links.push({
from: id,
to: node.value as string,
text: node.value as string,
position: node.position!,
});
}
if (node.type === 'definition') {
definitions.push({
label: node.label as string,
url: node.url as string,
title: node.title as string,
position: node.position,
});
}
});
return new Note(id, title, links, uri, markdown);
const end = tree.position!.end;
return new Note(id, title, links, definitions, end, uri, markdown, eol);
}
interface MarkdownReference {
linkText: string;
wikiLink: string;
pageTitle: string;
}
export function stringifyMarkdownLinkReferenceDefinition(
definition: NoteLinkDefinition
) {
let text = `[${definition.label}]: ${definition.url}`;
if (definition.title) {
text = `${text} "${definition.title}"`;
}
return text;
}
export function createMarkdownReferences(
graph: NoteGraph,
noteId: string
): MarkdownReference[] {
): NoteLinkDefinition[] {
const source = graph.getNote(noteId);
// Should never occur since we're already in a file,
@@ -72,7 +89,7 @@ export function createMarkdownReferences(
// but int the future we may want to surface these too
if (!target) {
console.log(
`Link '${link.to}' in '${noteId}' points to a non-existing note.`
`Warning: Link '${link.to}' in '${noteId}' points to a non-existing note.`
);
return null;
}
@@ -85,11 +102,11 @@ export function createMarkdownReferences(
// [wiki-link-text]: wiki-link "Page title"
return {
linkText: link.to,
wikiLink: relativePathWithoutExtension,
pageTitle: target.title,
label: link.text,
url: relativePathWithoutExtension,
title: target.title,
};
})
.filter(Boolean)
.sort() as MarkdownReference[];
.sort() as NoteLinkDefinition[];
}

View File

@@ -1,4 +1,6 @@
import { Graph, Edge } from 'graphlib';
import { Position, Point } from 'unist';
import GithubSlugger from 'github-slugger';
type ID = string;
@@ -11,6 +13,14 @@ export interface Link {
export interface NoteLink {
to: ID;
text: string;
position: Position;
}
export interface NoteLinkDefinition {
label: string;
url: string;
title?: string;
position?: Position;
}
export class Note {
@@ -18,20 +28,29 @@ export class Note {
public title: string;
public source: string;
public path: string;
public end: Point;
public eol: string;
public links: NoteLink[];
public definitions: NoteLinkDefinition[];
constructor(
id: ID,
title: string,
links: NoteLink[],
definitions: NoteLinkDefinition[],
end: Point,
path: string,
source: string
source: string,
eol: string
) {
this.id = id;
this.title = title;
this.source = source;
this.path = path;
this.links = links;
this.definitions = definitions;
this.end = end;
this.eol = eol;
}
}
@@ -50,7 +69,8 @@ export class NoteGraph {
}
this.graph.setNode(note.id, note);
note.links.forEach(link => {
this.graph.setEdge(note.id, link.to, link.text);
const slugger = new GithubSlugger();
this.graph.setEdge(note.id, slugger.slug(link.to), link.text);
});
}
@@ -67,25 +87,25 @@ export class NoteGraph {
public getAllLinks(noteId: ID): Link[] {
return (this.graph.nodeEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
convertEdgeToLink(edge, this.graph)
);
}
public getForwardLinks(noteId: ID): Link[] {
return (this.graph.outEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
convertEdgeToLink(edge, this.graph)
);
}
public getBacklinks(noteId: ID): Link[] {
return (this.graph.inEdges(noteId) || []).map(edge =>
convertEdgeToLink(edge)
convertEdgeToLink(edge, this.graph)
);
}
}
const convertEdgeToLink = (edge: Edge): Link => ({
from: edge.v,
to: edge.w,
text: edge.name || edge.w,
const convertEdgeToLink = (edge: Edge, graph: Graph): Link => ({
from: edge.v,
to: edge.w,
text: graph.edge(edge.v, edge.w),
});

View File

@@ -1,5 +1,16 @@
import { titleCase } from 'title-case';
export function dropExtension(path: string): string {
const parts = path.split('.');
parts.pop();
return parts.join('.');
}
/**
*
* @param filename
* @returns title cased heading after removing special characters
*/
export const getHeadingFromFileName = (filename: string): string => {
return titleCase(filename.replace(/[^\w\s]/gi, ' '));
}

View File

@@ -0,0 +1,3 @@
# Roam Document
[[Second Roam Document]]

View File

@@ -0,0 +1 @@
# Second Roam Document

View File

@@ -0,0 +1 @@
This file is missing a title

View File

@@ -0,0 +1,7 @@
# First Document
[[file-without-title]]
[//begin]: # 'Autogenerated link references for markdown compatibility'
[second-document]: second-document 'Second Document'
[//end]: # 'Autogenerated link references'

View File

@@ -0,0 +1,9 @@
# Index
This file is intentionally missing the link reference definitions
[[first-document]]
[[second-document]]
[[file-without-title]]

View File

@@ -0,0 +1,9 @@
# Second Document
This is just a link target for now.
We can use it for other things later if needed.
[//begin]: # 'Autogenerated link references for markdown compatibility'
[first-document]: first-document 'First Document'
[//end]: # 'Autogenerated link references'

View File

@@ -0,0 +1,11 @@
# Third Document
All the link references are correct in this file.
[[first-document]]
[[second-document]]
[first-document]: first-document "First Document"
[second-document]: second-document "Second Document"

View File

@@ -1,11 +1,25 @@
import { NoteGraph, Note } from '../src/note-graph';
const position = {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
};
const documentEnd = position.end;
const eol = '\n';
describe('Note graph', () => {
it('Adds notes to graph', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(new Note('page-b', 'page-b', [], '/page-b.md', ''));
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(
new Note('page-a', 'page-a', [], [], documentEnd, eol, '/page-a.md', '')
);
graph.setNote(
new Note('page-b', 'page-b', [], [], documentEnd, eol, '/page-b.md', '')
);
graph.setNote(
new Note('page-c', 'page-c', [], [], documentEnd, eol, '/page-c.md', '')
);
expect(
graph
@@ -17,17 +31,24 @@ describe('Note graph', () => {
it('Detects forward links', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(
new Note('page-a', 'page-a', [], [], documentEnd, eol, '/page-a.md', '')
);
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go' }],
[{ to: 'page-a', text: 'go', position }],
[],
documentEnd,
eol,
'/page-b.md',
''
)
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(
new Note('page-c', 'page-c', [], [], documentEnd, eol, '/page-c.md', '')
);
expect(
graph
@@ -39,17 +60,24 @@ describe('Note graph', () => {
it('Detects backlinks', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(
new Note('page-a', 'page-a', [], [], documentEnd, eol, '/page-a.md', '')
);
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go' }],
[{ to: 'page-a', text: 'go', position }],
[],
documentEnd,
eol,
'/page-b.md',
''
)
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(
new Note('page-c', 'page-c', [], [], documentEnd, eol, '/page-c.md', '')
);
expect(
graph
@@ -62,7 +90,9 @@ describe('Note graph', () => {
it('Fails when accessing non-existing node', () => {
expect(() => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/path-b.md', ''));
graph.setNote(
new Note('page-a', 'page-a', [], [], documentEnd, eol, '/path-b.md', '')
);
graph.getNote('non-existing');
}).toThrow();
});
@@ -73,7 +103,10 @@ describe('Note graph', () => {
new Note(
'page-a',
'page-a',
[{ to: 'non-existing', text: 'does not exist' }],
[{ to: 'non-existing', text: 'does not exist', position }],
[],
documentEnd,
eol,
'/path-b.md',
''
)
@@ -83,17 +116,24 @@ describe('Note graph', () => {
it('Updates links when modifying note', () => {
const graph = new NoteGraph();
graph.setNote(new Note('page-a', 'page-a', [], '/page-a.md', ''));
graph.setNote(
new Note('page-a', 'page-a', [], [], documentEnd, eol, '/page-a.md', '')
);
graph.setNote(
new Note(
'page-b',
'page-b',
[{ to: 'page-a', text: 'go' }],
[{ to: 'page-a', text: 'go', position }],
[],
documentEnd,
eol,
'/page-b.md',
''
)
);
graph.setNote(new Note('page-c', 'page-c', [], '/page-c.md', ''));
graph.setNote(
new Note('page-c', 'page-c', [], [], documentEnd, eol, '/page-c.md', '')
);
expect(
graph
@@ -118,7 +158,10 @@ describe('Note graph', () => {
new Note(
'page-b',
'page-b',
[{ to: 'page-c', text: 'go' }],
[{ to: 'page-c', text: 'go', position }],
[],
documentEnd,
eol,
'/path-2b.md',
''
)

View File

@@ -0,0 +1,53 @@
import * as path from 'path';
import { NoteGraph, Note } from '../../src/note-graph';
import { generateHeading } from '../../src/janitor';
import { initializeNoteGraph } from '../../src/initialize-note-graph';
describe('generateHeadings', () => {
let _graph: NoteGraph;
beforeAll(async () => {
_graph = await initializeNoteGraph(path.join(__dirname, '../__scaffold__'));
});
it('should add heading to a file that does not have them', () => {
const note = _graph.getNote('file-without-title') as Note;
const expected = {
newText: `# File without Title
`,
range: {
start: {
line: 0,
column: 0,
offset: 0,
},
end: {
line: 0,
column: 0,
offset: 0,
},
},
}
const actual = generateHeading(note!);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should not cause any changes to a file that does heading', () => {
const note = _graph.getNote('index') as Note;
const expected = null;
const actual = generateHeading(note!);
expect(actual).toEqual(expected);
})
});

View File

@@ -0,0 +1,106 @@
import * as path from 'path';
import { NoteGraph, Note } from '../../src/note-graph';
import { generateLinkReferences } from '../../src/janitor';
import { initializeNoteGraph } from '../../src/initialize-note-graph';
describe('generateLinkReferences', () => {
let _graph: NoteGraph;
beforeAll(async () => {
_graph = await initializeNoteGraph(path.join(__dirname, '../__scaffold__'));
});
it('initialised test graph correctly', () => {
expect(_graph.getNotes().length).toEqual(5);
});
it('should add link references to a file that does not have them', () => {
const note = _graph.getNote('index') as Note;
const expected = {
newText: `
[first-document]: first-document "First Document"
[second-document]: second-document "Second Document"
[file-without-title]: file-without-title "file-without-title"`,
range: {
start: {
line: 10,
column: 1,
offset: 140,
},
end: {
line: 10,
column: 1,
offset: 140,
},
},
};
const actual = generateLinkReferences(note!, _graph);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should remove link definitions from a file that has them, if no links are present', () => {
const note = _graph.getNote('second-document') as Note;
const expected = {
newText: "",
range: {
start: {
line: 7,
column: 1,
offset: 105,
},
end: {
line: 9,
column: 43,
offset: 269,
},
},
};
const actual = generateLinkReferences(note!, _graph);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should update link definitions if they are present but changed', () => {
const note = _graph.getNote('first-document') as Note;
const expected = {
newText: `[file-without-title]: file-without-title "file-without-title"`,
range: {
start: {
line: 5,
column: 1,
offset: 42,
},
end: {
line: 7,
column: 43,
offset: 209,
},
},
};
const actual = generateLinkReferences(note!, _graph);
expect(actual!.range.start).toEqual(expected.range.start);
expect(actual!.range.end).toEqual(expected.range.end);
expect(actual!.newText).toEqual(expected.newText);
});
it('should not cause any changes if link reference definitions were up to date', () => {
const note = _graph.getNote('third-document') as Note;
const expected = null;
const actual = generateLinkReferences(note!, _graph);
expect(actual).toEqual(expected);
});
});

View File

@@ -18,12 +18,13 @@ const pageC = `
# Page C
`;
// @todo: Add tests for definitions
describe('Markdown loader', () => {
it('Converts markdown to notes', () => {
const graph = new NoteGraph();
graph.setNote(createNoteFromMarkdown('page-a', pageA));
graph.setNote(createNoteFromMarkdown('page-b', pageB));
graph.setNote(createNoteFromMarkdown('page-c', pageC));
graph.setNote(createNoteFromMarkdown('page-a', pageA, '\n'));
graph.setNote(createNoteFromMarkdown('page-b', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('page-c', pageC, '\n'));
expect(
graph
@@ -35,9 +36,9 @@ describe('Markdown loader', () => {
it('Parses wikilinks correctly', () => {
const graph = new NoteGraph();
graph.setNote(createNoteFromMarkdown('page-a', pageA));
graph.setNote(createNoteFromMarkdown('page-b', pageB));
graph.setNote(createNoteFromMarkdown('page-c', pageC));
graph.setNote(createNoteFromMarkdown('page-a', pageA, '\n'));
graph.setNote(createNoteFromMarkdown('page-b', pageB, '\n'));
graph.setNote(createNoteFromMarkdown('page-c', pageC, '\n'));
expect(graph.getBacklinks('page-b').map(link => link.from)).toEqual([
'page-a',

View File

@@ -1,6 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src", "types"],
"include": [
"src",
"types"
],
"compilerOptions": {
"composite": true,
"declaration": true,
@@ -10,13 +13,14 @@
"rootDir": "./src",
// for references
"baseUrl": "src",
"lib": ["esnext"],
"lib": [
"esnext"
],
"module": "esnext",
"importHelpers": true,
"sourceMap": true,
"strict": true,
"noUnusedLocals": true,
// "noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
@@ -27,4 +31,4 @@
// },
// "jsx": "react",
},
}
}

View File

@@ -1 +1 @@
declare module 'remark-wiki-link';
declare module 'remark-wiki-link';

View File

@@ -1077,6 +1077,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.44.tgz#980cc5a29a3ef3bea6ff1f7d021047d7ea575e21"
integrity sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g==
"@types/graphlib@^2.1.6":
version "2.1.6"
resolved "https://registry.yarnpkg.com/@types/graphlib/-/graphlib-2.1.6.tgz#5c7b515bfadc08d737f2e84fadbd151117c73207"
integrity sha512-os2Xj+pV/iwLkLX17LWuXdPooA4Jf4xg8WSdKPUi0tCSseP95oikcA1irOgVl3K2QYnoXrjJT3qVZeQ1uskB7g==
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -2843,6 +2848,13 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
graphlib@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==
dependencies:
lodash "^4.17.15"
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -3986,10 +3998,10 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
log-symbols@^2.2.0:
version "2.2.0"
@@ -5975,6 +5987,15 @@ unist-util-visit@^2.0.0:
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"
unist-util-visit@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c"
integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"

View File

@@ -15,7 +15,8 @@
],
"activationEvents": [
"workspaceContains:.vscode/foam.json",
"onCommand:foam-vscode.update-wikilinks"
"onCommand:foam-vscode.update-wikilinks",
"onCommand:foam-vscode.open-daily-note"
],
"main": "./out/extension.js",
"contributes": {
@@ -23,6 +24,49 @@
{
"command": "foam-vscode.update-wikilinks",
"title": "Foam: Update Markdown Reference List"
},
{
"command": "foam-vscode.open-daily-note",
"title": "Foam: Open Daily Note"
}
],
"configuration": {
"title": "Foam",
"properties": {
"foam.openDailyNote.fileExtension": {
"type": "string",
"scope": "resource",
"default": "md"
},
"foam.openDailyNote.filenameFormat": {
"type": "string",
"default": "isoDate",
"markdownDescription": "Specifies how the daily note filename is formatted. See the [dateformat docs](https://www.npmjs.com/package/dateformat) for valid formats",
"scope": "resource"
},
"foam.openDailyNote.titleFormat": {
"type": [
"string",
"null"
],
"default": null,
"markdownDescription": "Specifies how the daily note title is formatted. Will default to the filename format if set to null. See the [dateformat docs](https://www.npmjs.com/package/dateformat) for valid formats",
"scope": "resource"
},
"foam.openDailyNote.directory": {
"type": [
"string",
"null"
],
"default": null,
"description": "The directory into which daily notes should be created. Defaults to the workspace root."
}
}
},
"keybindings": [
{
"command": "foam-vscode.open-daily-note",
"key": "alt+d"
}
]
},
@@ -37,6 +81,7 @@
"publish-extension": "npx vsce publish && yarn npm-cleanup"
},
"devDependencies": {
"@types/dateformat": "^3.0.1",
"@types/glob": "^7.1.1",
"@types/node": "^13.11.0",
"@types/vscode": "^1.45.1",
@@ -49,6 +94,7 @@
"vscode-test": "^1.3.0"
},
"dependencies": {
"dateformat": "^3.0.3",
"foam-core": "^0.2.0"
}
}

View File

@@ -5,16 +5,16 @@
"use strict";
import * as fs from "fs";
import { workspace, ExtensionContext } from "vscode";
import { workspace, ExtensionContext, window, EndOfLine } from "vscode";
import { createNoteFromMarkdown, createFoam, FoamConfig } from "foam-core";
import { features } from "./features";
export function activate(context: ExtensionContext) {
const foamPromise = bootstrap(getConfig())
const foamPromise = bootstrap(getConfig());
features.forEach(f => {
f.activate(context, foamPromise);
})
});
}
const bootstrap = async (config: FoamConfig) => {
@@ -26,7 +26,8 @@ const bootstrap = async (config: FoamConfig) => {
.map(f => {
return fs.promises.readFile(f.fsPath).then(data => {
const markdown = (data || "").toString();
foam.notes.setNote(createNoteFromMarkdown(f.fsPath, markdown));
const eol = window.activeTextEditor?.document?.eol === EndOfLine.CRLF ? "\r\n" : "\n";
foam.notes.setNote(createNoteFromMarkdown(f.fsPath, markdown, eol));
});
})
);
@@ -34,8 +35,5 @@ const bootstrap = async (config: FoamConfig) => {
};
const getConfig = () => {
return {}
}
return {};
};

View File

@@ -1,7 +1,5 @@
import createReferences from "./wikilink-reference-generation";
import openDailyNote from "./open-daily-note";
import { FoamFeature } from "../types";
import createReferences from './wikilink-reference-generation'
import { FoamFeature } from '../types'
export const features: FoamFeature[] = [
createReferences
]
export const features: FoamFeature[] = [createReferences, openDailyNote];

View File

@@ -0,0 +1,96 @@
import {
window,
workspace,
Uri,
WorkspaceConfiguration,
ExtensionContext,
commands,
} from "vscode";
import { dirname, join } from "path";
import dateFormat = require("dateformat");
import fs = require("fs");
import { FoamFeature } from "../types";
const feature: FoamFeature = {
activate: async (context: ExtensionContext) => {
context.subscriptions.push(
commands.registerCommand("foam-vscode.open-daily-note", openDailyNote)
);
},
};
async function openDailyNote() {
const foamConfiguration = workspace.getConfiguration("foam");
const currentDate = new Date();
const dailyNotePath = getDailyNotePath(foamConfiguration, currentDate);
createDailyNoteIfNotExists(foamConfiguration, dailyNotePath, currentDate);
await focusDailyNote(dailyNotePath);
}
function getDailyNotePath(configuration: WorkspaceConfiguration, date: Date) {
const rootDirectory = workspace.workspaceFolders[0].uri.fsPath;
const dailyNoteDirectory: string =
configuration.get("openDailyNote.directory") ?? ".";
const dailyNoteFilename = getDailyNoteFileName(configuration, date);
return join(rootDirectory, dailyNoteDirectory, dailyNoteFilename);
}
function getDailyNoteFileName(
configuration: WorkspaceConfiguration,
date: Date
): string {
const filenameFormat: string = configuration.get(
"openDailyNote.filenameFormat"
);
const fileExtension: string = configuration.get(
"openDailyNote.fileExtension"
);
return `${dateFormat(date, filenameFormat, false)}.${fileExtension}`;
}
async function createDailyNoteIfNotExists(
configuration: WorkspaceConfiguration,
dailyNotePath: string,
currentDate: Date
) {
if (await pathExists(dailyNotePath)) {
return;
}
createDailyNoteDirectoryIfNotExists(dailyNotePath);
const titleFormat: string =
configuration.get("openDailyNote.titleFormat") ??
configuration.get("openDailyNote.filenameFormat");
await fs.promises.writeFile(
dailyNotePath,
`# ${dateFormat(currentDate, titleFormat, false)}\r\n`
);
}
async function createDailyNoteDirectoryIfNotExists(dailyNotePath: string) {
const dailyNoteDirectory = dirname(dailyNotePath);
if (!(await pathExists(dailyNoteDirectory))) {
await fs.promises.mkdir(dailyNoteDirectory, { recursive: true });
}
}
async function focusDailyNote(dailyNotePath: string) {
const document = await workspace.openTextDocument(Uri.parse(dailyNotePath));
window.showTextDocument(document);
}
async function pathExists(path: string) {
return fs.promises
.access(path, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
}
export default feature;

View File

@@ -13,23 +13,36 @@ import {
Position
} from "vscode";
import { createMarkdownReferences, createNoteFromMarkdown, NoteGraph, Foam } from "foam-core";
import {
createMarkdownReferences,
stringifyMarkdownLinkReferenceDefinition,
createNoteFromMarkdown,
NoteGraph,
Foam
} from "foam-core";
import { basename } from "path";
import { hasEmptyTrailing, docConfig, loadDocConfig, isMdEditor, mdDocSelector, getText, dropExtension } from "../utils";
import {
hasEmptyTrailing,
docConfig,
loadDocConfig,
isMdEditor,
mdDocSelector,
getText,
dropExtension
} from "../utils";
import { FoamFeature } from "../types";
const feature: FoamFeature = {
activate: async (context: ExtensionContext, foamPromise: Promise<Foam>) => {
activate: async (context: ExtensionContext, foamPromise: Promise<Foam>) => {
const foam = await foamPromise;
context.subscriptions.push(
commands.registerCommand(
"foam-vscode.update-wikilinks",
() => updateReferenceList(foam.notes)
commands.registerCommand("foam-vscode.update-wikilinks", () =>
updateReferenceList(foam.notes)
),
workspace.onWillSaveTextDocument(e => {
if (e.document.languageId === "markdown") {
foam.notes.setNote(
createNoteFromMarkdown(e.document.fileName, e.document.getText())
createNoteFromMarkdown(e.document.fileName, e.document.getText(), docConfig.eol)
);
e.waitUntil(updateReferenceList(foam.notes));
}
@@ -42,7 +55,6 @@ const feature: FoamFeature = {
}
};
const REFERENCE_HEADER = `[//begin]: # "Autogenerated link references for markdown compatibility"`;
const REFERENCE_FOOTER = `[//end]: # "Autogenerated link references"`;
@@ -54,7 +66,7 @@ async function createReferenceList(foam: NoteGraph) {
let refs = await generateReferenceList(foam, editor.document);
if (refs && refs.length) {
await editor.edit(function(editBuilder) {
await editor.edit(function (editBuilder) {
if (editor) {
const spacing = hasEmptyTrailing
? docConfig.eol
@@ -97,14 +109,17 @@ async function updateReferenceList(foam: NoteGraph) {
}
}
async function generateReferenceList(foam: NoteGraph, doc: TextDocument): Promise<string[]> {
async function generateReferenceList(
foam: NoteGraph,
doc: TextDocument
): Promise<string[]> {
const filePath = doc.fileName;
const id = dropExtension(basename(filePath));
const references = uniq(
createMarkdownReferences(foam, id).map(
link => `[${link.linkText}]: ${link.wikiLink} "${link.pageTitle}"`
stringifyMarkdownLinkReferenceDefinition
)
);
@@ -146,10 +161,10 @@ function detectReferenceListRange(doc: TextDocument): Range {
}
class WikilinkReferenceCodeLensProvider implements CodeLensProvider {
private foam: NoteGraph
private foam: NoteGraph;
constructor(foam: NoteGraph) {
this.foam = foam
this.foam = foam;
}
public provideCodeLenses(

View File

@@ -7,7 +7,7 @@
# Foam
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-19-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-25-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
**Foam** is a personal knowledge management and sharing system inspired by [Roam Research](https://roamresearch.com/), built on [Visual Studio Code](https://code.visualstudio.com/) and [GitHub](https://github.com/).
@@ -58,29 +58,37 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4?s=60" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4" width="60px;" alt=""/><br /><sub><b>Jani Eväkallio</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jevakallio" title="Documentation">📖</a></td>
<td align="center"><a href="https://joeprevite.com/"><img src="https://avatars3.githubusercontent.com/u/3806031?v=4" width="60px;" alt=""/><br /><sub><b>Joe Previte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jsjoeio" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/riccardoferretti"><img src="https://avatars3.githubusercontent.com/u/457005?v=4" width="60px;" alt=""/><br /><sub><b>Riccardo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=riccardoferretti" title="Documentation">📖</a></td>
<td align="center"><a href="http://ojanaho.com/"><img src="https://avatars0.githubusercontent.com/u/2180090?v=4" width="60px;" alt=""/><br /><sub><b>Janne Ojanaho</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jojanaho" title="Documentation">📖</a></td>
<td align="center"><a href="http://bypaulshen.com/"><img src="https://avatars3.githubusercontent.com/u/2266187?v=4" width="60px;" alt=""/><br /><sub><b>Paul Shen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=paulshen" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/coffenbacher"><img src="https://avatars0.githubusercontent.com/u/245867?v=4" width="60px;" alt=""/><br /><sub><b>coffenbacher</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=coffenbacher" title="Documentation">📖</a></td>
<td align="center"><a href="https://mathieu.dutour.me/"><img src="https://avatars2.githubusercontent.com/u/3254314?v=4" width="60px;" alt=""/><br /><sub><b>Mathieu Dutour</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=mathieudutour" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4?s=60" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/presidentelect"><img src="https://avatars2.githubusercontent.com/u/1242300?v=4" width="60px;" alt=""/><br /><sub><b>Michael Hansen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=presidentelect" title="Documentation">📖</a></td>
<td align="center"><a href="http://klickverbot.at/"><img src="https://avatars1.githubusercontent.com/u/19335?v=4" width="60px;" alt=""/><br /><sub><b>David Nadlinger</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dnadlinger" title="Documentation">📖</a></td>
<td align="center"><a href="https://pluckd.co/"><img src="https://avatars2.githubusercontent.com/u/20598571?v=4" width="60px;" alt=""/><br /><sub><b>Fernando</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MrCordeiro" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/jfgonzalez7"><img src="https://avatars3.githubusercontent.com/u/58857736?v=4" width="60px;" alt=""/><br /><sub><b>Juan Gonzalez</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jfgonzalez7" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.louiechristie.com/"><img src="https://avatars1.githubusercontent.com/u/6807448?v=4" width="60px;" alt=""/><br /><sub><b>Louie Christie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=louiechristie" title="Documentation">📖</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4" width="60px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SuperSandro2000" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Skn0tt"><img src="https://avatars1.githubusercontent.com/u/14912729?v=4" width="60px;" alt=""/><br /><sub><b>Simon Knott</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Skn0tt" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
<td align="center"><a href="https://styfle.dev/"><img src="https://avatars1.githubusercontent.com/u/229881?v=4" width="60px;" alt=""/><br /><sub><b>Steven</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=styfle" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Georift"><img src="https://avatars2.githubusercontent.com/u/859430?v=4" width="60px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Georift" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sauravkhdoolia"><img src="https://avatars1.githubusercontent.com/u/34188267?v=4" width="60px;" alt=""/><br /><sub><b>Saurav Khdoolia</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sauravkhdoolia" title="Documentation">📖</a></td>
<td align="center"><a href="https://anku.netlify.com/"><img src="https://avatars1.githubusercontent.com/u/22813027?v=4" width="60px;" alt=""/><br /><sub><b>Ankit Tiwari</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anku255" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/ayushbaweja"><img src="https://avatars1.githubusercontent.com/u/44344063?v=4" width="60px;" alt=""/><br /><sub><b>Ayush Baweja</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ayushbaweja" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/TaiChi-IO"><img src="https://avatars3.githubusercontent.com/u/65092992?v=4" width="60px;" alt=""/><br /><sub><b>TaiChi-IO</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=TaiChi-IO" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/juanfrank77"><img src="https://avatars1.githubusercontent.com/u/12146882?v=4" width="60px;" alt=""/><br /><sub><b>Juan F Gonzalez </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=juanfrank77" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://sanketdg.github.io"><img src="https://avatars3.githubusercontent.com/u/8980971?v=4" width="60px;" alt=""/><br /><sub><b>Sanket Dasgupta</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4" width="60px;" alt=""/><br /><sub><b>Nicholas Stafie</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nstafie" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/francishamel"><img src="https://avatars3.githubusercontent.com/u/36383308?v=4" width="60px;" alt=""/><br /><sub><b>Francis Hamel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=francishamel" title="Code">💻</a></td>
<td align="center"><a href="http://digiguru.co.uk"><img src="https://avatars1.githubusercontent.com/u/619436?v=4" width="60px;" alt=""/><br /><sub><b>digiguru</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=digiguru" title="Documentation">📖</a></td>
</tr>
</table>

1552
yarn.lock

File diff suppressed because it is too large Load Diff