Compare commits

...

28 Commits

Author SHA1 Message Date
Riccardo Ferretti
18a2d76139 v0.20.1 2022-10-13 18:56:04 +02:00
Riccardo Ferretti
722bef4257 Preparation for next release 2022-10-13 18:53:16 +02:00
Riccardo
8fe869223d Improved support for globs in multiroot workspace (#1083)
* Added path util to resolve absolute path from multiple base directories
* Better support for multiple roots in include/ignore globs
* URI.asAbsolutePath to use path utils
* Lint
2022-10-13 18:49:07 +02:00
allcontributors[bot]
1a961aac70 add Dominic-DallOsto as a contributor for code (#1082)
* update docs/index.md [skip ci]

* update readme.md [skip ci]

* update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-10-12 17:36:27 +02:00
Dominic D
81dacef2fe Clicking the placeholder tooltip asks for template for new note (#1061)
* Add a hover tooltip to placeholder links offering to create a new note from template
* Clicking the placeholder tooltip asks for template for new note
* Create new note from template using `CREATE_NOTE` command
* Update tests
2022-10-12 17:35:34 +02:00
Carlo Bonamico
92932cd004 Update recommended-extensions.md (#1079)
Referenced Excalidraw support
2022-10-10 15:28:09 +02:00
Riccardo Ferretti
1f6f8fd720 fix #1073 - improved support for daily note folder in multiroot workspaces 2022-09-30 22:00:36 +02:00
Riccardo Ferretti
507699924f v0.20.0 2022-09-30 19:49:56 +02:00
Riccardo Ferretti
5d1fb2593b Preparation for next release 2022-09-30 19:49:31 +02:00
Riccardo Ferretti
6012cc1b64 Updated docs for creating new notes 2022-09-30 19:43:56 +02:00
Riccardo Ferretti
36f1b8af9e Code lint 2022-09-30 19:32:05 +02:00
Riccardo
6ac9f6d229 Added "create note" command (#1076)
* Added create-note command and refactored some template code

* Added tests

* Added uri resolution logic across multiple folders

* Deprecating create-note-from-default-template command, it is now replaced by the new and more flexible create-note command (which still has the same defaults)
2022-09-30 19:14:24 +02:00
Riccardo Ferretti
3ce4529232 Removed '+' as date snippet trigger 2022-09-27 11:47:18 +02:00
Riccardo Ferretti
d3b8e66b78 Improved attachment support (fixes #915)
- can now configure list of extensions to be treated as attachments
- improved extension matching
2022-09-26 19:11:22 +02:00
Riccardo Ferretti
836623257c Fixed issue when starting Foam without open workspace (fixes #908) 2022-09-26 18:45:50 +02:00
Riccardo Ferretti
27f2bc4050 Support for opening non-text files via link (fixes #915) 2022-09-26 18:45:02 +02:00
Riccardo Ferretti
5a5f3b1ef0 dataviz: simple clicking open target note 2022-09-15 18:08:08 +02:00
Riccardo Ferretti
d374c51e93 fix #1070: also open images/attachments from graph 2022-09-15 18:07:47 +02:00
Riccardo Ferretti
f3a242251b Updated spell checking documentation (#1068) 2022-09-13 12:18:35 +02:00
Riccardo Ferretti
3d6f98351f v0.19.5 2022-09-01 13:11:11 +02:00
Riccardo Ferretti
970c30f08c Preparation for next release 2022-09-01 13:10:41 +02:00
Riccardo Ferretti
8556982b17 #1056: by default don't show images and attachments in graph view 2022-09-01 13:06:20 +02:00
Matthias Thym
1ab9520c5c Fix link to tag explorer in readme (#1060) 2022-09-01 12:34:38 +02:00
Riccardo
2a72bde111 Use note title in link completion (#1059)
* added setting for completion label

* added alias support

* Disable extra commit characters when automatically adding an alias

* added tests
2022-08-20 00:29:08 +02:00
Riccardo Ferretti
2385bd75b5 Keep extension for attachments when generating references 2022-08-19 16:43:49 +02:00
Riccardo
53d2e7aaed refactor: moved panel modules in own directory (#1055) 2022-08-18 17:18:03 +02:00
allcontributors[bot]
48c13ef400 docs: add dmurph as a contributor for code (#1054)
* 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>
2022-08-17 23:47:32 +02:00
Riccardo
3ab30547eb Add FOAM_DATE_WEEK variable (#1053)
* Update variable-resolver.ts to include week number
* Added tests

Co-authored-by: Daniel Murphy <danielmurphy161@gmail.com>
2022-08-17 23:46:14 +02:00
56 changed files with 1595 additions and 751 deletions

View File

@@ -932,6 +932,24 @@
"contributions": [
"doc"
]
},
{
"login": "dmurph",
"name": "Daniel Murphy",
"avatar_url": "https://avatars.githubusercontent.com/u/294026?v=4",
"profile": "http://www.dmurph.com",
"contributions": [
"code"
]
},
{
"login": "Dominic-DallOsto",
"name": "Dominic D",
"avatar_url": "https://avatars.githubusercontent.com/u/26859884?v=4",
"profile": "https://github.com/Dominic-DallOsto",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -106,137 +106,141 @@ If that sounds like something you're interested in, I'd love to have you along o
<!-- prettier-ignore-start -->
<!-- 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>
</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>
</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> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Code">💻</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://github.com/TaiChi-IO"><img src="https://avatars3.githubusercontent.com/u/65092992?v=4?s=60" 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?s=60" 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?s=60" 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> <a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4?s=60" 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?s=60" 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?s=60" 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>
<td align="center"><a href="https://github.com/chirag-singhal"><img src="https://avatars3.githubusercontent.com/u/42653703?v=4?s=60" width="60px;" alt=""/><br /><sub><b>CHIRAG SINGHAL</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chirag-singhal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lostintangent"><img src="https://avatars3.githubusercontent.com/u/116461?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonathan Carter</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lostintangent" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.synesthesia.co.uk"><img src="https://avatars3.githubusercontent.com/u/181399?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Julian Elve</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=synesthesia" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/thomaskoppelaar"><img src="https://avatars3.githubusercontent.com/u/36331365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Thomas Koppelaar</b></sub></a><br /><a href="#question-thomaskoppelaar" title="Answering Questions">💬</a> <a href="https://github.com/foambubble/foam/commits?author=thomaskoppelaar" title="Code">💻</a> <a href="#userTesting-thomaskoppelaar" title="User Testing">📓</a></td>
<td align="center"><a href="http://www.akshaymehra.com"><img src="https://avatars1.githubusercontent.com/u/8671497?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Akshay</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MehraAkshay" title="Code">💻</a></td>
<td align="center"><a href="http://johnlindquist.com"><img src="https://avatars0.githubusercontent.com/u/36073?v=4?s=60" width="60px;" alt=""/><br /><sub><b>John Lindquist</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=johnlindquist" title="Documentation">📖</a></td>
<td align="center"><a href="https://ashwin.run/"><img src="https://avatars2.githubusercontent.com/u/1689183?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ashwin Ramaswami</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=epicfaace" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Klaudioz"><img src="https://avatars1.githubusercontent.com/u/632625?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Claudio Canales</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Klaudioz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vitaly-pevgonen"><img src="https://avatars0.githubusercontent.com/u/6272738?v=4?s=60" width="60px;" alt=""/><br /><sub><b>vitaly-pevgonen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vitaly-pevgonen" title="Documentation">📖</a></td>
<td align="center"><a href="https://dshemetov.github.io"><img src="https://avatars0.githubusercontent.com/u/1810426?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Dmitry Shemetov</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dshemetov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hooncp"><img src="https://avatars1.githubusercontent.com/u/48883554?v=4?s=60" width="60px;" alt=""/><br /><sub><b>hooncp</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hooncp" title="Documentation">📖</a></td>
<td align="center"><a href="http://rt-canada.ca"><img src="https://avatars1.githubusercontent.com/u/13721239?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Martin Laws</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=martinlaws" title="Documentation">📖</a></td>
<td align="center"><a href="http://seanksmith.me"><img src="https://avatars3.githubusercontent.com/u/2085441?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sean K Smith</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sksmith" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/kevin-neely/"><img src="https://avatars1.githubusercontent.com/u/37545028?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Kevin Neely</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=kneely" title="Documentation">📖</a></td>
<td align="center"><a href="https://ariefrahmansyah.dev"><img src="https://avatars3.githubusercontent.com/u/8122852?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Arief Rahmansyah</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ariefrahmansyah" title="Documentation">📖</a></td>
<td align="center"><a href="http://vhanda.in"><img src="https://avatars2.githubusercontent.com/u/426467?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Vishesh Handa</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vHanda" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.linkedin.com/in/heroichitesh"><img src="https://avatars3.githubusercontent.com/u/37622734?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Hitesh Kumar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=HeroicHitesh" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://spencerwoo.com"><img src="https://avatars2.githubusercontent.com/u/32114380?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Spencer Woo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=spencerwooo" title="Documentation">📖</a></td>
<td align="center"><a href="https://ingalless.com"><img src="https://avatars3.githubusercontent.com/u/22981941?v=4?s=60" width="60px;" alt=""/><br /><sub><b>ingalless</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Documentation">📖</a></td>
<td align="center"><a href="http://jmg-duarte.github.io"><img src="https://avatars2.githubusercontent.com/u/15343819?v=4?s=60" width="60px;" alt=""/><br /><sub><b>José Duarte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.yenly.wtf"><img src="https://avatars1.githubusercontent.com/u/6759658?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Yenly</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=yenly" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.hikerpig.cn"><img src="https://avatars1.githubusercontent.com/u/2259688?v=4?s=60" width="60px;" alt=""/><br /><sub><b>hikerpig</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hikerpig" title="Code">💻</a></td>
<td align="center"><a href="http://sigfried.org"><img src="https://avatars1.githubusercontent.com/u/1586931?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sigfried Gold</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Sigfried" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.tristansokol.com"><img src="https://avatars3.githubusercontent.com/u/867661?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tristan Sokol</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tristansokol" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://umbrellait.com"><img src="https://avatars0.githubusercontent.com/u/49779373?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Danil Rodin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=umbrellait-danil-rodin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/scottjoewilliams/"><img src="https://avatars1.githubusercontent.com/u/2026866?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Scott Williams</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=scott-joe" title="Documentation">📖</a></td>
<td align="center"><a href="https://jackiexiao.github.io/blog"><img src="https://avatars2.githubusercontent.com/u/18050469?v=4?s=60" width="60px;" alt=""/><br /><sub><b>jackiexiao</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Jackiexiao" title="Documentation">📖</a></td>
<td align="center"><a href="https://generativist.substack.com/"><img src="https://avatars3.githubusercontent.com/u/78835?v=4?s=60" width="60px;" alt=""/><br /><sub><b>John B Nelson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jbn" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/asifm"><img src="https://avatars2.githubusercontent.com/u/3958387?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Asif Mehedi</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=asifm" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/litanlitudan"><img src="https://avatars2.githubusercontent.com/u/4970420?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tan Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=litanlitudan" title="Code">💻</a></td>
<td align="center"><a href="http://shaunagordon.com"><img src="https://avatars1.githubusercontent.com/u/579361?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Shauna Gordon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ShaunaGordon" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://mcluck.tech"><img src="https://avatars1.githubusercontent.com/u/1753801?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mike Cluck</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MCluck90" title="Code">💻</a></td>
<td align="center"><a href="http://brandonpugh.com"><img src="https://avatars1.githubusercontent.com/u/684781?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brandon Pugh</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bpugh" title="Code">💻</a></td>
<td align="center"><a href="https://max.davitt.me"><img src="https://avatars1.githubusercontent.com/u/27709025?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Max Davitt</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=themaxdavitt" title="Documentation">📖</a></td>
<td align="center"><a href="http://briananglin.me"><img src="https://avatars3.githubusercontent.com/u/2637602?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brian Anglin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anglinb" title="Documentation">📖</a></td>
<td align="center"><a href="http://deft.work"><img src="https://avatars1.githubusercontent.com/u/1455507?v=4?s=60" width="60px;" alt=""/><br /><sub><b>elswork</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=elswork" title="Documentation">📖</a></td>
<td align="center"><a href="http://leonh.fr/"><img src="https://avatars.githubusercontent.com/u/19996318?v=4?s=60" width="60px;" alt=""/><br /><sub><b>léon h</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=leonhfr" title="Code">💻</a></td>
<td align="center"><a href="https://nygaard.site"><img src="https://avatars.githubusercontent.com/u/4606342?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Nikhil Nygaard</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=njnygaard" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.nitwit.se"><img src="https://avatars.githubusercontent.com/u/1382124?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mark Dixon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nitwit-se" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/joeltjames"><img src="https://avatars.githubusercontent.com/u/3732400?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joel James</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joeltjames" title="Code">💻</a></td>
<td align="center"><a href="https://www.ryo33.com"><img src="https://avatars.githubusercontent.com/u/8780513?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Hashiguchi Ryo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ryo33" title="Documentation">📖</a></td>
<td align="center"><a href="https://movermeyer.com"><img src="https://avatars.githubusercontent.com/u/1459385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Overmeyer</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=movermeyer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/derrickqin"><img src="https://avatars.githubusercontent.com/u/3038111?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Derrick Qin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=derrickqin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/zomars/"><img src="https://avatars.githubusercontent.com/u/3504472?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Omar López</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=zomars" title="Documentation">📖</a></td>
<td align="center"><a href="http://robincn.com"><img src="https://avatars.githubusercontent.com/u/1583193?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Robin King</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=RobinKing" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://twitter.com/deegovee"><img src="https://avatars.githubusercontent.com/u/4730170?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Dheepak </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dheepakg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bronson"><img src="https://avatars.githubusercontent.com/u/1776?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Scott Bronson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bronson" title="Documentation">📖</a></td>
<td align="center"><a href="http://rafaelriedel.de"><img src="https://avatars.githubusercontent.com/u/41793?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Rafael Riedel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=rafo" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Pearcekieser"><img src="https://avatars.githubusercontent.com/u/5055971?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Pearcekieser</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Pearcekieser" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/theowenyoung"><img src="https://avatars.githubusercontent.com/u/62473795?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Owen Young</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=theowenyoung" title="Documentation">📖</a> <a href="#content-theowenyoung" title="Content">🖋</a></td>
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt=""/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt=""/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iam-yan"><img src="https://avatars.githubusercontent.com/u/48427014?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Yan</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=iam-yan" title="Documentation">📖</a></td>
<td align="center"><a href="https://WikiEducator.org/User:JimTittsler"><img src="https://avatars.githubusercontent.com/u/180326?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jim Tittsler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimt" title="Documentation">📖</a></td>
<td align="center"><a href="http://malcolmmielle.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/4457840?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Malcolm Mielle</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MalcolmMielle" title="Documentation">📖</a></td>
<td align="center"><a href="https://snippets.page/"><img src="https://avatars.githubusercontent.com/u/74916913?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Veesar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=veesar" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bentongxyz"><img src="https://avatars.githubusercontent.com/u/60358804?v=4?s=60" width="60px;" alt=""/><br /><sub><b>bentongxyz</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bentongxyz" title="Code">💻</a></td>
<td align="center"><a href="https://brianjdevries.com"><img src="https://avatars.githubusercontent.com/u/42778030?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brian DeVries</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=techCarpenter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
<td align="center"><a href="http://www.drewtyler.com"><img src="https://avatars.githubusercontent.com/u/5640816?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Drew Tyler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=drewtyler" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Lauviah0622"><img src="https://avatars.githubusercontent.com/u/43416399?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Lauviah0622</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Lauviah0622" title="Code">💻</a></td>
<td align="center"><a href="https://www.elastic.co/elastic-agent"><img src="https://avatars.githubusercontent.com/u/1813008?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Josh Dover</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joshdover" title="Code">💻</a></td>
<td align="center"><a href="http://phelm.co.uk"><img src="https://avatars.githubusercontent.com/u/4057948?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Phil Helm</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=phelma" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/lingyv-li"><img src="https://avatars.githubusercontent.com/u/8937944?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Larry Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lingyv-li" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/infogulch"><img src="https://avatars.githubusercontent.com/u/133882?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Taber</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=infogulch" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.readingsnail.pe.kr"><img src="https://avatars.githubusercontent.com/u/1904967?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Woosuk Park</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=readingsnail" title="Documentation">📖</a></td>
</tr>
<tbody>
<tr>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt="Jani Eväkallio"/><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="Joe Previte"/><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="Riccardo"/><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="Janne Ojanaho"/><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="Paul Shen"/><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="coffenbacher"/><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="Mathieu Dutour"/><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="Michael Hansen"/><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="David Nadlinger"/><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="Fernando"/><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="Juan Gonzalez"/><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="Louie Christie"/><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="Sandro"/><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="Simon Knott"/><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="Steven"/><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="Tim"/><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="Saurav Khdoolia"/><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="Ankit Tiwari"/><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> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Code">💻</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="Ayush Baweja"/><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?s=60" width="60px;" alt="TaiChi-IO"/><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?s=60" width="60px;" alt="Juan F Gonzalez "/><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?s=60" width="60px;" alt="Sanket Dasgupta"/><br /><sub><b>Sanket Dasgupta</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4?s=60" width="60px;" alt="Nicholas Stafie"/><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?s=60" width="60px;" alt="Francis Hamel"/><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?s=60" width="60px;" alt="digiguru"/><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>
<td align="center"><a href="https://github.com/chirag-singhal"><img src="https://avatars3.githubusercontent.com/u/42653703?v=4?s=60" width="60px;" alt="CHIRAG SINGHAL"/><br /><sub><b>CHIRAG SINGHAL</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chirag-singhal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lostintangent"><img src="https://avatars3.githubusercontent.com/u/116461?v=4?s=60" width="60px;" alt="Jonathan Carter"/><br /><sub><b>Jonathan Carter</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lostintangent" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.synesthesia.co.uk"><img src="https://avatars3.githubusercontent.com/u/181399?v=4?s=60" width="60px;" alt="Julian Elve"/><br /><sub><b>Julian Elve</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=synesthesia" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/thomaskoppelaar"><img src="https://avatars3.githubusercontent.com/u/36331365?v=4?s=60" width="60px;" alt="Thomas Koppelaar"/><br /><sub><b>Thomas Koppelaar</b></sub></a><br /><a href="#question-thomaskoppelaar" title="Answering Questions">💬</a> <a href="https://github.com/foambubble/foam/commits?author=thomaskoppelaar" title="Code">💻</a> <a href="#userTesting-thomaskoppelaar" title="User Testing">📓</a></td>
<td align="center"><a href="http://www.akshaymehra.com"><img src="https://avatars1.githubusercontent.com/u/8671497?v=4?s=60" width="60px;" alt="Akshay"/><br /><sub><b>Akshay</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MehraAkshay" title="Code">💻</a></td>
<td align="center"><a href="http://johnlindquist.com"><img src="https://avatars0.githubusercontent.com/u/36073?v=4?s=60" width="60px;" alt="John Lindquist"/><br /><sub><b>John Lindquist</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=johnlindquist" title="Documentation">📖</a></td>
<td align="center"><a href="https://ashwin.run/"><img src="https://avatars2.githubusercontent.com/u/1689183?v=4?s=60" width="60px;" alt="Ashwin Ramaswami"/><br /><sub><b>Ashwin Ramaswami</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=epicfaace" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Klaudioz"><img src="https://avatars1.githubusercontent.com/u/632625?v=4?s=60" width="60px;" alt="Claudio Canales"/><br /><sub><b>Claudio Canales</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Klaudioz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vitaly-pevgonen"><img src="https://avatars0.githubusercontent.com/u/6272738?v=4?s=60" width="60px;" alt="vitaly-pevgonen"/><br /><sub><b>vitaly-pevgonen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vitaly-pevgonen" title="Documentation">📖</a></td>
<td align="center"><a href="https://dshemetov.github.io"><img src="https://avatars0.githubusercontent.com/u/1810426?v=4?s=60" width="60px;" alt="Dmitry Shemetov"/><br /><sub><b>Dmitry Shemetov</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dshemetov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hooncp"><img src="https://avatars1.githubusercontent.com/u/48883554?v=4?s=60" width="60px;" alt="hooncp"/><br /><sub><b>hooncp</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hooncp" title="Documentation">📖</a></td>
<td align="center"><a href="http://rt-canada.ca"><img src="https://avatars1.githubusercontent.com/u/13721239?v=4?s=60" width="60px;" alt="Martin Laws"/><br /><sub><b>Martin Laws</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=martinlaws" title="Documentation">📖</a></td>
<td align="center"><a href="http://seanksmith.me"><img src="https://avatars3.githubusercontent.com/u/2085441?v=4?s=60" width="60px;" alt="Sean K Smith"/><br /><sub><b>Sean K Smith</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sksmith" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/kevin-neely/"><img src="https://avatars1.githubusercontent.com/u/37545028?v=4?s=60" width="60px;" alt="Kevin Neely"/><br /><sub><b>Kevin Neely</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=kneely" title="Documentation">📖</a></td>
<td align="center"><a href="https://ariefrahmansyah.dev"><img src="https://avatars3.githubusercontent.com/u/8122852?v=4?s=60" width="60px;" alt="Arief Rahmansyah"/><br /><sub><b>Arief Rahmansyah</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ariefrahmansyah" title="Documentation">📖</a></td>
<td align="center"><a href="http://vhanda.in"><img src="https://avatars2.githubusercontent.com/u/426467?v=4?s=60" width="60px;" alt="Vishesh Handa"/><br /><sub><b>Vishesh Handa</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vHanda" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.linkedin.com/in/heroichitesh"><img src="https://avatars3.githubusercontent.com/u/37622734?v=4?s=60" width="60px;" alt="Hitesh Kumar"/><br /><sub><b>Hitesh Kumar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=HeroicHitesh" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://spencerwoo.com"><img src="https://avatars2.githubusercontent.com/u/32114380?v=4?s=60" width="60px;" alt="Spencer Woo"/><br /><sub><b>Spencer Woo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=spencerwooo" title="Documentation">📖</a></td>
<td align="center"><a href="https://ingalless.com"><img src="https://avatars3.githubusercontent.com/u/22981941?v=4?s=60" width="60px;" alt="ingalless"/><br /><sub><b>ingalless</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Documentation">📖</a></td>
<td align="center"><a href="http://jmg-duarte.github.io"><img src="https://avatars2.githubusercontent.com/u/15343819?v=4?s=60" width="60px;" alt="José Duarte"/><br /><sub><b>José Duarte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.yenly.wtf"><img src="https://avatars1.githubusercontent.com/u/6759658?v=4?s=60" width="60px;" alt="Yenly"/><br /><sub><b>Yenly</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=yenly" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.hikerpig.cn"><img src="https://avatars1.githubusercontent.com/u/2259688?v=4?s=60" width="60px;" alt="hikerpig"/><br /><sub><b>hikerpig</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hikerpig" title="Code">💻</a></td>
<td align="center"><a href="http://sigfried.org"><img src="https://avatars1.githubusercontent.com/u/1586931?v=4?s=60" width="60px;" alt="Sigfried Gold"/><br /><sub><b>Sigfried Gold</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Sigfried" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.tristansokol.com"><img src="https://avatars3.githubusercontent.com/u/867661?v=4?s=60" width="60px;" alt="Tristan Sokol"/><br /><sub><b>Tristan Sokol</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tristansokol" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://umbrellait.com"><img src="https://avatars0.githubusercontent.com/u/49779373?v=4?s=60" width="60px;" alt="Danil Rodin"/><br /><sub><b>Danil Rodin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=umbrellait-danil-rodin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/scottjoewilliams/"><img src="https://avatars1.githubusercontent.com/u/2026866?v=4?s=60" width="60px;" alt="Scott Williams"/><br /><sub><b>Scott Williams</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=scott-joe" title="Documentation">📖</a></td>
<td align="center"><a href="https://jackiexiao.github.io/blog"><img src="https://avatars2.githubusercontent.com/u/18050469?v=4?s=60" width="60px;" alt="jackiexiao"/><br /><sub><b>jackiexiao</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Jackiexiao" title="Documentation">📖</a></td>
<td align="center"><a href="https://generativist.substack.com/"><img src="https://avatars3.githubusercontent.com/u/78835?v=4?s=60" width="60px;" alt="John B Nelson"/><br /><sub><b>John B Nelson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jbn" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/asifm"><img src="https://avatars2.githubusercontent.com/u/3958387?v=4?s=60" width="60px;" alt="Asif Mehedi"/><br /><sub><b>Asif Mehedi</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=asifm" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/litanlitudan"><img src="https://avatars2.githubusercontent.com/u/4970420?v=4?s=60" width="60px;" alt="Tan Li"/><br /><sub><b>Tan Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=litanlitudan" title="Code">💻</a></td>
<td align="center"><a href="http://shaunagordon.com"><img src="https://avatars1.githubusercontent.com/u/579361?v=4?s=60" width="60px;" alt="Shauna Gordon"/><br /><sub><b>Shauna Gordon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ShaunaGordon" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://mcluck.tech"><img src="https://avatars1.githubusercontent.com/u/1753801?v=4?s=60" width="60px;" alt="Mike Cluck"/><br /><sub><b>Mike Cluck</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MCluck90" title="Code">💻</a></td>
<td align="center"><a href="http://brandonpugh.com"><img src="https://avatars1.githubusercontent.com/u/684781?v=4?s=60" width="60px;" alt="Brandon Pugh"/><br /><sub><b>Brandon Pugh</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bpugh" title="Code">💻</a></td>
<td align="center"><a href="https://max.davitt.me"><img src="https://avatars1.githubusercontent.com/u/27709025?v=4?s=60" width="60px;" alt="Max Davitt"/><br /><sub><b>Max Davitt</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=themaxdavitt" title="Documentation">📖</a></td>
<td align="center"><a href="http://briananglin.me"><img src="https://avatars3.githubusercontent.com/u/2637602?v=4?s=60" width="60px;" alt="Brian Anglin"/><br /><sub><b>Brian Anglin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anglinb" title="Documentation">📖</a></td>
<td align="center"><a href="http://deft.work"><img src="https://avatars1.githubusercontent.com/u/1455507?v=4?s=60" width="60px;" alt="elswork"/><br /><sub><b>elswork</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=elswork" title="Documentation">📖</a></td>
<td align="center"><a href="http://leonh.fr/"><img src="https://avatars.githubusercontent.com/u/19996318?v=4?s=60" width="60px;" alt="léon h"/><br /><sub><b>léon h</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=leonhfr" title="Code">💻</a></td>
<td align="center"><a href="https://nygaard.site"><img src="https://avatars.githubusercontent.com/u/4606342?v=4?s=60" width="60px;" alt="Nikhil Nygaard"/><br /><sub><b>Nikhil Nygaard</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=njnygaard" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.nitwit.se"><img src="https://avatars.githubusercontent.com/u/1382124?v=4?s=60" width="60px;" alt="Mark Dixon"/><br /><sub><b>Mark Dixon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nitwit-se" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/joeltjames"><img src="https://avatars.githubusercontent.com/u/3732400?v=4?s=60" width="60px;" alt="Joel James"/><br /><sub><b>Joel James</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joeltjames" title="Code">💻</a></td>
<td align="center"><a href="https://www.ryo33.com"><img src="https://avatars.githubusercontent.com/u/8780513?v=4?s=60" width="60px;" alt="Hashiguchi Ryo"/><br /><sub><b>Hashiguchi Ryo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ryo33" title="Documentation">📖</a></td>
<td align="center"><a href="https://movermeyer.com"><img src="https://avatars.githubusercontent.com/u/1459385?v=4?s=60" width="60px;" alt="Michael Overmeyer"/><br /><sub><b>Michael Overmeyer</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=movermeyer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/derrickqin"><img src="https://avatars.githubusercontent.com/u/3038111?v=4?s=60" width="60px;" alt="Derrick Qin"/><br /><sub><b>Derrick Qin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=derrickqin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/zomars/"><img src="https://avatars.githubusercontent.com/u/3504472?v=4?s=60" width="60px;" alt="Omar López"/><br /><sub><b>Omar López</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=zomars" title="Documentation">📖</a></td>
<td align="center"><a href="http://robincn.com"><img src="https://avatars.githubusercontent.com/u/1583193?v=4?s=60" width="60px;" alt="Robin King"/><br /><sub><b>Robin King</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=RobinKing" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://twitter.com/deegovee"><img src="https://avatars.githubusercontent.com/u/4730170?v=4?s=60" width="60px;" alt="Dheepak "/><br /><sub><b>Dheepak </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dheepakg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt="Daniel VG"/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt="Barabas"/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt="Engincan VESKE"/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt="Paul de Raaij"/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bronson"><img src="https://avatars.githubusercontent.com/u/1776?v=4?s=60" width="60px;" alt="Scott Bronson"/><br /><sub><b>Scott Bronson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bronson" title="Documentation">📖</a></td>
<td align="center"><a href="http://rafaelriedel.de"><img src="https://avatars.githubusercontent.com/u/41793?v=4?s=60" width="60px;" alt="Rafael Riedel"/><br /><sub><b>Rafael Riedel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=rafo" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Pearcekieser"><img src="https://avatars.githubusercontent.com/u/5055971?v=4?s=60" width="60px;" alt="Pearcekieser"/><br /><sub><b>Pearcekieser</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Pearcekieser" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/theowenyoung"><img src="https://avatars.githubusercontent.com/u/62473795?v=4?s=60" width="60px;" alt="Owen Young"/><br /><sub><b>Owen Young</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=theowenyoung" title="Documentation">📖</a> <a href="#content-theowenyoung" title="Content">🖋</a></td>
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt="Prashanth Subrahmanyam"/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt="Jonas SPRENGER"/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt="Paul"/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt="Ikko Ashimine"/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt="memeplex"/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt="AndreiD049"/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iam-yan"><img src="https://avatars.githubusercontent.com/u/48427014?v=4?s=60" width="60px;" alt="Yan"/><br /><sub><b>Yan</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=iam-yan" title="Documentation">📖</a></td>
<td align="center"><a href="https://WikiEducator.org/User:JimTittsler"><img src="https://avatars.githubusercontent.com/u/180326?v=4?s=60" width="60px;" alt="Jim Tittsler"/><br /><sub><b>Jim Tittsler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimt" title="Documentation">📖</a></td>
<td align="center"><a href="http://malcolmmielle.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/4457840?v=4?s=60" width="60px;" alt="Malcolm Mielle"/><br /><sub><b>Malcolm Mielle</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MalcolmMielle" title="Documentation">📖</a></td>
<td align="center"><a href="https://snippets.page/"><img src="https://avatars.githubusercontent.com/u/74916913?v=4?s=60" width="60px;" alt="Veesar"/><br /><sub><b>Veesar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=veesar" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bentongxyz"><img src="https://avatars.githubusercontent.com/u/60358804?v=4?s=60" width="60px;" alt="bentongxyz"/><br /><sub><b>bentongxyz</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bentongxyz" title="Code">💻</a></td>
<td align="center"><a href="https://brianjdevries.com"><img src="https://avatars.githubusercontent.com/u/42778030?v=4?s=60" width="60px;" alt="Brian DeVries"/><br /><sub><b>Brian DeVries</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=techCarpenter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt="Clifford Fajardo "/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt="Chris Usick"/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt="Joe DeCock"/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
<td align="center"><a href="http://www.drewtyler.com"><img src="https://avatars.githubusercontent.com/u/5640816?v=4?s=60" width="60px;" alt="Drew Tyler"/><br /><sub><b>Drew Tyler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=drewtyler" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Lauviah0622"><img src="https://avatars.githubusercontent.com/u/43416399?v=4?s=60" width="60px;" alt="Lauviah0622"/><br /><sub><b>Lauviah0622</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Lauviah0622" title="Code">💻</a></td>
<td align="center"><a href="https://www.elastic.co/elastic-agent"><img src="https://avatars.githubusercontent.com/u/1813008?v=4?s=60" width="60px;" alt="Josh Dover"/><br /><sub><b>Josh Dover</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joshdover" title="Code">💻</a></td>
<td align="center"><a href="http://phelm.co.uk"><img src="https://avatars.githubusercontent.com/u/4057948?v=4?s=60" width="60px;" alt="Phil Helm"/><br /><sub><b>Phil Helm</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=phelma" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/lingyv-li"><img src="https://avatars.githubusercontent.com/u/8937944?v=4?s=60" width="60px;" alt="Larry Li"/><br /><sub><b>Larry Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lingyv-li" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/infogulch"><img src="https://avatars.githubusercontent.com/u/133882?v=4?s=60" width="60px;" alt="Joe Taber"/><br /><sub><b>Joe Taber</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=infogulch" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.readingsnail.pe.kr"><img src="https://avatars.githubusercontent.com/u/1904967?v=4?s=60" width="60px;" alt="Woosuk Park"/><br /><sub><b>Woosuk Park</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=readingsnail" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.dmurph.com"><img src="https://avatars.githubusercontent.com/u/294026?v=4?s=60" width="60px;" alt="Daniel Murphy"/><br /><sub><b>Daniel Murphy</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dmurph" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Dominic-DallOsto"><img src="https://avatars.githubusercontent.com/u/26859884?v=4?s=60" width="60px;" alt="Dominic D"/><br /><sub><b>Dominic D</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Dominic-DallOsto" title="Code">💻</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->

View File

@@ -0,0 +1,45 @@
# Foam Commands
Foam has various commands that you can explore by calling the command palette and typing "Foam".
In particular, some commands can be very customizible and can help with custom workflows and use cases.
## foam-vscode.create-note command
This command creates a note.
Although it works fine on its own, it can be customized to achieve various use cases.
Here are the settings available for the command:
- notePath: The path of the note to create. If relative it will be resolved against the workspace root.
- templatePath: The path of the template to use. If relative it will be resolved against the workspace root.
- text: The text to use for the note. If also a template is provided, the template has precedence
- variables: Variables to use in the text or template (e.g. `FOAM_TITLE`)
- date: The date used to resolve the FOAM_DATE_* variables. in `YYYY-MM-DD` format
- onFileExists?: 'overwrite' | 'open' | 'ask' | 'cancel': What to do in case the target file already exists
To customize a command and associate a key binding to it, open the key binding settings and add the appropriate configuration, here are some examples:
- Create a note called `test note.md` with some text. If the note already exists, ask for a new name
```
{
"key": "alt+f",
"command": "foam-vscode.create-note",
"args": {
"text": "test note ${FOAM_DATE_YEAR}",
"notePath": "test note.md",
"onFileExists": "ask"
}
}
```
- Create a note following the `weekly-note.md` template. If the note already exists, open it
```
{
"key": "alt+g",
"command": "foam-vscode.create-note",
"args": {
"templatePath": ".foam/templates/weekly-note.md",
"onFileExists": "open"
}
}
```

View File

@@ -1,15 +1,9 @@
# Spell Checking
Foam comes with a spell checker powered by the [Spellright extension](https://marketplace.visualstudio.com/items?itemName=ban.spellright).
There are many spell checking extensions for VS Code.
Misspelled words are highlighted, like hellow.
You can place the cursor on top of the word, and press `cmd+.` for suggestions on how to fix the problem.
The most popular spell checker for VS Code is [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker).
You can configure the extension in the settings, for example to:
Another one of our favorites is [LTeX](https://marketplace.visualstudio.com/items?itemName=valentjn.vscode-ltex&ssr=false#overview), which is a bit heavier but offers some extra functionality.
- ignore certain files
- change the language(s)
- and much more
You can use any number of alternative spell checking extensions for VS Code.
One of our favorites is [LTeX](https://marketplace.visualstudio.com/items?itemName=valentjn.vscode-ltex&ssr=false#overview), which is a bit heavier but offers some extra functionality.
Another popular one is [Spellright](https://marketplace.visualstudio.com/items?itemName=ban.spellright), but be mindful that there have been reports of incompatibility with the `vscode-markdown` extension (see https://github.com/foambubble/foam/issues/1068).

View File

@@ -2,12 +2,13 @@
- Write out a new `[[wikilink]]` and `Cmd` + `Click` to create a new file and enter it.
- For keyboard navigation, use the 'Follow Definition' key `F12` (or [remap the 'editor.action.revealDefinition' key binding](https://code.visualstudio.com/docs/getstarted/keybindings) to something more ergonomic)
- `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), execute `Foam: Create New Note` and enter a **Title Case Name** to create `Title Case Name.md`
- Add a keyboard binding to make creating new notes easier.
- `Cmd` + `Shift` + `P` (`Ctrl` + `Shift` + `P` for Windows), execute `Foam: Create Note` and enter a **Title Case Name** to create `Title Case Name.md`
- Add a keyboard binding to make creating new notes easier. See [[commands]] for more info on this.
- The [[note-templates]] used by this command can be customized.
- You shouldn't worry too much about categorizing your notes. You can always [[search-for-notes]], and explore them using the [[graph-visualization]].
[//begin]: # "Autogenerated link references for markdown compatibility"
[commands]: ../features/commands.md "Foam Commands"
[note-templates]: ../features/note-templates.md "Note Templates"
[search-for-notes]: ../recipes/search-for-notes.md "Search for Notes"
[graph-visualization]: ../features/graph-visualization.md "Graph Visualization"

View File

@@ -7,7 +7,6 @@ This list is subject to change.
- [Foam for VSCode](https://marketplace.visualstudio.com/items?itemName=foam.foam-vscode) (alpha)
- [Markdown All In One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)
- [Paste Image](https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image)
- [Spell Right](https://marketplace.visualstudio.com/items?itemName=ban.spellright)
## Extensions For Additional Features
@@ -17,6 +16,7 @@ These extensions are not (yet?) defined in `.vscode/extensions.json`, but have b
- [Markdown Emoji](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-emoji) (adds `:smile:` syntax, works with emojisense to provide autocomplete for this syntax)
- [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
- [Mermaid Markdown Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=bpruitt-goddard.mermaid-markdown-syntax-highlighting)
- [Excalidraw whiteboard and sketching tool integration](https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor)
- [VSCode PDF Viewing](https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf)
- [Project Manager](https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager) (to quickly switch between projects)
- [Markdown Extended](https://marketplace.visualstudio.com/items?itemName=jebbs.markdown-extended) (with `kbd` option disabled, `kbd` turns wikilinks into non-clickable buttons)

View File

@@ -4,5 +4,5 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.19.4"
"version": "0.20.1"
}

View File

@@ -4,6 +4,34 @@ All notable changes to the "foam-vscode" extension will be documented in this fi
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [0.20.1] - 2022-10-13
Fixes and Improvements:
- Improved support for daily notes in multi root workspace (#1073)
- Create note from placeholder using template (#1061 - thanks @Dominic-DallOsto)
- Improved support for globs in multi root workspace (#1083)
## [0.20.0] - 2022-09-30
New Features:
- Added `foam-vscode.create-note` command, which can be very customized for several use cases (#1076)
Fixes and Improvements:
- Removed `+` as a trigger char for date snippets
- Improved attachment support (#915)
- Improved error handling when starting Foam without an open workspace (#908)
- Added support for opening non-text files via wikilink (#915)
- Dataviz: now clicking is enough to open a link from the graph
- Dataviz: clicking on images/attachments will open them
## [0.19.5] - 2022-09-01
Fixes and Improvements:
- Added `FOAM_DATE_WEEK` variable (#1053 - Thanks @dmurph)
- Fixed extension inclusion when generating references for attachments
- Link completion label can be note title as well as path (#1059)
- Images and attachments are not shown by default in graph view (#1056)
## [0.19.4] - 2022-08-07
Fixes and Improvements:

View File

@@ -8,7 +8,7 @@
"type": "git"
},
"homepage": "https://github.com/foambubble/foam",
"version": "0.19.4",
"version": "0.20.1",
"license": "MIT",
"publisher": "foam",
"engines": {
@@ -29,6 +29,7 @@
"onCommand:foam-vscode.copy-without-brackets",
"onCommand:foam-vscode.show-graph",
"onCommand:foam-vscode.create-new-template",
"onCommand:foam-vscode.create-note",
"onCommand:foam-vscode.create-note-from-template",
"onCommand:foam-vscode.create-note-from-default-template"
],
@@ -128,6 +129,10 @@
}
],
"commandPalette": [
{
"command": "foam-vscode.create-note-from-default-template",
"when": "false"
},
{
"command": "foam-vscode.update-graph",
"when": "false"
@@ -159,6 +164,10 @@
]
},
"commands": [
{
"command": "foam-vscode.create-note",
"title": "Foam: Create Note"
},
{
"command": "foam-vscode.clear-cache",
"title": "Foam: Clear Cache"
@@ -201,7 +210,7 @@
},
{
"command": "foam-vscode.create-note-from-template",
"title": "Foam: Create New Note From Template"
"title": "Foam: Create Note From Template"
},
{
"command": "foam-vscode.create-note-from-default-template",
@@ -243,6 +252,34 @@
"configuration": {
"title": "Foam",
"properties": {
"foam.completion.label": {
"type": "string",
"default": "path",
"description": "Describes what note property to use as a label for completion items",
"enum": [
"path",
"title",
"identifier"
],
"enumDescriptions": [
"Use the path of the note",
"Use the title of the note",
"Use the identifier of the note"
]
},
"foam.completion.useAlias": {
"type": "string",
"default": "never",
"description": "Specifies in which cases to use an alias when creating a wikilink",
"enum": [
"never",
"whenPathDiffersFromTitle"
],
"enumDescriptions": [
"Never use aliases in completion items",
"Use alias if resource path is different from title"
]
},
"foam.files.ignore": {
"type": [
"array"
@@ -255,6 +292,11 @@
],
"description": "Specifies the list of globs that will be ignored by Foam (e.g. they will not be considered when creating the graph). To ignore the all the content of a given folder, use `<folderName>/**/*`"
},
"foam.files.attachmentExtensions": {
"type": "string",
"default": "pdf mp3 webm wav m4a mp4 avi mov rtf txt doc docx pages xls xlsx numbers ppt pptm pptx",
"description": "Space separated list of file extensions that will be considered attachments"
},
"foam.logging.level": {
"type": "string",
"default": "info",

View File

@@ -1,5 +1,5 @@
import { Logger } from '../utils/log';
import { URI } from './uri';
import { asAbsoluteUri, URI } from './uri';
Logger.setLevel('error');
@@ -81,3 +81,47 @@ describe('Foam URI', () => {
).toEqual(URI.file('../a/note.md'));
});
});
describe('asAbsoluteUri', () => {
it('should throw if no workspace folder is found', () => {
expect(() => asAbsoluteUri(URI.file('relative/path'), [])).toThrow();
});
it('should return the given URI if already absolute', () => {
const uri = URI.file('/absolute/path');
expect(asAbsoluteUri(uri, [URI.file('/base')])).toEqual(uri);
});
describe('with relative URI', () => {
it('should return a URI relative if the given URI is relative and there is only one workspace folder', () => {
const uri = URI.file('relative/path');
const workspaceFolder = URI.file('/workspace/folder');
expect(asAbsoluteUri(uri, [workspaceFolder])).toEqual(
workspaceFolder.joinPath(uri.path)
);
});
it('should match the first folder with the same name as the first part of the URI', () => {
const uri = URI.file('folder2/file');
const workspaceFolder1 = URI.file('/absolute/path/folder1');
const workspaceFolder2 = URI.file('/absolute/path/folder2');
expect(asAbsoluteUri(uri, [workspaceFolder1, workspaceFolder2])).toEqual(
workspaceFolder2.joinPath('file')
);
});
});
it('should use the first folder if no matching folder is found', () => {
const uri = URI.file('folder3/file');
const workspaceFolder1 = URI.file('/absolute/path/folder1');
const workspaceFolder2 = URI.file('/absolute/path/folder2');
expect(asAbsoluteUri(uri, [workspaceFolder1, workspaceFolder2])).toEqual(
workspaceFolder1.joinPath(uri.path)
);
});
it('should use the first matching folder', () => {
const uri = URI.file('folder/file');
const workspaceFolder1 = URI.file('/absolute/path1');
const workspaceFolder2 = URI.file('/absolute/path2/folder');
const workspaceFolder3 = URI.file('/absolute/path3/folder');
expect(
asAbsoluteUri(uri, [workspaceFolder1, workspaceFolder2, workspaceFolder3])
).toEqual(workspaceFolder2.joinPath('file'));
});
});

View File

@@ -367,3 +367,24 @@ function encodeURIComponentMinimal(path: string): string {
}
return res !== undefined ? res : path;
}
/**
* Turns a relative URI into an absolute URI given a collection of base folders.
* In case of multiple matches it returns the first one.
*
* @see {@link pathUtils.asAbsolutePaths|path.asAbsolutePath}
*
* @param uri the uri to evaluate
* @param baseFolders the base folders to use
* @returns an absolute uri
*
* TODO this probably needs to be moved to the workspace service
*/
export function asAbsoluteUri(uri: URI, baseFolders: URI[]): URI {
return URI.file(
pathUtils.asAbsolutePaths(
uri.path,
baseFolders.map(f => f.path)
)[0]
);
}

View File

@@ -5,9 +5,17 @@ import { FoamWorkspace } from '../model/workspace';
import { IDataStore, IMatcher, IWatcher } from '../services/datastore';
import { IDisposable } from '../common/lifecycle';
import { ResourceProvider } from '../model/provider';
import { getFoamVsCodeConfig } from '../../services/config';
const imageExtensions = ['.png', '.jpg', '.gif'];
const attachmentExtensions = ['.pdf', ...imageExtensions];
const attachmentExtConfig = getFoamVsCodeConfig(
'files.attachmentExtensions',
''
)
.split(' ')
.map(ext => '.' + ext.trim());
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp'];
const attachmentExtensions = [...attachmentExtConfig, ...imageExtensions];
const asResource = (uri: URI): Resource => {
const type = imageExtensions.includes(uri.getExtension())
@@ -45,6 +53,11 @@ export class AttachmentResourceProvider implements ResourceProvider {
.match(filesByFolder.flat())
.filter(this.supports);
Logger.info(
`Found ${
files.length
} attachments, with extensions: ${attachmentExtensions.join(', ')}`
);
for (const uri of files) {
Logger.debug('Found: ' + uri.toString());
workspace.set(asResource(uri));
@@ -70,7 +83,9 @@ export class AttachmentResourceProvider implements ResourceProvider {
}
supports(uri: URI) {
return attachmentExtensions.includes(uri.getExtension());
return attachmentExtensions.includes(
uri.getExtension().toLocaleLowerCase()
);
}
async readAsMarkdown(uri: URI): Promise<string | null> {

View File

@@ -62,11 +62,11 @@ describe('Matcher', () => {
});
it('happy path', () => {
const matcher = new Matcher([URI.file('/')], ['**/*'], ['**/*.pdf']);
expect(matcher.isMatch(URI.file('/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/file.pdf'))).toBeFalsy();
expect(matcher.isMatch(URI.file('/dir/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/dir/file.pdf'))).toBeFalsy();
const matcher = new Matcher([URI.file('/root/')], ['**/*'], ['**/*.pdf']);
expect(matcher.isMatch(URI.file('/root/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/root/file.pdf'))).toBeFalsy();
expect(matcher.isMatch(URI.file('/root/dir/file.md'))).toBeTruthy();
expect(matcher.isMatch(URI.file('/root/dir/file.pdf'))).toBeFalsy();
});
it('ignores files in the exclude list', () => {

View File

@@ -5,6 +5,7 @@ import { glob } from 'glob';
import { promisify } from 'util';
import { isWindows } from '../common/platform';
import { Event } from '../common/event';
import { asAbsolutePaths } from '../utils/path';
const findAllFiles = promisify(glob);
@@ -54,21 +55,19 @@ export class Matcher implements IMatcher {
constructor(
baseFolders: URI[],
include: string[] = ['**/*'],
exclude: string[] = []
includeGlobs: string[] = ['**/*'],
excludeGlobs: string[] = []
) {
this.folders = baseFolders.map(toMatcherPathFormat);
Logger.info('Workspace folders: ', this.folders);
this.folders.forEach(folder => {
const withFolder = folderPlusGlob(folder);
this.include.push(
...include.map(glob => {
return withFolder(glob);
})
);
this.exclude.push(...exclude.map(withFolder));
});
this.include = includeGlobs.flatMap(glob =>
asAbsolutePaths(glob, this.folders)
);
this.exclude = excludeGlobs.flatMap(glob =>
asAbsolutePaths(glob, this.folders)
);
Logger.info('Glob patterns', {
includeGlobs: this.include,
ignoreGlobs: this.exclude,
@@ -142,13 +141,3 @@ export class FileDataStore implements IDataStore {
}
}
}
export const folderPlusGlob = (folder: string) => (glob: string): string => {
if (folder.substr(-1) === '/') {
folder = folder.slice(0, -1);
}
if (glob.startsWith('/')) {
glob = glob.slice(1);
}
return folder.length > 0 ? `${folder}/${glob}` : glob;
};

View File

@@ -301,6 +301,21 @@ describe('Generation of markdown references', () => {
expect(references.map(r => r.url)).toEqual(['page-b.md', 'page-c.md']);
});
it('should always add extensions for attachments, even when includeExtension = false', () => {
const workspace = createTestWorkspace();
const noteA = createNoteFromMarkdown(
'Link to [[page-b]] and [[image.png]]',
'/dir1/page-a.md'
);
workspace
.set(noteA)
.set(createNoteFromMarkdown('Content of note B', '/dir1/page-b.md'))
.set(createNoteFromMarkdown('', '/dir1/image.png'));
const references = createMarkdownReferences(workspace, noteA.uri, false);
expect(references.map(r => r.url)).toEqual(['page-b', 'image.png']);
});
it('should use relative paths', () => {
const workspace = createTestWorkspace();
const noteA = createNoteFromMarkdown(

View File

@@ -181,7 +181,7 @@ to generate markdown reference list`
}
let relativeUri = target.uri.relativeTo(noteUri.getDirectory());
if (!includeExtension) {
if (!includeExtension && relativeUri.path.endsWith('.md')) {
relativeUri = relativeUri.changeExtension('*', '');
}

View File

@@ -0,0 +1,30 @@
import { asAbsolutePaths } from './path';
describe('path utils', () => {
describe('asAbsolutePaths', () => {
it('returns the path if already absolute', () => {
const paths = asAbsolutePaths('/path/to/test', [
'/root/Users',
'/root/tmp',
]);
expect(paths).toEqual(['/path/to/test']);
});
it('returns the matching base if found', () => {
const paths = asAbsolutePaths('tmp/to/test', [
'/root/Users',
'/root/tmp',
]);
expect(paths).toEqual(['/root/tmp/to/test']);
});
it('returns all bases if no match is found', () => {
const paths = asAbsolutePaths('path/to/test', [
'/root/Users',
'/root/tmp',
]);
expect(paths).toEqual([
'/root/Users/path/to/test',
'/root/tmp/path/to/test',
]);
});
});
});

View File

@@ -1,5 +1,6 @@
import { CharCode } from '../common/charCode';
import { posix } from 'path';
import { isNone } from './core';
/**
* Converts filesystem path to POSIX path. Supported inputs are:
@@ -174,3 +175,46 @@ function parseUNCShare(uncPath: string): [string, string] {
return [uncPath.substring(2, idx), uncPath.substring(idx) || '\\'];
}
}
/**
* Turns a relative path into an absolute path given a collection of base folders.
* - if no base folder is provided, it will throw
* - if the given path is already absolute, it will return it
* - if the given path is relative it will return absolute paths for the ones matching the
* first part of the path
* - if no matching base folder is found, it will return an absolute path per base folder
* @param path the path to evaluate
* @param baseFolders the base folders to use
* @returns an array of absolute path, guaranteed to have at least 1 element
*/
export function asAbsolutePaths(path: string, baseFolders: string[]): string[] {
if (isNone(baseFolders) || baseFolders.length === 0) {
throw new Error('Cannot compute absolute URI without a base');
}
if (isAbsolute(path)) {
return [path];
}
let tokens = path.split('/');
const firstDir = tokens[0];
const res = [];
if (baseFolders.length > 1) {
for (const folder of baseFolders) {
const lastDir = folder.split('/').pop();
if (lastDir === firstDir) {
tokens = tokens.slice(1);
res.push([folder, ...tokens].join('/'));
continue;
}
}
}
if (res.length === 0) {
for (const folder of baseFolders) {
const match = folder.endsWith('/')
? folder.substring(0, folder.length - 1)
: folder;
res.push([match, ...tokens].join('/'));
}
}
return res;
}

View File

@@ -2,9 +2,10 @@ import { workspace } from 'vscode';
import dateFormat from 'dateformat';
import { focusNote } from './utils';
import { URI } from './core/model/uri';
import { fromVsCodeUri, toVsCodeUri } from './utils/vsc-utils';
import { toVsCodeUri } from './utils/vsc-utils';
import { NoteFactory } from './services/templates';
import { getFoamVsCodeConfig } from './services/config';
import { asAbsoluteWorkspaceUri } from './services/editor';
/**
* Open the daily note file.
@@ -42,17 +43,9 @@ export async function openDailyNoteFor(date?: Date) {
*/
export function getDailyNotePath(date: Date): URI {
const folder = getFoamVsCodeConfig<string>('openDailyNote.directory') ?? '.';
const dailyNoteDirectory = URI.file(folder);
const dailyNoteDirectory = asAbsoluteWorkspaceUri(URI.file(folder));
const dailyNoteFilename = getDailyNoteFileName(date);
if (dailyNoteDirectory.isAbsolute()) {
return dailyNoteDirectory.joinPath(dailyNoteFilename);
} else {
return fromVsCodeUri(workspace.workspaceFolders[0].uri).joinPath(
dailyNoteDirectory.path,
dailyNoteFilename
);
}
return dailyNoteDirectory.joinPath(dailyNoteFilename);
}
/**

View File

@@ -22,6 +22,11 @@ export async function activate(context: ExtensionContext) {
try {
Logger.info('Starting Foam');
if (workspace.workspaceFolders === undefined) {
Logger.info('No workspace open. Foam will not start');
return;
}
// Prepare Foam
const readFile = async (uri: URI) =>
(await workspace.fs.readFile(toVsCodeUri(uri))).toString();

View File

@@ -1,18 +1,26 @@
import { commands, ExtensionContext } from 'vscode';
import { commands, window, ExtensionContext } from 'vscode';
import { FoamFeature } from '../../types';
import { DEFAULT_TEMPLATE_URI, NoteFactory } from '../../services/templates';
import { getDefaultTemplateUri, NoteFactory } from '../../services/templates';
import { Resolver } from '../../services/variable-resolver';
/**
* Create a new note from the default template.
*
* @deprecated use 'foam-vscode.create-note' instead
*/
const feature: FoamFeature = {
activate: (context: ExtensionContext) => {
context.subscriptions.push(
commands.registerCommand(
'foam-vscode.create-note-from-default-template',
() => {
window.showWarningMessage(
"This command is deprecated, use 'Foam: Create Note' (foam-vscode.create-note) instead"
);
const resolver = new Resolver(new Map(), new Date());
return NoteFactory.createFromTemplate(
DEFAULT_TEMPLATE_URI,
getDefaultTemplateUri(),
resolver,
undefined,
`---

View File

@@ -1,11 +1,6 @@
import { commands, ExtensionContext, QuickPickItem, window } from 'vscode';
import { commands, ExtensionContext } from 'vscode';
import { FoamFeature } from '../../types';
import {
getTemplateMetadata,
getTemplates,
NoteFactory,
TEMPLATES_DIR,
} from '../../services/templates';
import { askUserForTemplate, NoteFactory } from '../../services/templates';
import { Resolver } from '../../services/variable-resolver';
const feature: FoamFeature = {
@@ -14,102 +9,17 @@ const feature: FoamFeature = {
commands.registerCommand(
'foam-vscode.create-note-from-template',
async () => {
const selectedTemplate = await askUserForTemplate();
if (selectedTemplate === undefined) {
return;
const templateUri = await askUserForTemplate();
if (templateUri) {
const resolver = new Resolver(new Map(), new Date());
await NoteFactory.createFromTemplate(templateUri, resolver);
}
const templateFilename =
(selectedTemplate as QuickPickItem).description ||
(selectedTemplate as QuickPickItem).label;
const templateUri = TEMPLATES_DIR.joinPath(templateFilename);
const resolver = new Resolver(new Map(), new Date());
await NoteFactory.createFromTemplate(templateUri, resolver);
}
)
);
},
};
async function offerToCreateTemplate(): Promise<void> {
const response = await window.showQuickPick(['Yes', 'No'], {
placeHolder:
'No templates available. Would you like to create one instead?',
});
if (response === 'Yes') {
commands.executeCommand('foam-vscode.create-new-template');
return;
}
}
function sortTemplatesMetadata(
t1: Map<string, string>,
t2: Map<string, string>
) {
// Sort by name's existence, then name, then path
if (t1.get('name') === undefined && t2.get('name') !== undefined) {
return 1;
}
if (t1.get('name') !== undefined && t2.get('name') === undefined) {
return -1;
}
const pathSortOrder = t1
.get('templatePath')
.localeCompare(t2.get('templatePath'));
if (t1.get('name') === undefined && t2.get('name') === undefined) {
return pathSortOrder;
}
const nameSortOrder = t1.get('name').localeCompare(t2.get('name'));
return nameSortOrder || pathSortOrder;
}
async function askUserForTemplate() {
const templates = await getTemplates();
if (templates.length === 0) {
return offerToCreateTemplate();
}
const templatesMetadata = (
await Promise.all(
templates.map(async templateUri => {
const metadata = await getTemplateMetadata(templateUri);
metadata.set('templatePath', templateUri.getBasename());
return metadata;
})
)
).sort(sortTemplatesMetadata);
const items: QuickPickItem[] = await Promise.all(
templatesMetadata.map(metadata => {
const label = metadata.get('name') || metadata.get('templatePath');
const description = metadata.get('name')
? metadata.get('templatePath')
: null;
const detail = metadata.get('description');
const item = {
label: label,
description: description,
detail: detail,
};
Object.keys(item).forEach(key => {
if (!item[key]) {
delete item[key];
}
});
return item;
})
);
return await window.showQuickPick(items, {
placeHolder: 'Select a template to use.',
});
}
export default feature;

View File

@@ -0,0 +1,129 @@
import { commands, window } from 'vscode';
import { URI } from '../../core/model/uri';
import { asAbsoluteWorkspaceUri, readFile } from '../../services/editor';
import {
closeEditors,
createFile,
deleteFile,
expectSameUri,
getUriInWorkspace,
} from '../../test/test-utils-vscode';
describe('create-note command', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('uses sensible defaults to work even without params', async () => {
const spy = jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve('Test note')));
await commands.executeCommand('foam-vscode.create-note');
expect(spy).toBeCalled();
const target = asAbsoluteWorkspaceUri(URI.file('Test note.md'));
expectSameUri(target, window.activeTextEditor?.document.uri);
await deleteFile(target);
});
it('gives precedence to the template over the text', async () => {
const templateA = await createFile('Template A', [
'.foam',
'templates',
'template-for-create-note.md',
]);
const target = getUriInWorkspace();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.path,
templatePath: templateA.uri.path,
text: 'hello',
});
expect(window.activeTextEditor.document.getText()).toEqual('Template A');
expectSameUri(window.activeTextEditor.document.uri, target);
await deleteFile(target);
await deleteFile(templateA.uri);
});
it('focuses on the newly created note', async () => {
const target = getUriInWorkspace();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.path,
text: 'hello',
});
expect(window.activeTextEditor.document.getText()).toEqual('hello');
expectSameUri(window.activeTextEditor.document.uri, target);
await deleteFile(target);
});
it('supports variables', async () => {
const target = getUriInWorkspace();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.path,
text: 'hello ${FOAM_TITLE}', // eslint-disable-line no-template-curly-in-string
variables: { FOAM_TITLE: 'world' },
});
expect(window.activeTextEditor.document.getText()).toEqual('hello world');
expectSameUri(window.activeTextEditor.document.uri, target);
await deleteFile(target);
});
it('supports date variables', async () => {
const target = getUriInWorkspace();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.path,
text: 'hello ${FOAM_DATE_YEAR}', // eslint-disable-line no-template-curly-in-string
date: '2021-10-01',
});
expect(window.activeTextEditor.document.getText()).toEqual('hello 2021');
expectSameUri(window.activeTextEditor.document.uri, target);
await deleteFile(target);
});
it('supports various options to deal with existing notes', async () => {
const target = await createFile('hello');
const content = await readFile(target.uri);
expect(content).toEqual('hello');
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.uri.path,
text: 'test overwrite',
onFileExists: 'overwrite',
});
expect(window.activeTextEditor.document.getText()).toEqual(
'test overwrite'
);
expectSameUri(window.activeTextEditor.document.uri, target.uri);
await closeEditors();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.uri.path,
text: 'test open',
onFileExists: 'open',
});
expect(window.activeTextEditor.document.getText()).toEqual(
'test overwrite'
);
expectSameUri(window.activeTextEditor.document.uri, target.uri);
await closeEditors();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.uri.path,
text: 'test cancel',
onFileExists: 'cancel',
});
expect(window.activeTextEditor).toBeUndefined();
const spy = jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve(undefined)));
await closeEditors();
await commands.executeCommand('foam-vscode.create-note', {
notePath: target.uri.path,
text: 'test ask',
onFileExists: 'ask',
});
expect(spy).toBeCalled();
await deleteFile(target);
});
});

View File

@@ -0,0 +1,110 @@
import * as vscode from 'vscode';
import { FoamFeature } from '../../types';
import { URI } from '../../core/model/uri';
import {
askUserForTemplate,
getPathFromTitle,
NoteFactory,
} from '../../services/templates';
import { Foam } from '../../core/model/foam';
import { Resolver } from '../../services/variable-resolver';
import { asAbsoluteWorkspaceUri, fileExists } from '../../services/editor';
import { isSome } from '../../core/utils';
interface CreateNoteArgs {
/**
* The path of the note to create.
* If relative it will be resolved against the workspace root.
*/
notePath?: string;
/**
* The path of the template to use.
*/
templatePath?: string;
/**
* Whether to ask the user to select a template for the new note. If so, overwrites templatePath.
*/
askForTemplate?: boolean;
/**
* The text to use for the note.
* If a template is provided, the template has precedence
*/
text?: string;
/**
* Variables to use in the text or template
*/
variables?: Map<string, string>;
/**
* The date used to resolve the FOAM_DATE_* variables. in YYYY-MM-DD format
*/
date?: string;
/**
* What to do in case the target file already exists
*/
onFileExists?: 'overwrite' | 'open' | 'ask' | 'cancel';
}
const DEFAULT_NEW_NOTE_TEXT = `# \${FOAM_TITLE}
\${FOAM_SELECTED_TEXT}`;
async function createNote(args: CreateNoteArgs) {
args = args ?? {};
const date = isSome(args.date) ? new Date(Date.parse(args.date)) : new Date();
const resolver = new Resolver(
new Map(Object.entries(args.variables ?? {})),
date
);
const text = args.text ?? DEFAULT_NEW_NOTE_TEXT;
const noteUri =
args.notePath && asAbsoluteWorkspaceUri(URI.file(args.notePath));
let templateUri: URI;
if (args.askForTemplate) {
const selectedTemplate = await askUserForTemplate();
if (selectedTemplate) {
templateUri = selectedTemplate;
} else {
return;
}
} else {
templateUri =
args.templatePath && asAbsoluteWorkspaceUri(URI.file(args.templatePath));
}
if (await fileExists(templateUri)) {
return NoteFactory.createFromTemplate(
templateUri,
resolver,
noteUri,
text,
args.onFileExists
);
} else {
return NoteFactory.createNote(
noteUri ?? (await getPathFromTitle(resolver)),
text,
resolver,
args.onFileExists
);
}
}
export const CREATE_NOTE_COMMAND = {
command: 'foam-vscode.create-note',
title: 'Foam: Create Note',
asURI: (args: CreateNoteArgs) =>
vscode.Uri.parse(`command:${CREATE_NOTE_COMMAND.command}`).with({
query: encodeURIComponent(JSON.stringify(args)),
}),
};
const feature: FoamFeature = {
activate: (context: vscode.ExtensionContext, foamPromise: Promise<Foam>) => {
context.subscriptions.push(
vscode.commands.registerCommand(CREATE_NOTE_COMMAND.command, createNote)
);
},
};
export default feature;

View File

@@ -10,3 +10,4 @@ export { default as openRandomNoteCommand } from './open-random-note';
export { default as openResource } from './open-resource';
export { default as updateGraphCommand } from './update-graph';
export { default as updateWikilinksCommand } from './update-wikilinks';
export { default as createNote } from './create-note';

View File

@@ -28,13 +28,12 @@ const feature: FoamFeature = {
uri.path === vscode.window.activeTextEditor?.document.uri.path
? vscode.window.activeTextEditor?.document.uri
: toVsCodeUri(uri.asPlain());
// if the doc is already open, reuse the same colunm
const targetEditor = vscode.window.visibleTextEditors.find(
ed => targetUri.path === ed.document.uri.path
);
const column = targetEditor?.viewColumn;
return vscode.window.showTextDocument(targetUri, {
viewColumn: column,
});
return vscode.commands.executeCommand('vscode.open', targetUri);
}
case 'placeholder': {
const title = uri.getName();

View File

@@ -206,8 +206,7 @@ const feature: FoamFeature = {
languages.registerCompletionItemProvider(
'markdown',
datesCompletionProvider,
'/',
'+'
'/'
)
);
},

View File

@@ -179,7 +179,7 @@ describe('Hover provider', () => {
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(getValue(result.contents[0])).toEqual(
`This is some content from file B`
);
@@ -205,7 +205,7 @@ describe('Hover provider', () => {
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(getValue(result.contents[0])).toEqual(
`This is some content from file B`
);
@@ -235,7 +235,7 @@ The content of file B`);
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(getValue(result.contents[0])).toEqual(`The content of file B`);
ws.dispose();
graph.dispose();
@@ -255,9 +255,12 @@ The content of file B`);
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(result.contents[0]).toEqual(null);
expect(result.contents[1]).toEqual(null);
expect(getValue(result.contents[2])).toMatch(
"[Create note from template for 'wikilink'](command:foam-vscode.create-note?"
);
ws.dispose();
graph.dispose();
});
@@ -281,11 +284,12 @@ The content of file B`);
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(getValue(result.contents[0])).toEqual(`This is some content`);
expect(getValue(result.contents[1])).toMatch(
/^Also referenced in 1 note:/
);
expect(result.contents[2]).toEqual(null);
ws.dispose();
graph.dispose();
});
@@ -309,7 +313,7 @@ The content of file B`);
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(getValue(result.contents[1])).toMatch(
/^Also referenced in 1 note:/
);
@@ -333,12 +337,14 @@ The content of file B`);
const provider = new HoverProvider(hoverEnabled, ws, graph, parser);
const result = await provider.provideHover(doc, pos, noCancelToken);
expect(result.contents).toHaveLength(2);
expect(result.contents).toHaveLength(3);
expect(result.contents[0]).toEqual(null);
expect(getValue(result.contents[1])).toMatch(
/^Also referenced in 2 notes:/
);
expect(getValue(result.contents[2])).toMatch(
"[Create note from template for 'placeholder'](command:foam-vscode.create-note?"
);
ws.dispose();
graph.dispose();
});

View File

@@ -13,6 +13,7 @@ import { FoamWorkspace } from '../core/model/workspace';
import { Range } from '../core/model/range';
import { FoamGraph } from '../core/model/graph';
import { OPEN_COMMAND } from './commands/open-resource';
import { CREATE_NOTE_COMMAND } from './commands/create-note';
export const CONFIG_KEY = 'links.hover.enable';
@@ -108,8 +109,36 @@ export class HoverProvider implements vscode.HoverProvider {
: this.workspace.get(targetUri).title;
}
// If placeholder, offer to create a new note from template (compared to default link provider - not from template)
const basedir =
vscode.workspace.workspaceFolders.length > 0
? vscode.workspace.workspaceFolders[0].uri
: vscode.window.activeTextEditor?.document.uri
? vscode.window.activeTextEditor!.document.uri
: undefined;
if (basedir === undefined) {
return;
}
const target = fromVsCodeUri(basedir)
.resolve(targetUri, true)
.changeExtension('', '.md');
const args = {
text: target.getName(),
notePath: target.path,
askForTemplate: true,
};
const command = CREATE_NOTE_COMMAND.asURI(args);
const newNoteFromTemplate = new vscode.MarkdownString(
`[Create note from template for '${targetUri.getName()}'](${command})`
);
newNoteFromTemplate.isTrusted = true;
const hover: vscode.Hover = {
contents: [mdContent, sources.length > 0 ? references : null],
contents: [
mdContent,
sources.length > 0 ? references : null,
targetUri.isPlaceholder() ? newNoteFromTemplate : null,
],
range: toVsCodeRange(targetLink.range),
};
return hover;

View File

@@ -1,34 +1,26 @@
import { FoamFeature } from '../types';
import * as commands from './commands';
import dataviz from './dataviz';
import * as panels from './panels';
import dateSnippets from './date-snippets';
import tagsExplorer from './tags-tree-view';
import orphans from './orphans';
import placeholders from './placeholders';
import backlinks from './backlinks';
import hoverProvider from './hover-provider';
import previewNavigation from './preview';
import preview from './preview';
import completionProvider, { completionCursorMove } from './link-completion';
import tagCompletionProvider from './tag-completion';
import linkDecorations from './document-decorator';
import navigationProviders from './navigation-provider';
import wikilinkDiagnostics from './wikilink-diagnostics';
import refactor from './refactor';
import { FoamFeature } from '../types';
export const features: FoamFeature[] = [
...Object.values(commands),
...Object.values(panels),
refactor,
navigationProviders,
wikilinkDiagnostics,
tagsExplorer,
dataviz,
dateSnippets,
orphans,
placeholders,
backlinks,
hoverProvider,
linkDecorations,
previewNavigation,
preview,
completionProvider,
tagCompletionProvider,
completionCursorMove,

View File

@@ -7,10 +7,11 @@ import {
closeEditors,
createFile,
showInEditor,
withModifiedFoamConfiguration,
} from '../test/test-utils-vscode';
import { fromVsCodeUri } from '../utils/vsc-utils';
import {
CompletionProvider,
WikilinkCompletionProvider,
SectionCompletionProvider,
} from './link-completion';
@@ -63,7 +64,7 @@ describe('Link Completion', () => {
it('should not return any link for empty documents', async () => {
const { uri } = await createFile('');
const { doc } = await showInEditor(uri);
const provider = new CompletionProvider(ws, graph);
const provider = new WikilinkCompletionProvider(ws, graph);
const links = await provider.provideCompletionItems(
doc,
@@ -76,7 +77,7 @@ describe('Link Completion', () => {
it('should not return link outside the wikilink brackets', async () => {
const { uri } = await createFile('[[file]] then');
const { doc } = await showInEditor(uri);
const provider = new CompletionProvider(ws, graph);
const provider = new WikilinkCompletionProvider(ws, graph);
const links = await provider.provideCompletionItems(
doc,
@@ -90,7 +91,7 @@ describe('Link Completion', () => {
for (const text of ['[[', '[[file]] [[', '[[file]] #tag [[']) {
const { uri } = await createFile(text);
const { doc } = await showInEditor(uri);
const provider = new CompletionProvider(ws, graph);
const provider = new WikilinkCompletionProvider(ws, graph);
const links = await provider.provideCompletionItems(
doc,
@@ -110,6 +111,103 @@ describe('Link Completion', () => {
}
});
it('should support label setting', async () => {
const { uri: noteUri, content } = await createFile(`# My Note Title`);
const workspace = createTestWorkspace();
workspace.set(parser.parse(noteUri, content));
const provider = new WikilinkCompletionProvider(
workspace,
FoamGraph.fromWorkspace(workspace)
);
const { uri } = await createFile('[[');
const { doc } = await showInEditor(uri);
await withModifiedFoamConfiguration(
'completion.label',
'title',
async () => {
const links = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 3)
);
expect(links.items.map(i => i.label)).toEqual(['My Note Title']);
}
);
await withModifiedFoamConfiguration(
'completion.label',
'path',
async () => {
const links = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 3)
);
expect(links.items.map(i => i.label)).toEqual([noteUri.getBasename()]);
}
);
await withModifiedFoamConfiguration(
'completion.label',
'identifier',
async () => {
const links = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 3)
);
expect(links.items.map(i => i.label)).toEqual([
workspace.getIdentifier(noteUri),
]);
}
);
});
it('should support alias setting', async () => {
const { uri: noteUri, content } = await createFile(`# My Note Title`);
const workspace = createTestWorkspace();
workspace.set(parser.parse(noteUri, content));
const provider = new WikilinkCompletionProvider(
workspace,
FoamGraph.fromWorkspace(workspace)
);
const { uri } = await createFile('[[');
const { doc } = await showInEditor(uri);
await withModifiedFoamConfiguration(
'completion.useAlias',
'never',
async () => {
const links = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 3)
);
expect(links.items.map(i => i.insertText)).toEqual([
workspace.getIdentifier(noteUri),
]);
}
);
await withModifiedFoamConfiguration(
'completion.useAlias',
'whenPathDiffersFromTitle',
async () => {
const links = await provider.provideCompletionItems(
doc,
new vscode.Position(0, 3)
);
expect(links.items.map(i => i.insertText)).toEqual([
`${workspace.getIdentifier(noteUri)}|My Note Title`,
]);
}
);
});
it('should return sections for other notes', async () => {
for (const text of [
'[[file-name#',
@@ -171,7 +269,7 @@ alias: alias-a
ws.set(parser.parse(uri, content));
const { doc } = await showInEditor(uri);
const provider = new CompletionProvider(ws, graph);
const provider = new WikilinkCompletionProvider(ws, graph);
const links = await provider.provideCompletionItems(
doc,

View File

@@ -1,8 +1,10 @@
import * as vscode from 'vscode';
import { Foam } from '../core/model/foam';
import { FoamGraph } from '../core/model/graph';
import { Resource } from '../core/model/note';
import { URI } from '../core/model/uri';
import { FoamWorkspace } from '../core/model/workspace';
import { getFoamVsCodeConfig } from '../services/config';
import { FoamFeature } from '../types';
import { getNoteTooltip, mdDocSelector } from '../utils';
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
@@ -28,7 +30,7 @@ const feature: FoamFeature = {
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider(
mdDocSelector,
new CompletionProvider(foam.workspace, foam.graph),
new WikilinkCompletionProvider(foam.workspace, foam.graph),
'['
),
vscode.languages.registerCompletionItemProvider(
@@ -159,7 +161,7 @@ export class SectionCompletionProvider
}
}
export class CompletionProvider
export class WikilinkCompletionProvider
implements vscode.CompletionItemProvider<vscode.CompletionItem> {
constructor(private ws: FoamWorkspace, private graph: FoamGraph) {}
@@ -186,20 +188,45 @@ export class CompletionProvider
position.line,
position.character
);
const labelStyle = getCompletionLabelSetting();
const aliasSetting = getCompletionAliasSetting();
const resources = this.ws.list().map(resource => {
const label = vscode.workspace.asRelativePath(toVsCodeUri(resource.uri));
const resourceIsDocument =
['attachment', 'image'].indexOf(resource.type) === -1;
const identifier = this.ws.getIdentifier(resource.uri);
const label = !resourceIsDocument
? identifier
: labelStyle === 'path'
? vscode.workspace.asRelativePath(toVsCodeUri(resource.uri))
: labelStyle === 'title'
? resource.title
: identifier;
const item = new ResourceCompletionItem(
label,
vscode.CompletionItemKind.File,
resource.uri
);
item.sortText =
resource.type === 'attachment' ? `1-${item.label}` : `0-${item.label}`;
item.filterText = resource.uri.getName();
item.insertText = this.ws.getIdentifier(resource.uri);
item.detail = vscode.workspace.asRelativePath(toVsCodeUri(resource.uri));
item.sortText = resourceIsDocument
? `0-${item.label}`
: `1-${item.label}`;
const useAlias =
resourceIsDocument &&
aliasSetting !== 'never' &&
wikilinkRequiresAlias(resource);
item.insertText = useAlias
? `${identifier}|${resource.title}`
: identifier;
item.commitCharacters = useAlias ? [] : linkCommitCharacters;
item.range = replacementRange;
item.command = COMPLETION_CURSOR_MOVE;
item.commitCharacters = linkCommitCharacters;
return item;
});
const aliases = this.ws.list().flatMap(resource =>
@@ -265,4 +292,23 @@ class ResourceCompletionItem extends vscode.CompletionItem {
}
}
function getCompletionLabelSetting() {
const labelStyle: 'path' | 'title' | 'identifier' = getFoamVsCodeConfig(
'completion.label'
);
return labelStyle;
}
function getCompletionAliasSetting() {
const aliasStyle: 'never' | 'whenPathDiffersFromTitle' = getFoamVsCodeConfig(
'completion.useAlias'
);
return aliasStyle;
}
const normalize = (text: string) => text.toLocaleLowerCase().trim();
function wikilinkRequiresAlias(resource: Resource) {
return normalize(resource.uri.getName()) !== normalize(resource.title);
}
export default feature;

View File

@@ -1,17 +1,17 @@
import { workspace, window } from 'vscode';
import { createTestNote, createTestWorkspace } from '../test/test-utils';
import { createTestNote, createTestWorkspace } from '../../test/test-utils';
import {
cleanWorkspace,
closeEditors,
createNote,
getUriInWorkspace,
} from '../test/test-utils-vscode';
} from '../../test/test-utils-vscode';
import { BacklinksTreeDataProvider, BacklinkTreeItem } from './backlinks';
import { ResourceTreeItem } from '../utils/grouped-resources-tree-data-provider';
import { OPEN_COMMAND } from './commands/open-resource';
import { toVsCodeUri } from '../utils/vsc-utils';
import { FoamGraph } from '../core/model/graph';
import { URI } from '../core/model/uri';
import { ResourceTreeItem } from '../../utils/grouped-resources-tree-data-provider';
import { OPEN_COMMAND } from '../commands/open-resource';
import { toVsCodeUri } from '../../utils/vsc-utils';
import { FoamGraph } from '../../core/model/graph';
import { URI } from '../../core/model/uri';
describe('Backlinks panel', () => {
beforeAll(async () => {

View File

@@ -1,16 +1,16 @@
import * as vscode from 'vscode';
import { groupBy } from 'lodash';
import { URI } from '../core/model/uri';
import { URI } from '../../core/model/uri';
import { getNoteTooltip, isNone } from '../utils';
import { FoamFeature } from '../types';
import { ResourceTreeItem } from '../utils/grouped-resources-tree-data-provider';
import { Foam } from '../core/model/foam';
import { FoamWorkspace } from '../core/model/workspace';
import { FoamGraph } from '../core/model/graph';
import { Resource, ResourceLink } from '../core/model/note';
import { Range } from '../core/model/range';
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
import { getNoteTooltip, isNone } from '../../utils';
import { FoamFeature } from '../../types';
import { ResourceTreeItem } from '../../utils/grouped-resources-tree-data-provider';
import { Foam } from '../../core/model/foam';
import { FoamWorkspace } from '../../core/model/workspace';
import { FoamGraph } from '../../core/model/graph';
import { Resource, ResourceLink } from '../../core/model/note';
import { Range } from '../../core/model/range';
import { fromVsCodeUri, toVsCodeUri } from '../../utils/vsc-utils';
const feature: FoamFeature = {
activate: async (

View File

@@ -1,11 +1,11 @@
import * as vscode from 'vscode';
import { FoamFeature } from '../types';
import { FoamFeature } from '../../types';
import { TextDecoder } from 'util';
import { getGraphStyle, getTitleMaxLength } from '../settings';
import { isSome } from '../utils';
import { Foam } from '../core/model/foam';
import { Logger } from '../core/utils/log';
import { fromVsCodeUri } from '../utils/vsc-utils';
import { getGraphStyle, getTitleMaxLength } from '../../settings';
import { isSome } from '../../utils';
import { Foam } from '../../core/model/foam';
import { Logger } from '../../core/utils/log';
import { fromVsCodeUri } from '../../utils/vsc-utils';
const feature: FoamFeature = {
activate: (context: vscode.ExtensionContext, foamPromise: Promise<Foam>) => {
@@ -141,10 +141,11 @@ async function createGraphPanel(foam: Foam, context: vscode.ExtensionContext) {
const selectedNote = foam.workspace.get(fromVsCodeUri(noteUri));
if (isSome(selectedNote)) {
const doc = await vscode.workspace.openTextDocument(
selectedNote.uri.path // vscode doesn't recognize the URI directly
vscode.commands.executeCommand(
'vscode.open',
noteUri,
vscode.ViewColumn.One
);
vscode.window.showTextDocument(doc, vscode.ViewColumn.One);
}
break;
}

View File

@@ -0,0 +1,5 @@
export { default as backlinks } from './backlinks';
export { default as dataviz } from './dataviz';
export { default as orphans } from './orphans';
export { default as placeholders } from './placeholders';
export { default as tags } from './tags-explorer';

View File

@@ -1,5 +1,5 @@
import { FoamGraph } from '../core/model/graph';
import { createTestNote, createTestWorkspace } from '../test/test-utils';
import { FoamGraph } from '../../core/model/graph';
import { createTestNote, createTestWorkspace } from '../../test/test-utils';
import { isOrphan } from './orphans';
const orphanA = createTestNote({

View File

@@ -1,15 +1,15 @@
import * as vscode from 'vscode';
import { Foam } from '../core/model/foam';
import { FoamGraph } from '../core/model/graph';
import { URI } from '../core/model/uri';
import { getOrphansConfig } from '../settings';
import { FoamFeature } from '../types';
import { Foam } from '../../core/model/foam';
import { FoamGraph } from '../../core/model/graph';
import { URI } from '../../core/model/uri';
import { getOrphansConfig } from '../../settings';
import { FoamFeature } from '../../types';
import {
GroupedResourcesTreeDataProvider,
ResourceTreeItem,
UriTreeItem,
} from '../utils/grouped-resources-tree-data-provider';
import { fromVsCodeUri } from '../utils/vsc-utils';
} from '../../utils/grouped-resources-tree-data-provider';
import { fromVsCodeUri } from '../../utils/vsc-utils';
const feature: FoamFeature = {
activate: async (

View File

@@ -1,12 +1,12 @@
import * as vscode from 'vscode';
import { Foam } from '../core/model/foam';
import { getPlaceholdersConfig } from '../settings';
import { FoamFeature } from '../types';
import { Foam } from '../../core/model/foam';
import { getPlaceholdersConfig } from '../../settings';
import { FoamFeature } from '../../types';
import {
GroupedResourcesTreeDataProvider,
UriTreeItem,
} from '../utils/grouped-resources-tree-data-provider';
import { fromVsCodeUri } from '../utils/vsc-utils';
} from '../../utils/grouped-resources-tree-data-provider';
import { fromVsCodeUri } from '../../utils/vsc-utils';
const feature: FoamFeature = {
activate: async (

View File

@@ -1,17 +1,18 @@
import { createTestNote, readFileFromFs } from '../test/test-utils';
import { cleanWorkspace, closeEditors } from '../test/test-utils-vscode';
import { TagItem, TagReference, TagsProvider } from './tags-tree-view';
import { bootstrap, Foam } from '../core/model/foam';
import { MarkdownResourceProvider } from '../core/services/markdown-provider';
import { FileDataStore, Matcher } from '../core/services/datastore';
import { createMarkdownParser } from '../core/services/markdown-parser';
import { createTestNote, readFileFromFs } from '../../test/test-utils';
import { cleanWorkspace, closeEditors } from '../../test/test-utils-vscode';
import { TagItem, TagReference, TagsProvider } from './tags-explorer';
import { bootstrap, Foam } from '../../core/model/foam';
import { MarkdownResourceProvider } from '../../core/services/markdown-provider';
import { FileDataStore, Matcher } from '../../core/services/datastore';
import { createMarkdownParser } from '../../core/services/markdown-parser';
import { URI } from '../../core/model/uri';
describe('Tags tree panel', () => {
let _foam: Foam;
let provider: TagsProvider;
const dataStore = new FileDataStore(readFileFromFs);
const matcher = new Matcher([]);
const matcher = new Matcher([URI.file('/root')]);
const parser = createMarkdownParser();
const mdProvider = new MarkdownResourceProvider(matcher, dataStore, parser);

View File

@@ -1,11 +1,11 @@
import { URI } from '../core/model/uri';
import { URI } from '../../core/model/uri';
import * as vscode from 'vscode';
import { FoamFeature } from '../types';
import { getNoteTooltip, isSome } from '../utils';
import { toVsCodeRange, toVsCodeUri } from '../utils/vsc-utils';
import { Foam } from '../core/model/foam';
import { FoamWorkspace } from '../core/model/workspace';
import { Resource, Tag } from '../core/model/note';
import { FoamFeature } from '../../types';
import { getNoteTooltip, isSome } from '../../utils';
import { toVsCodeRange, toVsCodeUri } from '../../utils/vsc-utils';
import { Foam } from '../../core/model/foam';
import { FoamWorkspace } from '../../core/model/workspace';
import { Resource, Tag } from '../../core/model/note';
const TAG_SEPARATOR = '/';
const feature: FoamFeature = {

View File

@@ -1,10 +1,10 @@
import * as vscode from 'vscode';
import { FoamFeature } from '../../types';
import { Foam } from '../../core/model/foam';
import markdownItFoamTags from './tag-highlight';
import markdownItWikilinkNavigation from './wikilink-navigation';
import markdownItRemoveLinkReferences from './remove-wikilink-references';
import markdownItWikilinkEmbed from './wikilink-embed';
import { default as markdownItFoamTags } from './tag-highlight';
import { default as markdownItWikilinkNavigation } from './wikilink-navigation';
import { default as markdownItRemoveLinkReferences } from './remove-wikilink-references';
import { default as markdownItWikilinkEmbed } from './wikilink-embed';
const feature: FoamFeature = {
activate: async (

View File

@@ -1,6 +1,6 @@
import MarkdownIt from 'markdown-it';
import { FoamWorkspace } from '../../core/model/workspace';
import markdownItFoamTags from './tag-highlight';
import { default as markdownItFoamTags } from './tag-highlight';
describe('Stylable tag generation in preview', () => {
const md = markdownItFoamTags(MarkdownIt(), new FoamWorkspace());

View File

@@ -6,7 +6,8 @@ import {
deleteFile,
withModifiedFoamConfiguration,
} from '../../test/test-utils-vscode';
import markdownItWikilinkEmbed, {
import {
default as markdownItWikilinkEmbed,
CONFIG_EMBED_NOTE_IN_CONTAINER,
} from './wikilink-embed';

View File

@@ -1,9 +1,7 @@
import markdownItRegex from 'markdown-it-regex';
import * as vscode from 'vscode';
import { isSome } from '../../utils';
import { FoamWorkspace } from '../../core/model/workspace';
import { Logger } from '../../core/utils/log';
import { toVsCodeUri } from '../../utils/vsc-utils';
import { Resource } from '../../core/model/note';
import { getFoamVsCodeConfig } from '../../services/config';
// eslint-disable-next-line no-restricted-imports

View File

@@ -1,12 +1,9 @@
import MarkdownIt from 'markdown-it';
import { createMarkdownParser } from '../../core/services/markdown-parser';
import { FoamWorkspace } from '../../core/model/workspace';
import { createTestNote } from '../../test/test-utils';
import { getUriInWorkspace } from '../../test/test-utils-vscode';
import markdownItWikilinkNavigation from './wikilink-navigation';
import markdownItRemoveLinkReferences from './remove-wikilink-references';
const parser = createMarkdownParser();
import { default as markdownItWikilinkNavigation } from './wikilink-navigation';
import { default as markdownItRemoveLinkReferences } from './remove-wikilink-references';
describe('Link generation in preview', () => {
const noteA = createTestNote({

View File

@@ -1,18 +1,12 @@
import markdownItRegex from 'markdown-it-regex';
import * as vscode from 'vscode';
import { FoamFeature } from '../../types';
import { isNone, isSome } from '../../utils';
import { Foam } from '../../core/model/foam';
import { isNone } from '../../utils';
import { FoamWorkspace } from '../../core/model/workspace';
import { Logger } from '../../core/utils/log';
import { toVsCodeUri } from '../../utils/vsc-utils';
import { Resource } from '../../core/model/note';
import { MarkdownLink } from '../../core/services/markdown-link';
import { Range } from '../../core/model/range';
import { isEmpty } from 'lodash';
import { getFoamVsCodeConfig } from '../../services/config';
// eslint-disable-next-line no-restricted-imports
import { readFileSync } from 'fs';
export const markdownItWikilinkNavigation = (
md: markdownit,

View File

@@ -5,7 +5,12 @@ import {
createFile,
showInEditor,
} from '../test/test-utils-vscode';
import { getCurrentEditorDirectory, replaceSelection } from './editor';
import {
asAbsoluteWorkspaceUri,
getCurrentEditorDirectory,
replaceSelection,
} from './editor';
import { URI } from '../core/model/uri';
describe('Editor utils', () => {
beforeAll(closeEditors);
@@ -38,4 +43,14 @@ describe('Editor utils', () => {
expect(doc.doc.getText()).toEqual('This was the file A');
});
});
describe('asAbsoluteWorkspaceUri', () => {
it('should work with the VS Code workspace folders if none are passed', () => {
const uri = URI.file('relative/path');
const workspaceFolder = workspace.workspaceFolders[0];
expect(asAbsoluteWorkspaceUri(uri)).toEqual(
fromVsCodeUri(workspaceFolder.uri).joinPath(uri.path)
);
});
});
});

View File

@@ -1,6 +1,7 @@
import { URI } from '../core/model/uri';
import { asAbsoluteUri, URI } from '../core/model/uri';
import { TextEncoder } from 'util';
import {
FileType,
Selection,
SnippetString,
TextDocument,
@@ -85,3 +86,41 @@ export function getCurrentEditorDirectory(): URI {
throw new Error('A file must be open in editor, or workspace folder needed');
}
export async function fileExists(uri: URI): Promise<boolean> {
try {
const stat = await workspace.fs.stat(toVsCodeUri(uri));
return stat.type === FileType.File;
} catch (e) {
return false;
}
}
export async function readFile(uri: URI): Promise<string | undefined> {
if (await fileExists(uri)) {
return workspace.fs
.readFile(toVsCodeUri(uri))
.then(bytes => bytes.toString());
}
return undefined;
}
export const deleteFile = (uri: URI) => {
return workspace.fs.delete(toVsCodeUri(uri), { recursive: true });
};
/**
* Turns a relative URI into an absolute URI for the given workspace.
* @param uri the uri to evaluate
* @returns an absolute uri
*/
export function asAbsoluteWorkspaceUri(uri: URI): URI {
if (workspace.workspaceFolders === undefined) {
throw new Error('An open folder or workspace is required');
}
const folders = workspace.workspaceFolders.map(folder =>
fromVsCodeUri(folder.uri)
);
const res = asAbsoluteUri(uri, folders);
return res;
}

View File

@@ -1,7 +1,6 @@
import { Selection, ViewColumn, window, workspace } from 'vscode';
import { isWindows } from '../core/common/platform';
import { Selection, ViewColumn, window } from 'vscode';
import { fromVsCodeUri } from '../utils/vsc-utils';
import { determineNewNoteFilepath, NoteFactory } from '../services/templates';
import { NoteFactory } from '../services/templates';
import {
closeEditors,
createFile,
@@ -10,6 +9,7 @@ import {
showInEditor,
} from '../test/test-utils-vscode';
import { Resolver } from './variable-resolver';
import { fileExists } from './editor';
describe('Create note from template', () => {
beforeEach(async () => {
@@ -113,27 +113,6 @@ foam_template: # foam template metadata
});
describe('Creation with active text selection', () => {
it('should populate FOAM_SELECTED_TEXT with the current selection', async () => {
const templateA = await createFile('Template A', [
'.foam',
'templates',
'template-a.md',
]);
const file = await createFile('Content of first file');
const { editor } = await showInEditor(file.uri);
editor.selection = new Selection(0, 11, 1, 0);
const target = getUriInWorkspace();
const resolver = new Resolver(new Map(), new Date());
await NoteFactory.createFromTemplate(templateA.uri, resolver, target);
expect(await resolver.resolveFromName('FOAM_SELECTED_TEXT')).toEqual(
'first file'
);
await deleteFile(templateA);
await deleteFile(target);
await deleteFile(file);
});
it('should open created note in a new column if there was a selection', async () => {
const templateA = await createFile('Template A', [
'.foam',
@@ -183,91 +162,69 @@ foam_template: # foam template metadata
expect(window.visibleTextEditors[0].document.getText()).toEqual(
`This is my first file: [[${target.getName()}]]`
);
await deleteFile(template.uri);
});
});
});
describe('determineNewNoteFilepath', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
describe('NoteFactory.createNote', () => {
beforeEach(async () => {
await closeEditors();
});
it('should use the template path if absolute', async () => {
const winAbsolutePath = 'C:\\absolute_path\\journal\\My Note Title.md';
const linuxAbsolutePath = '/absolute_path/journal/My Note Title.md';
const winResult = await determineNewNoteFilepath(
winAbsolutePath,
undefined,
it('should create a new note', async () => {
const target = getUriInWorkspace();
await NoteFactory.createNote(
target,
'Hello World',
new Resolver(new Map(), new Date())
);
expect(winResult.toFsPath()).toMatch(winAbsolutePath);
const linuxResult = await determineNewNoteFilepath(
linuxAbsolutePath,
undefined,
new Resolver(new Map(), new Date())
);
expect(linuxResult.toFsPath()).toMatch(linuxAbsolutePath);
expect(await fileExists(target)).toBeTruthy();
expect(window.activeTextEditor.document.getText()).toEqual('Hello World');
await deleteFile(target);
});
it('should compute the relative template filepath from the current directory', async () => {
const relativePath = isWindows
? 'journal\\My Note Title.md'
: 'journal/My Note Title.md';
const resultFilepath = await determineNewNoteFilepath(
relativePath,
it('should support not replacing the selection with a link to the newly created note', async () => {
const file = await createFile('This is my first file: World');
const { editor } = await showInEditor(file.uri);
editor.selection = new Selection(0, 23, 0, 28);
const target = getUriInWorkspace();
await NoteFactory.createNote(
target,
'Hello ${FOAM_SELECTED_TEXT} ${FOAM_SELECTED_TEXT}', // eslint-disable-line no-template-curly-in-string
new Resolver(new Map(), new Date()),
undefined,
new Resolver(new Map(), new Date())
false
);
const expectedPath = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath(relativePath);
expect(resultFilepath.toFsPath()).toMatch(expectedPath.toFsPath());
expect(window.activeTextEditor.document.getText()).toEqual(
'Hello World World'
);
expect(window.visibleTextEditors[0].document.getText()).toEqual(
`This is my first file: World`
);
await deleteFile(file.uri);
await deleteFile(target);
});
it('should use the note title if nothing else is available', async () => {
const noteTitle = 'My new note';
const resultFilepath = await determineNewNoteFilepath(
it('should support replacing the selection with a link to the newly created note', async () => {
const file = await createFile('This is my first file: World');
const { editor } = await showInEditor(file.uri);
editor.selection = new Selection(0, 23, 0, 28);
const target = getUriInWorkspace();
await NoteFactory.createNote(
target,
'Hello ${FOAM_SELECTED_TEXT} ${FOAM_SELECTED_TEXT}', // eslint-disable-line no-template-curly-in-string
new Resolver(new Map(), new Date()),
undefined,
undefined,
new Resolver(new Map().set('FOAM_TITLE', noteTitle), new Date())
true
);
const expectedPath = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath(`${noteTitle}.md`);
expect(resultFilepath.toFsPath()).toMatch(expectedPath.toFsPath());
});
it('should ask the user for a note title if nothing else is available', async () => {
const noteTitle = 'My new note';
const spy = jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve(noteTitle)));
const resultFilepath = await determineNewNoteFilepath(
undefined,
undefined,
new Resolver(new Map(), new Date())
expect(window.activeTextEditor.document.getText()).toEqual(
'Hello World World'
);
const expectedPath = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath(`${noteTitle}.md`);
expect(spy).toHaveBeenCalled();
expect(resultFilepath.toFsPath()).toMatch(expectedPath.toFsPath());
});
it('should filter invalid chars from the title #1042', async () => {
const noteTitle = 'My new note/';
const spy = jest
.spyOn(window, 'showInputBox')
.mockImplementationOnce(jest.fn(() => Promise.resolve(noteTitle)));
const resultFilepath = await determineNewNoteFilepath(
undefined,
undefined,
new Resolver(new Map(), new Date())
expect(window.visibleTextEditors[0].document.getText()).toEqual(
`This is my first file: [[${target.getName()}]]`
);
const expectedPath = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath(`My new note.md`);
expect(spy).toHaveBeenCalled();
expect(resultFilepath.toFsPath()).toMatch(expectedPath.toFsPath());
await deleteFile(file.uri);
await deleteFile(target);
});
});

View File

@@ -1,35 +1,51 @@
import { URI } from '../core/model/uri';
import { TextEncoder } from 'util';
import { FileType, SnippetString, ViewColumn, window, workspace } from 'vscode';
import {
SnippetString,
ViewColumn,
QuickPickItem,
commands,
window,
workspace,
} from 'vscode';
import { focusNote } from '../utils';
import { fromVsCodeUri, toVsCodeUri } from '../utils/vsc-utils';
import { extractFoamTemplateFrontmatterMetadata } from '../utils/template-frontmatter-parser';
import { UserCancelledOperation } from './errors';
import {
asAbsoluteWorkspaceUri,
createDocAndFocus,
deleteFile,
fileExists,
findSelectionContent,
getCurrentEditorDirectory,
readFile,
replaceSelection,
} from './editor';
import { Resolver } from './variable-resolver';
import dateFormat from 'dateformat';
import { isSome } from '../core/utils';
/**
* The templates directory
*/
export const TEMPLATES_DIR = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath('.foam', 'templates');
export const getTemplatesDir = () =>
fromVsCodeUri(workspace.workspaceFolders[0].uri).joinPath(
'.foam',
'templates'
);
/**
* The URI of the default template
*/
export const DEFAULT_TEMPLATE_URI = TEMPLATES_DIR.joinPath('new-note.md');
export const getDefaultTemplateUri = () =>
getTemplatesDir().joinPath('new-note.md');
/**
* The URI of the template for daily notes
*/
export const DAILY_NOTE_TEMPLATE_URI = TEMPLATES_DIR.joinPath('daily-note.md');
export const getDailyNoteTemplateUri = () =>
getTemplatesDir().joinPath('daily-note.md');
const WIKILINK_DEFAULT_TEMPLATE_TEXT = `# $\{1:$FOAM_TITLE}\n\n$0`;
@@ -55,9 +71,7 @@ For a full list of features see [the VS Code snippets page](https://code.visuals
export async function getTemplateMetadata(
templateUri: URI
): Promise<Map<string, string>> {
const contents = await workspace.fs
.readFile(toVsCodeUri(templateUri))
.then(bytes => bytes.toString());
const contents = (await readFile(templateUri)) ?? '';
const [templateMetadata] = extractFoamTemplateFrontmatterMetadata(contents);
return templateMetadata;
}
@@ -74,11 +88,7 @@ export async function getTemplateInfo(
templateFallbackText = '',
resolver: Resolver
) {
const templateText = (await fileExists(templateUri))
? await workspace.fs
.readFile(toVsCodeUri(templateUri))
.then(bytes => bytes.toString())
: templateFallbackText;
const templateText = (await readFile(templateUri)) ?? templateFallbackText;
const templateWithResolvedVariables = await resolver.resolveText(
templateText
@@ -95,7 +105,173 @@ export async function getTemplateInfo(
};
}
export type OnFileExistStrategy =
| 'open'
| 'overwrite'
| 'cancel'
| 'ask'
| ((filePath: URI) => Promise<URI | undefined>);
export async function askUserForTemplate() {
const templates = await getTemplates();
if (templates.length === 0) {
return offerToCreateTemplate();
}
const templatesMetadata = (
await Promise.all(
templates.map(async templateUri => {
const metadata = await getTemplateMetadata(templateUri);
metadata.set('templatePath', templateUri.getBasename());
return metadata;
})
)
).sort(sortTemplatesMetadata);
const items: QuickPickItem[] = await Promise.all(
templatesMetadata.map(metadata => {
const label = metadata.get('name') || metadata.get('templatePath');
const description = metadata.get('name')
? metadata.get('templatePath')
: null;
const detail = metadata.get('description');
const item = {
label: label,
description: description,
detail: detail,
};
Object.keys(item).forEach(key => {
if (!item[key]) {
delete item[key];
}
});
return item;
})
);
const selectedTemplate = await window.showQuickPick(items, {
placeHolder: 'Select a template to use.',
});
if (selectedTemplate === undefined) {
return undefined;
}
const templateFilename =
(selectedTemplate as QuickPickItem).description ||
(selectedTemplate as QuickPickItem).label;
const templateUri = getTemplatesDir().joinPath(templateFilename);
return templateUri;
}
async function offerToCreateTemplate(): Promise<void> {
const response = await window.showQuickPick(['Yes', 'No'], {
placeHolder:
'No templates available. Would you like to create one instead?',
});
if (response === 'Yes') {
commands.executeCommand('foam-vscode.create-new-template');
return;
}
}
function sortTemplatesMetadata(
t1: Map<string, string>,
t2: Map<string, string>
) {
// Sort by name's existence, then name, then path
if (t1.get('name') === undefined && t2.get('name') !== undefined) {
return 1;
}
if (t1.get('name') !== undefined && t2.get('name') === undefined) {
return -1;
}
const pathSortOrder = t1
.get('templatePath')
.localeCompare(t2.get('templatePath'));
if (t1.get('name') === undefined && t2.get('name') === undefined) {
return pathSortOrder;
}
const nameSortOrder = t1.get('name').localeCompare(t2.get('name'));
return nameSortOrder || pathSortOrder;
}
export const NoteFactory = {
createNote: async (
newFilePath: URI,
text: string,
resolver: Resolver,
onFileExists?: OnFileExistStrategy,
replaceSelectionWithLink = true
): Promise<{ didCreateFile: boolean; uri: URI | undefined }> => {
try {
const onFileExistsFn = async (existingFile: URI) => {
if (typeof onFileExists === 'function') {
return onFileExists(existingFile);
}
switch (onFileExists) {
case 'open':
await commands.executeCommand(
'vscode.open',
toVsCodeUri(existingFile)
);
return;
case 'overwrite':
await deleteFile(existingFile);
return existingFile;
case 'cancel':
return undefined;
case 'ask':
default: {
const newProposedPath = await askUserForFilepathConfirmation(
existingFile
);
return newProposedPath && URI.file(newProposedPath);
}
}
};
while (await fileExists(newFilePath)) {
const proposedNewFilepath = await onFileExistsFn(newFilePath);
if (proposedNewFilepath === undefined) {
return { didCreateFile: false, uri: newFilePath };
}
newFilePath = proposedNewFilepath;
}
const expandedText = await resolver.resolveText(text);
const selectedContent = findSelectionContent();
await createDocAndFocus(
new SnippetString(expandedText),
newFilePath,
selectedContent ? ViewColumn.Beside : ViewColumn.Active
);
if (replaceSelectionWithLink && selectedContent !== undefined) {
const newNoteTitle = newFilePath.getName();
await replaceSelection(
selectedContent.document,
selectedContent.selection,
`[[${newNoteTitle}]]`
);
}
return { didCreateFile: true, uri: newFilePath };
} catch (err) {
if (err instanceof UserCancelledOperation) {
return;
}
throw err;
}
},
/**
* Creates a new note using a template.
* @param templateUri the URI of the template to use.
@@ -108,60 +284,29 @@ export const NoteFactory = {
resolver: Resolver,
filepathFallbackURI?: URI,
templateFallbackText = '',
onFileExists?: (filePath: URI) => Promise<string | undefined>
onFileExists?: OnFileExistStrategy
): Promise<{ didCreateFile: boolean; uri: URI | undefined }> => {
try {
onFileExists = onFileExists
? onFileExists
: (existingFile: URI) => {
const filename = existingFile.getBasename();
return askUserForFilepathConfirmation(existingFile, filename);
};
const template = await getTemplateInfo(
templateUri,
templateFallbackText,
resolver
);
const selectedContent = findSelectionContent();
if (selectedContent?.content) {
resolver.define('FOAM_SELECTED_TEXT', selectedContent?.content);
}
const templateSnippet = new SnippetString(template.text);
let newFilePath = await determineNewNoteFilepath(
template.metadata.get('filepath'),
filepathFallbackURI,
resolver
const newFilePath = asAbsoluteWorkspaceUri(
template.metadata.has('filepath')
? URI.file(template.metadata.get('filepath'))
: isSome(filepathFallbackURI)
? filepathFallbackURI
: await getPathFromTitle(resolver)
);
while (await fileExists(newFilePath)) {
const proposedNewFilepath = await onFileExists(newFilePath);
if (proposedNewFilepath === undefined) {
return { didCreateFile: false, uri: newFilePath };
}
newFilePath = URI.file(proposedNewFilepath);
}
await createDocAndFocus(
templateSnippet,
return NoteFactory.createNote(
newFilePath,
selectedContent ? ViewColumn.Beside : ViewColumn.Active
template.text,
resolver,
onFileExists
);
if (selectedContent !== undefined) {
const newNoteTitle = newFilePath.getName();
await replaceSelection(
selectedContent.document,
selectedContent.selection,
`[[${newNoteTitle}]]`
);
}
return { didCreateFile: true, uri: newFilePath };
} catch (err) {
if (err instanceof UserCancelledOperation) {
return;
@@ -185,7 +330,7 @@ export const NoteFactory = {
targetDate
);
return NoteFactory.createFromTemplate(
DAILY_NOTE_TEMPLATE_URI,
getDailyNoteTemplateUri(),
resolver,
filepathFallbackURI,
templateFallbackText,
@@ -197,17 +342,24 @@ export const NoteFactory = {
* Creates a new note when following a placeholder wikilink using the default template.
* @param wikilinkPlaceholder the placeholder value from the wikilink. (eg. `[[Hello Joe]]` -> `Hello Joe`)
* @param filepathFallbackURI the URI to use if the template does not specify the `filepath` metadata attribute. This is configurable by the caller for backwards compatibility purposes.
* @param templateURI URI of the template to use. If undefined, use the default template.
*/
createForPlaceholderWikilink: (
createForPlaceholderWikilink: async (
wikilinkPlaceholder: string,
filepathFallbackURI: URI
filepathFallbackURI: URI,
templateURI?: URI
): Promise<{ didCreateFile: boolean; uri: URI | undefined }> => {
const resolver = new Resolver(
new Map().set('FOAM_TITLE', wikilinkPlaceholder),
new Date()
);
if (templateURI === undefined) {
templateURI = getDefaultTemplateUri();
}
return NoteFactory.createFromTemplate(
DEFAULT_TEMPLATE_URI,
templateURI,
resolver,
filepathFallbackURI,
WIKILINK_DEFAULT_TEMPLATE_TEXT
@@ -217,7 +369,7 @@ export const NoteFactory = {
export const createTemplate = async (): Promise<void> => {
const defaultFilename = 'new-template.md';
const defaultTemplate = TEMPLATES_DIR.joinPath(defaultFilename);
const defaultTemplate = getTemplatesDir().joinPath(defaultFilename);
const fsPath = defaultTemplate.toFsPath();
const filename = await window.showInputBox({
prompt: `Enter the filename for the new template`,
@@ -243,14 +395,18 @@ export const createTemplate = async (): Promise<void> => {
};
async function askUserForFilepathConfirmation(
defaultFilepath: URI,
defaultFilename: string
) {
defaultFilepath: URI
): Promise<string | undefined> {
const fsPath = defaultFilepath.toFsPath();
return await window.showInputBox({
const defaultFilename = defaultFilepath.getBasename();
const defaultExtension = defaultFilepath.getExtension();
return window.showInputBox({
prompt: `Enter the filename for the new note`,
value: fsPath,
valueSelection: [fsPath.length - defaultFilename.length, fsPath.length - 3],
valueSelection: [
fsPath.length - defaultFilename.length,
fsPath.length - defaultExtension.length,
],
validateInput: async value =>
value.trim().length === 0
? 'Please enter a value'
@@ -271,25 +427,14 @@ async function askUserForFilepathConfirmation(
*/
const UNALLOWED_CHARS = '/\\#%&{}<>?*$!\'":@+`|=';
export async function determineNewNoteFilepath(
templateFilepathAttribute: string | undefined,
fallbackURI: URI | undefined,
resolver: Resolver
): Promise<URI> {
if (templateFilepathAttribute) {
let defaultFilepath = URI.file(templateFilepathAttribute);
if (!defaultFilepath.isAbsolute()) {
defaultFilepath = fromVsCodeUri(
workspace.workspaceFolders[0].uri
).joinPath(templateFilepathAttribute);
}
return defaultFilepath;
}
if (fallbackURI) {
return fallbackURI;
}
/**
* Uses the title to generate a file path.
* It sanitizes the title to remove special characters and spaces.
*
* @param resolver the resolver to use
* @returns the string path of the new note
*/
export const getPathFromTitle = async (resolver: Resolver) => {
let defaultName = await resolver.resolveFromName('FOAM_TITLE');
UNALLOWED_CHARS.split('').forEach(char => {
defaultName = defaultName.split(char).join('');
@@ -299,13 +444,4 @@ export async function determineNewNoteFilepath(
`${defaultName}.md`
);
return defaultFilepath;
}
async function fileExists(uri: URI): Promise<boolean> {
try {
const stat = await workspace.fs.stat(toVsCodeUri(uri));
return stat.type === FileType.File;
} catch (e) {
return false;
}
}
};

View File

@@ -1,9 +1,14 @@
import { window } from 'vscode';
import { Selection, window } from 'vscode';
import { Resolver } from './variable-resolver';
import { Variable } from '../core/common/snippetParser';
import {
createFile,
deleteFile,
showInEditor,
} from '../test/test-utils-vscode';
describe('substituteFoamVariables', () => {
test('Does nothing if no Foam-specific variables are used', async () => {
describe('variable-resolver, text substitution', () => {
it('should do nothing if no Foam-specific variables are used', async () => {
const input = `
# \${AnotherVariable} <-- Unrelated to Foam
# \${AnotherVariable:default_value} <-- Unrelated to Foam
@@ -31,7 +36,7 @@ describe('substituteFoamVariables', () => {
expect(await resolver.resolveText(input)).toEqual(input);
});
test('Correctly substitutes variables that are substrings of one another', async () => {
it('should correctly substitute variables that are substrings of one another', async () => {
// FOAM_TITLE is a substring of FOAM_TITLE_NON_EXISTENT_VARIABLE
// If we're not careful with how we substitute the values
// we can end up putting the FOAM_TITLE in place FOAM_TITLE_NON_EXISTENT_VARIABLE should be.
@@ -56,8 +61,8 @@ describe('substituteFoamVariables', () => {
});
});
describe('resolveFoamVariables', () => {
test('Does nothing for unknown Foam-specific variables', async () => {
describe('variable-resolver, variable resolution', () => {
it('should do nothing for unknown Foam-specific variables', async () => {
const variables = [new Variable('FOAM_FOO')];
const expected = new Map<string, string>();
@@ -67,7 +72,7 @@ describe('resolveFoamVariables', () => {
expect(await resolver.resolveAll(variables)).toEqual(expected);
});
test('Resolves FOAM_TITLE', async () => {
it('should resolve FOAM_TITLE', async () => {
const foamTitle = 'My note title';
const variables = [new Variable('FOAM_TITLE'), new Variable('FOAM_SLUG')];
@@ -84,7 +89,7 @@ describe('resolveFoamVariables', () => {
expect(await resolver.resolveAll(variables)).toEqual(expected);
});
test('Resolves FOAM_TITLE without asking the user when it is provided', async () => {
it('should resolve FOAM_TITLE without asking the user when it is provided', async () => {
const foamTitle = 'My note title';
const variables = [new Variable('FOAM_TITLE')];
@@ -97,7 +102,7 @@ describe('resolveFoamVariables', () => {
expect(await resolver.resolveAll(variables)).toEqual(expected);
});
test('Resolves FOAM_DATE_* properties with current day by default', async () => {
it('should resolve FOAM_DATE_* properties with current day by default', async () => {
const variables = [
new Variable('FOAM_DATE_YEAR'),
new Variable('FOAM_DATE_YEAR_SHORT'),
@@ -134,7 +139,7 @@ describe('resolveFoamVariables', () => {
);
});
test('Resolves FOAM_DATE_* properties with given date', async () => {
it('should resolve FOAM_DATE_* properties with given date', async () => {
const targetDate = new Date(2021, 9, 12, 1, 2, 3);
const variables = [
new Variable('FOAM_DATE_YEAR'),
@@ -149,6 +154,7 @@ describe('resolveFoamVariables', () => {
new Variable('FOAM_DATE_MINUTE'),
new Variable('FOAM_DATE_SECOND'),
new Variable('FOAM_DATE_SECONDS_UNIX'),
new Variable('FOAM_DATE_WEEK'),
];
const expected = new Map<string, string>();
@@ -161,6 +167,7 @@ describe('resolveFoamVariables', () => {
expected.set('FOAM_DATE_DAY_NAME', 'Tuesday');
expected.set('FOAM_DATE_DAY_NAME_SHORT', 'Tue');
expected.set('FOAM_DATE_HOUR', '01');
expected.set('FOAM_DATE_WEEK', '41');
expected.set('FOAM_DATE_MINUTE', '02');
expected.set('FOAM_DATE_SECOND', '03');
expected.set(
@@ -173,10 +180,35 @@ describe('resolveFoamVariables', () => {
expect(await resolver.resolveAll(variables)).toEqual(expected);
});
describe('FOAM_DATE_WEEK', () => {
it('should start counting weeks from 1', async () => {
// week number starts from 1, not 0
// the first "partial week" of the year is really the last of the previous
const resolver = new Resolver(
new Map<string, string>(),
new Date(2021, 0, 1, 1, 2, 3)
);
expect(await resolver.resolve(new Variable('FOAM_DATE_WEEK'))).toEqual(
'53'
);
});
it('should pad week number to 2 digits', async () => {
// week number is 2-digit
const resolver = new Resolver(
new Map<string, string>(),
new Date(2021, 0, 7, 1, 2, 3)
);
expect(await resolver.resolve(new Variable('FOAM_DATE_WEEK'))).toEqual(
'01'
);
});
});
});
describe('resolveFoamTemplateVariables', () => {
test('Does nothing for template without Foam-specific variables', async () => {
describe('variable-resolver, resolveText', () => {
it('should do nothing for template without Foam-specific variables', async () => {
const input = `
# \${AnotherVariable} <-- Unrelated to Foam
# \${AnotherVariable:default_value} <-- Unrelated to Foam
@@ -191,7 +223,7 @@ describe('resolveFoamTemplateVariables', () => {
expect(await resolver.resolveText(input)).toEqual(expected);
});
test('Does nothing for unknown Foam-specific variables', async () => {
it('should do nothing for unknown Foam-specific variables', async () => {
const input = `
# $FOAM_FOO
# \${FOAM_FOO}
@@ -204,7 +236,18 @@ describe('resolveFoamTemplateVariables', () => {
expect(await resolver.resolveText(input)).toEqual(expected);
});
test('Appends FOAM_SELECTED_TEXT with a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template ends in a newline', async () => {
it('should resolve FOAM_SELECTED_TEXT with the editor selection', async () => {
const file = await createFile('Content of note file');
const { editor } = await showInEditor(file.uri);
editor.selection = new Selection(0, 11, 1, 0);
const resolver = new Resolver(new Map(), new Date());
expect(await resolver.resolveFromName('FOAM_SELECTED_TEXT')).toEqual(
'note file'
);
await deleteFile(file);
});
it('should append FOAM_SELECTED_TEXT with a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template ends in a newline', async () => {
const foamTitle = 'My note title';
jest
@@ -221,7 +264,7 @@ describe('resolveFoamTemplateVariables', () => {
expect(await resolver.resolveText(input)).toEqual(expected);
});
test('Appends FOAM_SELECTED_TEXT with a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template ends in multiple newlines', async () => {
it('should append FOAM_SELECTED_TEXT with a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template ends in multiple newlines', async () => {
const foamTitle = 'My note title';
jest
@@ -238,7 +281,7 @@ describe('resolveFoamTemplateVariables', () => {
expect(await resolver.resolveText(input)).toEqual(expected);
});
test('Appends FOAM_SELECTED_TEXT without a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template does not end in a newline', async () => {
it('should append FOAM_SELECTED_TEXT without a newline to the template if there is selected text but FOAM_SELECTED_TEXT is not referenced and the template does not end in a newline', async () => {
const foamTitle = 'My note title';
jest
@@ -255,7 +298,7 @@ describe('resolveFoamTemplateVariables', () => {
expect(await resolver.resolveText(input)).toEqual(expected);
});
test('Does not append FOAM_SELECTED_TEXT to a template if there is no selected text and is not referenced', async () => {
it('should not append FOAM_SELECTED_TEXT to a template if there is no selected text and is not referenced', async () => {
const foamTitle = 'My note title';
jest

View File

@@ -18,6 +18,7 @@ const knownFoamVariables = new Set([
'FOAM_DATE_MONTH_NAME',
'FOAM_DATE_MONTH_NAME_SHORT',
'FOAM_DATE_DATE',
'FOAM_DATE_WEEK',
'FOAM_DATE_DAY_NAME',
'FOAM_DATE_DAY_NAME_SHORT',
'FOAM_DATE_HOUR',
@@ -165,6 +166,25 @@ export class Resolver implements VariableResolver {
String(this.foamDate.getDate().valueOf()).padStart(2, '0')
);
break;
case 'FOAM_DATE_WEEK': {
// https://en.wikipedia.org/wiki/ISO_8601#Week_dates
const date = new Date(this.foamDate);
// Find Thursday of this week starting on Monday
date.setDate(date.getDate() + 4 - (date.getDay() || 7));
const thursday = date.getTime();
// Find January 1st
date.setMonth(0); // January
date.setDate(1); // 1st
const janFirst = date.getTime();
// Round the amount of days to compensate for daylight saving time
const days = Math.round((thursday - janFirst) / 86400000); // 1 day = 86400000 ms
const weekDay = Math.floor(days / 7) + 1;
value = Promise.resolve(String(weekDay.valueOf()).padStart(2, '0'));
break;
}
case 'FOAM_DATE_DAY_NAME':
value = Promise.resolve(
this.foamDate.toLocaleString('default', { weekday: 'long' })

View File

@@ -118,3 +118,21 @@ export const withModifiedConfiguration = async (key, value, fn: () => void) => {
*/
export const withModifiedFoamConfiguration = (key, value, fn: () => void) =>
withModifiedConfiguration(`foam.${key}`, value, fn);
/**
* Utility function to check if two URIs are the same.
* It has the goal of supporting Uri and URI, and dealing with
* inconsistencies in the way they are represented (especially the
* drive letter in Windows)
*
* @param actual the actual value
* @param expected the expected value
*/
export const expectSameUri = (
actual: vscode.Uri | URI,
expected: vscode.Uri | URI
) => {
expect(actual.path.toLocaleLowerCase()).toEqual(
expected.path.toLocaleLowerCase()
);
};

View File

@@ -85,6 +85,8 @@ let model = {
style: defaultStyle,
showNodesOfType: {
placeholder: true,
image: false,
attachment: false,
note: true,
tag: true,
},
@@ -235,12 +237,10 @@ function initDataviz(channel) {
Actions.highlightNode(node?.id);
})
.onNodeClick((node, event) => {
if (event.getModifierState('Control') || event.getModifierState('Meta')) {
channel.postMessage({
type: 'webviewDidSelectNode',
payload: node.id,
});
}
channel.postMessage({
type: 'webviewDidSelectNode',
payload: node.id,
});
Actions.selectNode(node.id, event.getModifierState('Shift'));
})
.onBackgroundClick(event => {

270
readme.md
View File

@@ -5,7 +5,7 @@
👀*This is an early stage project under rapid development. For updates join the [Foam community Discord](https://foambubble.github.io/join-discord/g)! 💬*
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-101-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-103-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
[![Discord Chat](https://img.shields.io/discord/729975036148056075?color=748AD9&label=discord%20chat&style=flat-square)](https://foambubble.github.io/join-discord/g)
@@ -91,7 +91,7 @@ See for each occurrence the context in which it lives, as well as a preview of t
### Tag Explorer Panel
Tag your notes and navigate them with the [Tag Explorer](https://foambubble.github.io/foam/user/user/features/tags).
Tag your notes and navigate them with the [Tag Explorer](https://foambubble.github.io/foam/user/features/tags).
Foam also supports hierarchical tags.
![Tag Explorer Panel](./assets/screenshots/feature-tags-panel.gif)
@@ -197,137 +197,141 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- prettier-ignore-start -->
<!-- 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>
</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>
</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> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Code">💻</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://github.com/TaiChi-IO"><img src="https://avatars3.githubusercontent.com/u/65092992?v=4?s=60" 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?s=60" 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?s=60" 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> <a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4?s=60" 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?s=60" 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?s=60" 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>
<td align="center"><a href="https://github.com/chirag-singhal"><img src="https://avatars3.githubusercontent.com/u/42653703?v=4?s=60" width="60px;" alt=""/><br /><sub><b>CHIRAG SINGHAL</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chirag-singhal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lostintangent"><img src="https://avatars3.githubusercontent.com/u/116461?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonathan Carter</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lostintangent" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.synesthesia.co.uk"><img src="https://avatars3.githubusercontent.com/u/181399?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Julian Elve</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=synesthesia" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/thomaskoppelaar"><img src="https://avatars3.githubusercontent.com/u/36331365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Thomas Koppelaar</b></sub></a><br /><a href="#question-thomaskoppelaar" title="Answering Questions">💬</a> <a href="https://github.com/foambubble/foam/commits?author=thomaskoppelaar" title="Code">💻</a> <a href="#userTesting-thomaskoppelaar" title="User Testing">📓</a></td>
<td align="center"><a href="http://www.akshaymehra.com"><img src="https://avatars1.githubusercontent.com/u/8671497?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Akshay</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MehraAkshay" title="Code">💻</a></td>
<td align="center"><a href="http://johnlindquist.com"><img src="https://avatars0.githubusercontent.com/u/36073?v=4?s=60" width="60px;" alt=""/><br /><sub><b>John Lindquist</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=johnlindquist" title="Documentation">📖</a></td>
<td align="center"><a href="https://ashwin.run/"><img src="https://avatars2.githubusercontent.com/u/1689183?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ashwin Ramaswami</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=epicfaace" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Klaudioz"><img src="https://avatars1.githubusercontent.com/u/632625?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Claudio Canales</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Klaudioz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vitaly-pevgonen"><img src="https://avatars0.githubusercontent.com/u/6272738?v=4?s=60" width="60px;" alt=""/><br /><sub><b>vitaly-pevgonen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vitaly-pevgonen" title="Documentation">📖</a></td>
<td align="center"><a href="https://dshemetov.github.io"><img src="https://avatars0.githubusercontent.com/u/1810426?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Dmitry Shemetov</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dshemetov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hooncp"><img src="https://avatars1.githubusercontent.com/u/48883554?v=4?s=60" width="60px;" alt=""/><br /><sub><b>hooncp</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hooncp" title="Documentation">📖</a></td>
<td align="center"><a href="http://rt-canada.ca"><img src="https://avatars1.githubusercontent.com/u/13721239?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Martin Laws</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=martinlaws" title="Documentation">📖</a></td>
<td align="center"><a href="http://seanksmith.me"><img src="https://avatars3.githubusercontent.com/u/2085441?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sean K Smith</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sksmith" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/kevin-neely/"><img src="https://avatars1.githubusercontent.com/u/37545028?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Kevin Neely</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=kneely" title="Documentation">📖</a></td>
<td align="center"><a href="https://ariefrahmansyah.dev"><img src="https://avatars3.githubusercontent.com/u/8122852?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Arief Rahmansyah</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ariefrahmansyah" title="Documentation">📖</a></td>
<td align="center"><a href="http://vhanda.in"><img src="https://avatars2.githubusercontent.com/u/426467?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Vishesh Handa</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vHanda" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.linkedin.com/in/heroichitesh"><img src="https://avatars3.githubusercontent.com/u/37622734?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Hitesh Kumar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=HeroicHitesh" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://spencerwoo.com"><img src="https://avatars2.githubusercontent.com/u/32114380?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Spencer Woo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=spencerwooo" title="Documentation">📖</a></td>
<td align="center"><a href="https://ingalless.com"><img src="https://avatars3.githubusercontent.com/u/22981941?v=4?s=60" width="60px;" alt=""/><br /><sub><b>ingalless</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Documentation">📖</a></td>
<td align="center"><a href="http://jmg-duarte.github.io"><img src="https://avatars2.githubusercontent.com/u/15343819?v=4?s=60" width="60px;" alt=""/><br /><sub><b>José Duarte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.yenly.wtf"><img src="https://avatars1.githubusercontent.com/u/6759658?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Yenly</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=yenly" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.hikerpig.cn"><img src="https://avatars1.githubusercontent.com/u/2259688?v=4?s=60" width="60px;" alt=""/><br /><sub><b>hikerpig</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hikerpig" title="Code">💻</a></td>
<td align="center"><a href="http://sigfried.org"><img src="https://avatars1.githubusercontent.com/u/1586931?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Sigfried Gold</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Sigfried" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.tristansokol.com"><img src="https://avatars3.githubusercontent.com/u/867661?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tristan Sokol</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tristansokol" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://umbrellait.com"><img src="https://avatars0.githubusercontent.com/u/49779373?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Danil Rodin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=umbrellait-danil-rodin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/scottjoewilliams/"><img src="https://avatars1.githubusercontent.com/u/2026866?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Scott Williams</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=scott-joe" title="Documentation">📖</a></td>
<td align="center"><a href="https://jackiexiao.github.io/blog"><img src="https://avatars2.githubusercontent.com/u/18050469?v=4?s=60" width="60px;" alt=""/><br /><sub><b>jackiexiao</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Jackiexiao" title="Documentation">📖</a></td>
<td align="center"><a href="https://generativist.substack.com/"><img src="https://avatars3.githubusercontent.com/u/78835?v=4?s=60" width="60px;" alt=""/><br /><sub><b>John B Nelson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jbn" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/asifm"><img src="https://avatars2.githubusercontent.com/u/3958387?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Asif Mehedi</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=asifm" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/litanlitudan"><img src="https://avatars2.githubusercontent.com/u/4970420?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Tan Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=litanlitudan" title="Code">💻</a></td>
<td align="center"><a href="http://shaunagordon.com"><img src="https://avatars1.githubusercontent.com/u/579361?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Shauna Gordon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ShaunaGordon" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://mcluck.tech"><img src="https://avatars1.githubusercontent.com/u/1753801?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mike Cluck</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MCluck90" title="Code">💻</a></td>
<td align="center"><a href="http://brandonpugh.com"><img src="https://avatars1.githubusercontent.com/u/684781?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brandon Pugh</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bpugh" title="Code">💻</a></td>
<td align="center"><a href="https://max.davitt.me"><img src="https://avatars1.githubusercontent.com/u/27709025?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Max Davitt</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=themaxdavitt" title="Documentation">📖</a></td>
<td align="center"><a href="http://briananglin.me"><img src="https://avatars3.githubusercontent.com/u/2637602?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brian Anglin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anglinb" title="Documentation">📖</a></td>
<td align="center"><a href="http://deft.work"><img src="https://avatars1.githubusercontent.com/u/1455507?v=4?s=60" width="60px;" alt=""/><br /><sub><b>elswork</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=elswork" title="Documentation">📖</a></td>
<td align="center"><a href="http://leonh.fr/"><img src="https://avatars.githubusercontent.com/u/19996318?v=4?s=60" width="60px;" alt=""/><br /><sub><b>léon h</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=leonhfr" title="Code">💻</a></td>
<td align="center"><a href="https://nygaard.site"><img src="https://avatars.githubusercontent.com/u/4606342?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Nikhil Nygaard</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=njnygaard" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.nitwit.se"><img src="https://avatars.githubusercontent.com/u/1382124?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Mark Dixon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nitwit-se" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/joeltjames"><img src="https://avatars.githubusercontent.com/u/3732400?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joel James</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joeltjames" title="Code">💻</a></td>
<td align="center"><a href="https://www.ryo33.com"><img src="https://avatars.githubusercontent.com/u/8780513?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Hashiguchi Ryo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ryo33" title="Documentation">📖</a></td>
<td align="center"><a href="https://movermeyer.com"><img src="https://avatars.githubusercontent.com/u/1459385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Michael Overmeyer</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=movermeyer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/derrickqin"><img src="https://avatars.githubusercontent.com/u/3038111?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Derrick Qin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=derrickqin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/zomars/"><img src="https://avatars.githubusercontent.com/u/3504472?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Omar López</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=zomars" title="Documentation">📖</a></td>
<td align="center"><a href="http://robincn.com"><img src="https://avatars.githubusercontent.com/u/1583193?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Robin King</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=RobinKing" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://twitter.com/deegovee"><img src="https://avatars.githubusercontent.com/u/4730170?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Dheepak </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dheepakg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bronson"><img src="https://avatars.githubusercontent.com/u/1776?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Scott Bronson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bronson" title="Documentation">📖</a></td>
<td align="center"><a href="http://rafaelriedel.de"><img src="https://avatars.githubusercontent.com/u/41793?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Rafael Riedel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=rafo" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Pearcekieser"><img src="https://avatars.githubusercontent.com/u/5055971?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Pearcekieser</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Pearcekieser" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/theowenyoung"><img src="https://avatars.githubusercontent.com/u/62473795?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Owen Young</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=theowenyoung" title="Documentation">📖</a> <a href="#content-theowenyoung" title="Content">🖋</a></td>
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt=""/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt=""/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iam-yan"><img src="https://avatars.githubusercontent.com/u/48427014?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Yan</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=iam-yan" title="Documentation">📖</a></td>
<td align="center"><a href="https://WikiEducator.org/User:JimTittsler"><img src="https://avatars.githubusercontent.com/u/180326?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Jim Tittsler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimt" title="Documentation">📖</a></td>
<td align="center"><a href="http://malcolmmielle.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/4457840?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Malcolm Mielle</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MalcolmMielle" title="Documentation">📖</a></td>
<td align="center"><a href="https://snippets.page/"><img src="https://avatars.githubusercontent.com/u/74916913?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Veesar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=veesar" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bentongxyz"><img src="https://avatars.githubusercontent.com/u/60358804?v=4?s=60" width="60px;" alt=""/><br /><sub><b>bentongxyz</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bentongxyz" title="Code">💻</a></td>
<td align="center"><a href="https://brianjdevries.com"><img src="https://avatars.githubusercontent.com/u/42778030?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Brian DeVries</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=techCarpenter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
<td align="center"><a href="http://www.drewtyler.com"><img src="https://avatars.githubusercontent.com/u/5640816?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Drew Tyler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=drewtyler" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Lauviah0622"><img src="https://avatars.githubusercontent.com/u/43416399?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Lauviah0622</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Lauviah0622" title="Code">💻</a></td>
<td align="center"><a href="https://www.elastic.co/elastic-agent"><img src="https://avatars.githubusercontent.com/u/1813008?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Josh Dover</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joshdover" title="Code">💻</a></td>
<td align="center"><a href="http://phelm.co.uk"><img src="https://avatars.githubusercontent.com/u/4057948?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Phil Helm</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=phelma" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/lingyv-li"><img src="https://avatars.githubusercontent.com/u/8937944?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Larry Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lingyv-li" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/infogulch"><img src="https://avatars.githubusercontent.com/u/133882?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Joe Taber</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=infogulch" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.readingsnail.pe.kr"><img src="https://avatars.githubusercontent.com/u/1904967?v=4?s=60" width="60px;" alt=""/><br /><sub><b>Woosuk Park</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=readingsnail" title="Documentation">📖</a></td>
</tr>
<tbody>
<tr>
<td align="center"><a href="https://jevakallio.dev/"><img src="https://avatars1.githubusercontent.com/u/1203949?v=4?s=60" width="60px;" alt="Jani Eväkallio"/><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="Joe Previte"/><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="Riccardo"/><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="Janne Ojanaho"/><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="Paul Shen"/><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="coffenbacher"/><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="Mathieu Dutour"/><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="Michael Hansen"/><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="David Nadlinger"/><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="Fernando"/><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="Juan Gonzalez"/><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="Louie Christie"/><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="Sandro"/><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="Simon Knott"/><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="Steven"/><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="Tim"/><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="Saurav Khdoolia"/><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="Ankit Tiwari"/><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> <a href="https://github.com/foambubble/foam/commits?author=anku255" title="Code">💻</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="Ayush Baweja"/><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?s=60" width="60px;" alt="TaiChi-IO"/><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?s=60" width="60px;" alt="Juan F Gonzalez "/><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?s=60" width="60px;" alt="Sanket Dasgupta"/><br /><sub><b>Sanket Dasgupta</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Documentation">📖</a> <a href="https://github.com/foambubble/foam/commits?author=SanketDG" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nstafie"><img src="https://avatars1.githubusercontent.com/u/10801854?v=4?s=60" width="60px;" alt="Nicholas Stafie"/><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?s=60" width="60px;" alt="Francis Hamel"/><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?s=60" width="60px;" alt="digiguru"/><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>
<td align="center"><a href="https://github.com/chirag-singhal"><img src="https://avatars3.githubusercontent.com/u/42653703?v=4?s=60" width="60px;" alt="CHIRAG SINGHAL"/><br /><sub><b>CHIRAG SINGHAL</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chirag-singhal" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lostintangent"><img src="https://avatars3.githubusercontent.com/u/116461?v=4?s=60" width="60px;" alt="Jonathan Carter"/><br /><sub><b>Jonathan Carter</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lostintangent" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.synesthesia.co.uk"><img src="https://avatars3.githubusercontent.com/u/181399?v=4?s=60" width="60px;" alt="Julian Elve"/><br /><sub><b>Julian Elve</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=synesthesia" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/thomaskoppelaar"><img src="https://avatars3.githubusercontent.com/u/36331365?v=4?s=60" width="60px;" alt="Thomas Koppelaar"/><br /><sub><b>Thomas Koppelaar</b></sub></a><br /><a href="#question-thomaskoppelaar" title="Answering Questions">💬</a> <a href="https://github.com/foambubble/foam/commits?author=thomaskoppelaar" title="Code">💻</a> <a href="#userTesting-thomaskoppelaar" title="User Testing">📓</a></td>
<td align="center"><a href="http://www.akshaymehra.com"><img src="https://avatars1.githubusercontent.com/u/8671497?v=4?s=60" width="60px;" alt="Akshay"/><br /><sub><b>Akshay</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MehraAkshay" title="Code">💻</a></td>
<td align="center"><a href="http://johnlindquist.com"><img src="https://avatars0.githubusercontent.com/u/36073?v=4?s=60" width="60px;" alt="John Lindquist"/><br /><sub><b>John Lindquist</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=johnlindquist" title="Documentation">📖</a></td>
<td align="center"><a href="https://ashwin.run/"><img src="https://avatars2.githubusercontent.com/u/1689183?v=4?s=60" width="60px;" alt="Ashwin Ramaswami"/><br /><sub><b>Ashwin Ramaswami</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=epicfaace" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Klaudioz"><img src="https://avatars1.githubusercontent.com/u/632625?v=4?s=60" width="60px;" alt="Claudio Canales"/><br /><sub><b>Claudio Canales</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Klaudioz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vitaly-pevgonen"><img src="https://avatars0.githubusercontent.com/u/6272738?v=4?s=60" width="60px;" alt="vitaly-pevgonen"/><br /><sub><b>vitaly-pevgonen</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vitaly-pevgonen" title="Documentation">📖</a></td>
<td align="center"><a href="https://dshemetov.github.io"><img src="https://avatars0.githubusercontent.com/u/1810426?v=4?s=60" width="60px;" alt="Dmitry Shemetov"/><br /><sub><b>Dmitry Shemetov</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dshemetov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/hooncp"><img src="https://avatars1.githubusercontent.com/u/48883554?v=4?s=60" width="60px;" alt="hooncp"/><br /><sub><b>hooncp</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hooncp" title="Documentation">📖</a></td>
<td align="center"><a href="http://rt-canada.ca"><img src="https://avatars1.githubusercontent.com/u/13721239?v=4?s=60" width="60px;" alt="Martin Laws"/><br /><sub><b>Martin Laws</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=martinlaws" title="Documentation">📖</a></td>
<td align="center"><a href="http://seanksmith.me"><img src="https://avatars3.githubusercontent.com/u/2085441?v=4?s=60" width="60px;" alt="Sean K Smith"/><br /><sub><b>Sean K Smith</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=sksmith" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/kevin-neely/"><img src="https://avatars1.githubusercontent.com/u/37545028?v=4?s=60" width="60px;" alt="Kevin Neely"/><br /><sub><b>Kevin Neely</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=kneely" title="Documentation">📖</a></td>
<td align="center"><a href="https://ariefrahmansyah.dev"><img src="https://avatars3.githubusercontent.com/u/8122852?v=4?s=60" width="60px;" alt="Arief Rahmansyah"/><br /><sub><b>Arief Rahmansyah</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ariefrahmansyah" title="Documentation">📖</a></td>
<td align="center"><a href="http://vhanda.in"><img src="https://avatars2.githubusercontent.com/u/426467?v=4?s=60" width="60px;" alt="Vishesh Handa"/><br /><sub><b>Vishesh Handa</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=vHanda" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.linkedin.com/in/heroichitesh"><img src="https://avatars3.githubusercontent.com/u/37622734?v=4?s=60" width="60px;" alt="Hitesh Kumar"/><br /><sub><b>Hitesh Kumar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=HeroicHitesh" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://spencerwoo.com"><img src="https://avatars2.githubusercontent.com/u/32114380?v=4?s=60" width="60px;" alt="Spencer Woo"/><br /><sub><b>Spencer Woo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=spencerwooo" title="Documentation">📖</a></td>
<td align="center"><a href="https://ingalless.com"><img src="https://avatars3.githubusercontent.com/u/22981941?v=4?s=60" width="60px;" alt="ingalless"/><br /><sub><b>ingalless</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=ingalless" title="Documentation">📖</a></td>
<td align="center"><a href="http://jmg-duarte.github.io"><img src="https://avatars2.githubusercontent.com/u/15343819?v=4?s=60" width="60px;" alt="José Duarte"/><br /><sub><b>José Duarte</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Code">💻</a> <a href="https://github.com/foambubble/foam/commits?author=jmg-duarte" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.yenly.wtf"><img src="https://avatars1.githubusercontent.com/u/6759658?v=4?s=60" width="60px;" alt="Yenly"/><br /><sub><b>Yenly</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=yenly" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.hikerpig.cn"><img src="https://avatars1.githubusercontent.com/u/2259688?v=4?s=60" width="60px;" alt="hikerpig"/><br /><sub><b>hikerpig</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=hikerpig" title="Code">💻</a></td>
<td align="center"><a href="http://sigfried.org"><img src="https://avatars1.githubusercontent.com/u/1586931?v=4?s=60" width="60px;" alt="Sigfried Gold"/><br /><sub><b>Sigfried Gold</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Sigfried" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.tristansokol.com"><img src="https://avatars3.githubusercontent.com/u/867661?v=4?s=60" width="60px;" alt="Tristan Sokol"/><br /><sub><b>Tristan Sokol</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=tristansokol" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://umbrellait.com"><img src="https://avatars0.githubusercontent.com/u/49779373?v=4?s=60" width="60px;" alt="Danil Rodin"/><br /><sub><b>Danil Rodin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=umbrellait-danil-rodin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/scottjoewilliams/"><img src="https://avatars1.githubusercontent.com/u/2026866?v=4?s=60" width="60px;" alt="Scott Williams"/><br /><sub><b>Scott Williams</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=scott-joe" title="Documentation">📖</a></td>
<td align="center"><a href="https://jackiexiao.github.io/blog"><img src="https://avatars2.githubusercontent.com/u/18050469?v=4?s=60" width="60px;" alt="jackiexiao"/><br /><sub><b>jackiexiao</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Jackiexiao" title="Documentation">📖</a></td>
<td align="center"><a href="https://generativist.substack.com/"><img src="https://avatars3.githubusercontent.com/u/78835?v=4?s=60" width="60px;" alt="John B Nelson"/><br /><sub><b>John B Nelson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jbn" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/asifm"><img src="https://avatars2.githubusercontent.com/u/3958387?v=4?s=60" width="60px;" alt="Asif Mehedi"/><br /><sub><b>Asif Mehedi</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=asifm" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/litanlitudan"><img src="https://avatars2.githubusercontent.com/u/4970420?v=4?s=60" width="60px;" alt="Tan Li"/><br /><sub><b>Tan Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=litanlitudan" title="Code">💻</a></td>
<td align="center"><a href="http://shaunagordon.com"><img src="https://avatars1.githubusercontent.com/u/579361?v=4?s=60" width="60px;" alt="Shauna Gordon"/><br /><sub><b>Shauna Gordon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ShaunaGordon" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://mcluck.tech"><img src="https://avatars1.githubusercontent.com/u/1753801?v=4?s=60" width="60px;" alt="Mike Cluck"/><br /><sub><b>Mike Cluck</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MCluck90" title="Code">💻</a></td>
<td align="center"><a href="http://brandonpugh.com"><img src="https://avatars1.githubusercontent.com/u/684781?v=4?s=60" width="60px;" alt="Brandon Pugh"/><br /><sub><b>Brandon Pugh</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bpugh" title="Code">💻</a></td>
<td align="center"><a href="https://max.davitt.me"><img src="https://avatars1.githubusercontent.com/u/27709025?v=4?s=60" width="60px;" alt="Max Davitt"/><br /><sub><b>Max Davitt</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=themaxdavitt" title="Documentation">📖</a></td>
<td align="center"><a href="http://briananglin.me"><img src="https://avatars3.githubusercontent.com/u/2637602?v=4?s=60" width="60px;" alt="Brian Anglin"/><br /><sub><b>Brian Anglin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=anglinb" title="Documentation">📖</a></td>
<td align="center"><a href="http://deft.work"><img src="https://avatars1.githubusercontent.com/u/1455507?v=4?s=60" width="60px;" alt="elswork"/><br /><sub><b>elswork</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=elswork" title="Documentation">📖</a></td>
<td align="center"><a href="http://leonh.fr/"><img src="https://avatars.githubusercontent.com/u/19996318?v=4?s=60" width="60px;" alt="léon h"/><br /><sub><b>léon h</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=leonhfr" title="Code">💻</a></td>
<td align="center"><a href="https://nygaard.site"><img src="https://avatars.githubusercontent.com/u/4606342?v=4?s=60" width="60px;" alt="Nikhil Nygaard"/><br /><sub><b>Nikhil Nygaard</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=njnygaard" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.nitwit.se"><img src="https://avatars.githubusercontent.com/u/1382124?v=4?s=60" width="60px;" alt="Mark Dixon"/><br /><sub><b>Mark Dixon</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=nitwit-se" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/joeltjames"><img src="https://avatars.githubusercontent.com/u/3732400?v=4?s=60" width="60px;" alt="Joel James"/><br /><sub><b>Joel James</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joeltjames" title="Code">💻</a></td>
<td align="center"><a href="https://www.ryo33.com"><img src="https://avatars.githubusercontent.com/u/8780513?v=4?s=60" width="60px;" alt="Hashiguchi Ryo"/><br /><sub><b>Hashiguchi Ryo</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ryo33" title="Documentation">📖</a></td>
<td align="center"><a href="https://movermeyer.com"><img src="https://avatars.githubusercontent.com/u/1459385?v=4?s=60" width="60px;" alt="Michael Overmeyer"/><br /><sub><b>Michael Overmeyer</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=movermeyer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/derrickqin"><img src="https://avatars.githubusercontent.com/u/3038111?v=4?s=60" width="60px;" alt="Derrick Qin"/><br /><sub><b>Derrick Qin</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=derrickqin" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/zomars/"><img src="https://avatars.githubusercontent.com/u/3504472?v=4?s=60" width="60px;" alt="Omar López"/><br /><sub><b>Omar López</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=zomars" title="Documentation">📖</a></td>
<td align="center"><a href="http://robincn.com"><img src="https://avatars.githubusercontent.com/u/1583193?v=4?s=60" width="60px;" alt="Robin King"/><br /><sub><b>Robin King</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=RobinKing" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://twitter.com/deegovee"><img src="https://avatars.githubusercontent.com/u/4730170?v=4?s=60" width="60px;" alt="Dheepak "/><br /><sub><b>Dheepak </b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dheepakg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/daniel-vera-g"><img src="https://avatars.githubusercontent.com/u/28257108?v=4?s=60" width="60px;" alt="Daniel VG"/><br /><sub><b>Daniel VG</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=daniel-vera-g" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Barabazs"><img src="https://avatars.githubusercontent.com/u/31799121?v=4?s=60" width="60px;" alt="Barabas"/><br /><sub><b>Barabas</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Barabazs" title="Code">💻</a></td>
<td align="center"><a href="http://enginveske@gmail.com"><img src="https://avatars.githubusercontent.com/u/43685404?v=4?s=60" width="60px;" alt="Engincan VESKE"/><br /><sub><b>Engincan VESKE</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=EngincanV" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.paulderaaij.nl"><img src="https://avatars.githubusercontent.com/u/495374?v=4?s=60" width="60px;" alt="Paul de Raaij"/><br /><sub><b>Paul de Raaij</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=pderaaij" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bronson"><img src="https://avatars.githubusercontent.com/u/1776?v=4?s=60" width="60px;" alt="Scott Bronson"/><br /><sub><b>Scott Bronson</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bronson" title="Documentation">📖</a></td>
<td align="center"><a href="http://rafaelriedel.de"><img src="https://avatars.githubusercontent.com/u/41793?v=4?s=60" width="60px;" alt="Rafael Riedel"/><br /><sub><b>Rafael Riedel</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=rafo" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Pearcekieser"><img src="https://avatars.githubusercontent.com/u/5055971?v=4?s=60" width="60px;" alt="Pearcekieser"/><br /><sub><b>Pearcekieser</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Pearcekieser" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/theowenyoung"><img src="https://avatars.githubusercontent.com/u/62473795?v=4?s=60" width="60px;" alt="Owen Young"/><br /><sub><b>Owen Young</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=theowenyoung" title="Documentation">📖</a> <a href="#content-theowenyoung" title="Content">🖋</a></td>
<td align="center"><a href="http://www.prashu.com"><img src="https://avatars.githubusercontent.com/u/476729?v=4?s=60" width="60px;" alt="Prashanth Subrahmanyam"/><br /><sub><b>Prashanth Subrahmanyam</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=ksprashu" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/JonasSprenger"><img src="https://avatars.githubusercontent.com/u/25108895?v=4?s=60" width="60px;" alt="Jonas SPRENGER"/><br /><sub><b>Jonas SPRENGER</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=JonasSprenger" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Laptop765"><img src="https://avatars.githubusercontent.com/u/1468359?v=4?s=60" width="60px;" alt="Paul"/><br /><sub><b>Paul</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Laptop765" title="Documentation">📖</a></td>
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=60" width="60px;" alt="Ikko Ashimine"/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=eltociear" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/memeplex"><img src="https://avatars.githubusercontent.com/u/2845433?v=4?s=60" width="60px;" alt="memeplex"/><br /><sub><b>memeplex</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=memeplex" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/AndreiD049"><img src="https://avatars.githubusercontent.com/u/52671223?v=4?s=60" width="60px;" alt="AndreiD049"/><br /><sub><b>AndreiD049</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=AndreiD049" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iam-yan"><img src="https://avatars.githubusercontent.com/u/48427014?v=4?s=60" width="60px;" alt="Yan"/><br /><sub><b>Yan</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=iam-yan" title="Documentation">📖</a></td>
<td align="center"><a href="https://WikiEducator.org/User:JimTittsler"><img src="https://avatars.githubusercontent.com/u/180326?v=4?s=60" width="60px;" alt="Jim Tittsler"/><br /><sub><b>Jim Tittsler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=jimt" title="Documentation">📖</a></td>
<td align="center"><a href="http://malcolmmielle.wordpress.com/"><img src="https://avatars.githubusercontent.com/u/4457840?v=4?s=60" width="60px;" alt="Malcolm Mielle"/><br /><sub><b>Malcolm Mielle</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=MalcolmMielle" title="Documentation">📖</a></td>
<td align="center"><a href="https://snippets.page/"><img src="https://avatars.githubusercontent.com/u/74916913?v=4?s=60" width="60px;" alt="Veesar"/><br /><sub><b>Veesar</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=veesar" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bentongxyz"><img src="https://avatars.githubusercontent.com/u/60358804?v=4?s=60" width="60px;" alt="bentongxyz"/><br /><sub><b>bentongxyz</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=bentongxyz" title="Code">💻</a></td>
<td align="center"><a href="https://brianjdevries.com"><img src="https://avatars.githubusercontent.com/u/42778030?v=4?s=60" width="60px;" alt="Brian DeVries"/><br /><sub><b>Brian DeVries</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=techCarpenter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://Cliffordfajardo.com"><img src="https://avatars.githubusercontent.com/u/6743796?v=4?s=60" width="60px;" alt="Clifford Fajardo "/><br /><sub><b>Clifford Fajardo </b></sub></a><br /><a href="#tool-cliffordfajardo" title="Tools">🔧</a></td>
<td align="center"><a href="http://cu-dev.ca"><img src="https://avatars.githubusercontent.com/u/6589365?v=4?s=60" width="60px;" alt="Chris Usick"/><br /><sub><b>Chris Usick</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=chrisUsick" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/josephdecock"><img src="https://avatars.githubusercontent.com/u/1145533?v=4?s=60" width="60px;" alt="Joe DeCock"/><br /><sub><b>Joe DeCock</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=josephdecock" title="Code">💻</a></td>
<td align="center"><a href="http://www.drewtyler.com"><img src="https://avatars.githubusercontent.com/u/5640816?v=4?s=60" width="60px;" alt="Drew Tyler"/><br /><sub><b>Drew Tyler</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=drewtyler" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Lauviah0622"><img src="https://avatars.githubusercontent.com/u/43416399?v=4?s=60" width="60px;" alt="Lauviah0622"/><br /><sub><b>Lauviah0622</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Lauviah0622" title="Code">💻</a></td>
<td align="center"><a href="https://www.elastic.co/elastic-agent"><img src="https://avatars.githubusercontent.com/u/1813008?v=4?s=60" width="60px;" alt="Josh Dover"/><br /><sub><b>Josh Dover</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=joshdover" title="Code">💻</a></td>
<td align="center"><a href="http://phelm.co.uk"><img src="https://avatars.githubusercontent.com/u/4057948?v=4?s=60" width="60px;" alt="Phil Helm"/><br /><sub><b>Phil Helm</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=phelma" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/lingyv-li"><img src="https://avatars.githubusercontent.com/u/8937944?v=4?s=60" width="60px;" alt="Larry Li"/><br /><sub><b>Larry Li</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=lingyv-li" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/infogulch"><img src="https://avatars.githubusercontent.com/u/133882?v=4?s=60" width="60px;" alt="Joe Taber"/><br /><sub><b>Joe Taber</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=infogulch" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.readingsnail.pe.kr"><img src="https://avatars.githubusercontent.com/u/1904967?v=4?s=60" width="60px;" alt="Woosuk Park"/><br /><sub><b>Woosuk Park</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=readingsnail" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.dmurph.com"><img src="https://avatars.githubusercontent.com/u/294026?v=4?s=60" width="60px;" alt="Daniel Murphy"/><br /><sub><b>Daniel Murphy</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=dmurph" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Dominic-DallOsto"><img src="https://avatars.githubusercontent.com/u/26859884?v=4?s=60" width="60px;" alt="Dominic D"/><br /><sub><b>Dominic D</b></sub></a><br /><a href="https://github.com/foambubble/foam/commits?author=Dominic-DallOsto" title="Code">💻</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->