From b030b50b95d3ac8f0e64ecac55db1ff5d4323a24 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Thu, 28 Jul 2022 22:09:51 +0200
Subject: [PATCH 01/54] Added stork search

---
 default/templates/layouts/book.tpl | 36 ++++++++++++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 45f805202..b2bbf99c5 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -1,5 +1,21 @@
 <apply template="base">
-  <bind tag="head-main"></bind>
+  <bind tag="head-main">
+    <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
+    <style>
+    .stork-wrapper .stork-output {
+      margin-top: 0;
+      border-radius: 0;
+      position: sticky;
+    }
+    .stork-wrapper .stork-message {
+      padding: 0.5rem 1rem;
+      border-radius: 0;
+      margin-top: 1px;
+      margin-bottom: 1px;
+      border-color: rgba(99,102,241,var(--tw-border-opacity));
+    }
+    </style>
+  </bind>
   <bind tag="body-main">
     <div class="container mx-auto">
 
@@ -10,6 +26,9 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
+          <div class="stork-wrapper">
+            <input data-stork="federalist" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
+          </div>
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
@@ -61,6 +80,9 @@
 
         <!-- Main body column -->
         <div class="flex-1 w-full overflow-x-auto bg-white">
+          <div class="stork-wrapper md:sticky top-0">
+            <div data-stork="federalist-output" class="stork-output"></div>
+          </div>
           <main class="px-4 py-4">
             <apply template="components/note-title" />
             <apply template="components/note-body" />
@@ -75,5 +97,15 @@
       </div>
       <apply template="components/footer" />
     </div>
+    <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
+    <script>
+      if (document.readyState == 'loading') {
+        stork.register(
+          'federalist',
+          //'https://files.stork-search.net/releases/v1.5.0/federalist.st'
+          '/stork.st'
+        );
+      }
+    </script>
   </bind>
-</apply>
\ No newline at end of file
+</apply>

From 116265a77469094d235311941cc7c989afab673f Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Thu, 28 Jul 2022 22:19:05 +0200
Subject: [PATCH 02/54] Added search tmp docs

---
 docs/tips/search.md | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 docs/tips/search.md

diff --git a/docs/tips/search.md b/docs/tips/search.md
new file mode 100644
index 000000000..e4abd8c09
--- /dev/null
+++ b/docs/tips/search.md
@@ -0,0 +1,18 @@
+# Search
+
+**These are temporary private notes!** TODO: Cleanup and write actual docs
+
+```bash
+emanote gen result -L 'docs;default'
+
+cd result
+
+echo '[input]' > stork.toml
+echo 'files = [' >> stork.toml
+rg -g '!-/' '<title>' -A1 | grep html- | sed 's/\(.*\)-  *\(.*\)/  {path = "\1", url="\1", title = "\2"},/' >> stork.toml
+echo ']' >> stork.toml
+
+stork build --input stork.toml --output stork.st
+
+podman run --rm -it -v $PWD:/usr/share/nginx/html:Z -p 8080:80 nginx:alpine
+```

From 46a5c0eef360c0bf59a23da7135e8576e717103f Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Thu, 28 Jul 2022 23:44:30 +0200
Subject: [PATCH 03/54] Fixed rg cmd

---
 docs/tips/search.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/tips/search.md b/docs/tips/search.md
index e4abd8c09..48767b754 100644
--- a/docs/tips/search.md
+++ b/docs/tips/search.md
@@ -9,7 +9,7 @@ cd result
 
 echo '[input]' > stork.toml
 echo 'files = [' >> stork.toml
-rg -g '!-/' '<title>' -A1 | grep html- | sed 's/\(.*\)-  *\(.*\)/  {path = "\1", url="\1", title = "\2"},/' >> stork.toml
+rg -g '!-/' '<title>' -A1 | grep html- | sed 's/\(.*.html\)-  *\(.*\)/  {path = "\1", url="\1", title = """\2"""},/' >> stork.toml
 echo ']' >> stork.toml
 
 stork build --input stork.toml --output stork.st

From 6a29a32e0b4d0ca84634d6da088df12db8aa5fe5 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 15:29:16 +0200
Subject: [PATCH 04/54] Extracted to hooks

---
 default/templates/base.tpl                  |  3 +-
 default/templates/layouts/book.tpl          | 38 +++------------------
 docs/templates/hooks/after-body.tpl         | 16 +++++++++
 docs/templates/hooks/before-nav-content.tpl |  5 +++
 docs/templates/hooks/more-head.tpl          | 32 +++++++++++++++++
 5 files changed, 60 insertions(+), 34 deletions(-)
 create mode 100644 docs/templates/hooks/after-body.tpl
 create mode 100644 docs/templates/hooks/before-nav-content.tpl
 create mode 100644 docs/templates/hooks/more-head.tpl

diff --git a/default/templates/base.tpl b/default/templates/base.tpl
index a0d092f2a..6eabbabeb 100644
--- a/default/templates/base.tpl
+++ b/default/templates/base.tpl
@@ -44,6 +44,7 @@
 
 <body class="${bodyClass}">
   <body-main />
+  <apply template="/templates/hooks/after-body" />
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index b2bbf99c5..affca4023 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -1,21 +1,5 @@
 <apply template="base">
-  <bind tag="head-main">
-    <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
-    <style>
-    .stork-wrapper .stork-output {
-      margin-top: 0;
-      border-radius: 0;
-      position: sticky;
-    }
-    .stork-wrapper .stork-message {
-      padding: 0.5rem 1rem;
-      border-radius: 0;
-      margin-top: 1px;
-      margin-bottom: 1px;
-      border-color: rgba(99,102,241,var(--tw-border-opacity));
-    }
-    </style>
-  </bind>
+  <bind tag="head-main"></bind>
   <bind tag="body-main">
     <div class="container mx-auto">
 
@@ -26,9 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <div class="stork-wrapper">
-            <input data-stork="federalist" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
-          </div>
+          <apply template="hooks/before-nav-content" />
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
@@ -76,13 +58,12 @@
             </ema:route-tree>
 
           </div>
+          <apply template="hooks/after-nav-content" />
         </nav>
 
         <!-- Main body column -->
         <div class="flex-1 w-full overflow-x-auto bg-white">
-          <div class="stork-wrapper md:sticky top-0">
-            <div data-stork="federalist-output" class="stork-output"></div>
-          </div>
+          <apply template="/templates/hooks/before-main" />
           <main class="px-4 py-4">
             <apply template="components/note-title" />
             <apply template="components/note-body" />
@@ -93,19 +74,10 @@
             <apply template="components/metadata" />
             <apply template="/templates/hooks/note-end" />
           </main>
+          <apply template="/templates/hooks/after-main" />
         </div>
       </div>
       <apply template="components/footer" />
     </div>
-    <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
-    <script>
-      if (document.readyState == 'loading') {
-        stork.register(
-          'federalist',
-          //'https://files.stork-search.net/releases/v1.5.0/federalist.st'
-          '/stork.st'
-        );
-      }
-    </script>
   </bind>
 </apply>
diff --git a/docs/templates/hooks/after-body.tpl b/docs/templates/hooks/after-body.tpl
new file mode 100644
index 000000000..bb840c801
--- /dev/null
+++ b/docs/templates/hooks/after-body.tpl
@@ -0,0 +1,16 @@
+<!-- Stork search scripts, styling are at end of <head> -->
+<script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
+<script>
+	if (document.readyState !== "complete") {
+		stork.register(
+			'emanote-search', // has to match input[data-stork] attribute value
+			(document.querySelector('head > base').getAttribute('href') || '')+'stork.st'
+		);
+	} else {
+		stork.downloadIndex(
+			'emanote-search', // has to match input[data-stork] attribute value
+			(document.querySelector('head > base').getAttribute('href') || '')+'stork.st',
+			{ forceOverwrite: true } // needed for Ema's hot-reload
+		);
+	}
+</script>
diff --git a/docs/templates/hooks/before-nav-content.tpl b/docs/templates/hooks/before-nav-content.tpl
new file mode 100644
index 000000000..616a41041
--- /dev/null
+++ b/docs/templates/hooks/before-nav-content.tpl
@@ -0,0 +1,5 @@
+
+<div class="stork-wrapper">
+	<input data-stork="emanote-search" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
+	<div data-stork="emanote-search-output" class="stork-output"></div>
+</div>
diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
new file mode 100644
index 000000000..8ea0a36d3
--- /dev/null
+++ b/docs/templates/hooks/more-head.tpl
@@ -0,0 +1,32 @@
+<!-- Stork search styling, scripts are at end of <body> -->
+<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
+<style>
+/* Applies changes to Stork search (https://stork-search.net/)
+   to make it fit in with Emanote's styling.
+*/
+
+.stork-wrapper .stork-output {
+  margin-top: 0;
+  border-radius: 0;
+  position: sticky;
+}
+
+.stork-wrapper .stork-message {
+  padding: 0.5rem 1rem;
+  border-radius: 0;
+  margin-top: 1px;
+  margin-bottom: 1px;
+  border-color: rgba(99,102,241,var(--tw-border-opacity));
+}
+
+.stork-wrapper .stork-close-button {
+  top: 0;
+  margin: 0.8em 0.6em;
+}
+
+.stork-wrapper .stork-close-button svg {
+  top: unset;
+  margin-left: auto;
+  margin-right: auto;
+}
+</style>

From 22965ee3f0551c0e28a2fd1191bcfcbab54e667d Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 15:44:16 +0200
Subject: [PATCH 05/54] Removed search.md

---
 docs/tips/search.md | 18 ------------------
 1 file changed, 18 deletions(-)
 delete mode 100644 docs/tips/search.md

diff --git a/docs/tips/search.md b/docs/tips/search.md
deleted file mode 100644
index 48767b754..000000000
--- a/docs/tips/search.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Search
-
-**These are temporary private notes!** TODO: Cleanup and write actual docs
-
-```bash
-emanote gen result -L 'docs;default'
-
-cd result
-
-echo '[input]' > stork.toml
-echo 'files = [' >> stork.toml
-rg -g '!-/' '<title>' -A1 | grep html- | sed 's/\(.*.html\)-  *\(.*\)/  {path = "\1", url="\1", title = """\2"""},/' >> stork.toml
-echo ']' >> stork.toml
-
-stork build --input stork.toml --output stork.st
-
-podman run --rm -it -v $PWD:/usr/share/nginx/html:Z -p 8080:80 nginx:alpine
-```

From a5a67a2b1ac65b2c36993a30bf77fad9fc6293e3 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 16:22:59 +0200
Subject: [PATCH 06/54] Style so search results overlap notes

---
 docs/templates/hooks/more-head.tpl | 45 ++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
index 8ea0a36d3..c20bdfa51 100644
--- a/docs/templates/hooks/more-head.tpl
+++ b/docs/templates/hooks/more-head.tpl
@@ -9,13 +9,50 @@
   margin-top: 0;
   border-radius: 0;
   position: sticky;
+  border-color: rgba(99,102,241,var(--tw-border-opacity));
+  --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
+}
+
+@media (min-width: 768px) {
+  .stork-wrapper .stork-output {
+    top: 0;
+    left: 100%;
+    position: absolute;
+    width: calc(768px - 12rem);
+  }
+}
+
+@media (min-width: 1024px) {
+  .stork-wrapper .stork-output {
+    top: 0;
+    left: 100%;
+    position: absolute;
+    width: calc(1024px - 12rem);
+  }
+}
+
+@media (min-width: 1280px) {
+  .stork-wrapper .stork-output {
+    top: 0;
+    left: 100%;
+    position: absolute;
+    width: calc(1280px - 16rem);
+  }
+}
+
+@media (min-width: 1536px) {
+  .stork-wrapper .stork-output {
+    top: 0;
+    left: 100%;
+    position: absolute;
+    width: calc(1536px - 16rem);
+  }
 }
 
 .stork-wrapper .stork-message {
   padding: 0.5rem 1rem;
   border-radius: 0;
-  margin-top: 1px;
-  margin-bottom: 1px;
   border-color: rgba(99,102,241,var(--tw-border-opacity));
 }
 
@@ -29,4 +66,8 @@
   margin-left: auto;
   margin-right: auto;
 }
+
+nav#sidebar {
+  z-index: 100;
+}
 </style>

From b49c00b506a66fe9a908dc6f7070b0ac022f4a83 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 16:28:34 +0200
Subject: [PATCH 07/54] Removed duplicate stylings

---
 docs/templates/hooks/more-head.tpl | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
index c20bdfa51..93f7d4cfb 100644
--- a/docs/templates/hooks/more-head.tpl
+++ b/docs/templates/hooks/more-head.tpl
@@ -25,27 +25,18 @@
 
 @media (min-width: 1024px) {
   .stork-wrapper .stork-output {
-    top: 0;
-    left: 100%;
-    position: absolute;
     width: calc(1024px - 12rem);
   }
 }
 
 @media (min-width: 1280px) {
   .stork-wrapper .stork-output {
-    top: 0;
-    left: 100%;
-    position: absolute;
     width: calc(1280px - 16rem);
   }
 }
 
 @media (min-width: 1536px) {
   .stork-wrapper .stork-output {
-    top: 0;
-    left: 100%;
-    position: absolute;
     width: calc(1536px - 16rem);
   }
 }

From 809d5ddd9f391eefac69f47b24b9c404a079231c Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 16:38:16 +0200
Subject: [PATCH 08/54] Cleaned up JS a little

---
 docs/templates/hooks/after-body.tpl | 22 ++++++++++++----------
 docs/templates/hooks/more-head.tpl  |  2 +-
 2 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/docs/templates/hooks/after-body.tpl b/docs/templates/hooks/after-body.tpl
index bb840c801..b76bb6668 100644
--- a/docs/templates/hooks/after-body.tpl
+++ b/docs/templates/hooks/after-body.tpl
@@ -1,16 +1,18 @@
-<!-- Stork search scripts, styling are at end of <head> -->
+<!-- Stork search scripts, styling added in more-head.tpl -->
 <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
 <script>
+(function () {
+	const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
+	const baseUrl = document.querySelector('head > base').getAttribute('href') || '/';
+	const indexUrl = baseUrl + 'stork.st';
+
 	if (document.readyState !== "complete") {
-		stork.register(
-			'emanote-search', // has to match input[data-stork] attribute value
-			(document.querySelector('head > base').getAttribute('href') || '')+'stork.st'
-		);
+		stork.register(indexName, indexUrl);
 	} else {
-		stork.downloadIndex(
-			'emanote-search', // has to match input[data-stork] attribute value
-			(document.querySelector('head > base').getAttribute('href') || '')+'stork.st',
-			{ forceOverwrite: true } // needed for Ema's hot-reload
-		);
+		// In case of Ema hot-reload: only update index
+		stork.downloadIndex(indexName, indexUrl, {
+			forceOverwrite: true
+		});
 	}
+})();
 </script>
diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
index 93f7d4cfb..b206e5274 100644
--- a/docs/templates/hooks/more-head.tpl
+++ b/docs/templates/hooks/more-head.tpl
@@ -1,4 +1,4 @@
-<!-- Stork search styling, scripts are at end of <body> -->
+<!-- Stork search styling, scripts added in after-body.tpl -->
 <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
 <style>
 /* Applies changes to Stork search (https://stork-search.net/)

From 79f23c4c9787177b682b1052638fde1d788b8473 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 16:57:48 +0200
Subject: [PATCH 09/54] Added shift 1px so borders align

---
 docs/templates/hooks/more-head.tpl | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
index b206e5274..5d9919fa8 100644
--- a/docs/templates/hooks/more-head.tpl
+++ b/docs/templates/hooks/more-head.tpl
@@ -17,10 +17,14 @@
 @media (min-width: 768px) {
   .stork-wrapper .stork-output {
     top: 0;
-    left: 100%;
+    left: calc(100% - 1px);
     position: absolute;
     width: calc(768px - 12rem);
   }
+
+  .stork-wrapper {
+    margin-right: -1px;
+  }
 }
 
 @media (min-width: 1024px) {

From 1a1c9e805183fa6a4be8a46ed48b1a1f8fada97e Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 30 Jul 2022 19:40:02 +0200
Subject: [PATCH 10/54] Get baseUrl from Emanote instead of <base>

---
 docs/templates/hooks/after-body.tpl | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/docs/templates/hooks/after-body.tpl b/docs/templates/hooks/after-body.tpl
index b76bb6668..f1ff9f36c 100644
--- a/docs/templates/hooks/after-body.tpl
+++ b/docs/templates/hooks/after-body.tpl
@@ -1,9 +1,11 @@
 <!-- Stork search scripts, styling added in more-head.tpl -->
 <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
-<script>
+<ema:metadata>
+<with var="template">
+<script data-emanote-base-url="${value:baseUrl}">
 (function () {
 	const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
-	const baseUrl = document.querySelector('head > base').getAttribute('href') || '/';
+	const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
 	const indexUrl = baseUrl + 'stork.st';
 
 	if (document.readyState !== "complete") {
@@ -16,3 +18,5 @@
 	}
 })();
 </script>
+</with>
+</ema:metadata>

From b47037361fdbf675649aa2b0a55d747cbcbc683d Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 31 Jul 2022 15:12:31 +0200
Subject: [PATCH 11/54] Moved JS & CSS to default/index.yaml

---
 default/index.yaml                            | 102 ++++++++++++++++++
 default/templates/base.tpl                    |   1 -
 default/templates/layouts/book.tpl            |   5 +-
 default/templates/layouts/note.tpl            |   5 +-
 docs/templates/hooks/after-body.tpl           |  22 ----
 docs/templates/hooks/more-head.tpl            |  68 ------------
 ...{before-nav-content.tpl => search-box.tpl} |   1 -
 7 files changed, 106 insertions(+), 98 deletions(-)
 delete mode 100644 docs/templates/hooks/after-body.tpl
 delete mode 100644 docs/templates/hooks/more-head.tpl
 rename docs/templates/hooks/{before-nav-content.tpl => search-box.tpl} (99%)

diff --git a/default/index.yaml b/default/index.yaml
index d13765034..aabb9ec64 100644
--- a/default/index.yaml
+++ b/default/index.yaml
@@ -117,6 +117,108 @@ js:
     </script>
     <script async="" id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
 
+  stork-search-base: |
+    <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
+    <!-- Stork-search styling base stylings -->
+    <style>
+      .stork-wrapper .stork-output {
+        margin-top: 0;
+        border-radius: 0;
+        position: sticky;
+        border-color: rgba(99,102,241,var(--tw-border-opacity));
+        --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
+        box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
+      }
+
+      .stork-wrapper .stork-message {
+        padding: 0.5rem 1rem;
+        border-radius: 0;
+        border-color: rgba(99,102,241,var(--tw-border-opacity));
+      }
+
+      .stork-wrapper .stork-close-button {
+        top: 0;
+        margin: 0.8em 0.6em;
+      }
+
+      .stork-wrapper .stork-close-button svg {
+        top: unset;
+        margin-left: auto;
+        margin-right: auto;
+      }
+    </style>
+    <!-- Stork search scripts, styling added in more-head.tpl -->
+    <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
+    <ema:metadata>
+      <with var="template">
+        <script data-emanote-base-url="${value:baseUrl}">
+          (function() {
+            const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
+            const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
+            const indexUrl = baseUrl + 'stork.st';
+            if (document.readyState !== 'complete') {
+              window.addEventListener('load', function() {
+                stork.register(indexName, indexUrl);
+              });
+            } else {
+              stork.register(indexName, indexUrl, {forceOverwrite: true});
+            }
+          })();
+        </script>
+      </with>
+    </ema:metadata>
+  stork-search-book: |
+    <snippet var="js.stork-search-base"/>
+    <!-- Stork-search styling, specific to Emanote's book layout -->
+    <style>
+      @media (min-width: 768px) {
+        .stork-wrapper .stork-output {
+          top: 0;
+          left: calc(100% - 1px);
+          position: absolute;
+          width: calc(768px - 12rem);
+        }
+
+        .stork-wrapper {
+          margin-right: -1px;
+        }
+      }
+
+      @media (min-width: 1024px) {
+        .stork-wrapper .stork-output {
+          width: calc(1024px - 12rem);
+        }
+      }
+
+      @media (min-width: 1280px) {
+        .stork-wrapper .stork-output {
+          width: calc(1280px - 16rem);
+        }
+      }
+
+      @media (min-width: 1536px) {
+        .stork-wrapper .stork-output {
+          width: calc(1536px - 16rem);
+        }
+      }
+
+      nav#sidebar {
+        z-index: 100;
+      }
+    </style>
+  stork-search-note: |
+    <snippet var="js.stork-search-base"/>
+    <!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
+    <style>
+      .stork-wrapper .stork-output {
+        position: absolute;
+      }
+    </style>
+  # This adds styling for the "default layout", and should
+  # be used if you rely on Emanote's default layout.
+  stork-search: |
+    <snippet var="js.stork-search-book"/>
+
 emanote:
   # Whether to automatically treat folder notes as a folgezettel parent of its contents
   folder-folgezettel: true
diff --git a/default/templates/base.tpl b/default/templates/base.tpl
index 6eabbabeb..7a85417ba 100644
--- a/default/templates/base.tpl
+++ b/default/templates/base.tpl
@@ -44,7 +44,6 @@
 
 <body class="${bodyClass}">
   <body-main />
-  <apply template="/templates/hooks/after-body" />
 </body>
 
 </html>
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index affca4023..25cbe0bd8 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -10,7 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <apply template="hooks/before-nav-content" />
+          <apply template="hooks/search-box" />
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
@@ -58,12 +58,10 @@
             </ema:route-tree>
 
           </div>
-          <apply template="hooks/after-nav-content" />
         </nav>
 
         <!-- Main body column -->
         <div class="flex-1 w-full overflow-x-auto bg-white">
-          <apply template="/templates/hooks/before-main" />
           <main class="px-4 py-4">
             <apply template="components/note-title" />
             <apply template="components/note-body" />
@@ -74,7 +72,6 @@
             <apply template="components/metadata" />
             <apply template="/templates/hooks/note-end" />
           </main>
-          <apply template="/templates/hooks/after-main" />
         </div>
       </div>
       <apply template="components/footer" />
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index ca301e626..e9fc567fc 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -6,12 +6,13 @@
 
 <apply template="base">
   <bind tag="head-main">
-    <link rel="stylesheet" href="${ema:emanoteStaticLayerUrl}/inverted-tree.css" />
+    <link rel="stylesheet" href="/${ema:emanoteStaticLayerUrl}/inverted-tree.css" />
   </bind>
   <bind tag="body-main">
     <div class="${containerClass}">
       <div class="mt-2 md:mt-4">
         <apply template="components/note-uptree" />
+        <apply template="hooks/search-box" />
         <div class="md:shadow-2xl md:mb-8">
           <div class="flex-1 w-full overflow-x-auto bg-white">
             <main class="px-4 py-4">
@@ -30,4 +31,4 @@
       </div>
     </div>
   </bind>
-</apply>
\ No newline at end of file
+</apply>
diff --git a/docs/templates/hooks/after-body.tpl b/docs/templates/hooks/after-body.tpl
deleted file mode 100644
index f1ff9f36c..000000000
--- a/docs/templates/hooks/after-body.tpl
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Stork search scripts, styling added in more-head.tpl -->
-<script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
-<ema:metadata>
-<with var="template">
-<script data-emanote-base-url="${value:baseUrl}">
-(function () {
-	const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
-	const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
-	const indexUrl = baseUrl + 'stork.st';
-
-	if (document.readyState !== "complete") {
-		stork.register(indexName, indexUrl);
-	} else {
-		// In case of Ema hot-reload: only update index
-		stork.downloadIndex(indexName, indexUrl, {
-			forceOverwrite: true
-		});
-	}
-})();
-</script>
-</with>
-</ema:metadata>
diff --git a/docs/templates/hooks/more-head.tpl b/docs/templates/hooks/more-head.tpl
deleted file mode 100644
index 5d9919fa8..000000000
--- a/docs/templates/hooks/more-head.tpl
+++ /dev/null
@@ -1,68 +0,0 @@
-<!-- Stork search styling, scripts added in after-body.tpl -->
-<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
-<style>
-/* Applies changes to Stork search (https://stork-search.net/)
-   to make it fit in with Emanote's styling.
-*/
-
-.stork-wrapper .stork-output {
-  margin-top: 0;
-  border-radius: 0;
-  position: sticky;
-  border-color: rgba(99,102,241,var(--tw-border-opacity));
-  --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
-  box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
-}
-
-@media (min-width: 768px) {
-  .stork-wrapper .stork-output {
-    top: 0;
-    left: calc(100% - 1px);
-    position: absolute;
-    width: calc(768px - 12rem);
-  }
-
-  .stork-wrapper {
-    margin-right: -1px;
-  }
-}
-
-@media (min-width: 1024px) {
-  .stork-wrapper .stork-output {
-    width: calc(1024px - 12rem);
-  }
-}
-
-@media (min-width: 1280px) {
-  .stork-wrapper .stork-output {
-    width: calc(1280px - 16rem);
-  }
-}
-
-@media (min-width: 1536px) {
-  .stork-wrapper .stork-output {
-    width: calc(1536px - 16rem);
-  }
-}
-
-.stork-wrapper .stork-message {
-  padding: 0.5rem 1rem;
-  border-radius: 0;
-  border-color: rgba(99,102,241,var(--tw-border-opacity));
-}
-
-.stork-wrapper .stork-close-button {
-  top: 0;
-  margin: 0.8em 0.6em;
-}
-
-.stork-wrapper .stork-close-button svg {
-  top: unset;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-nav#sidebar {
-  z-index: 100;
-}
-</style>
diff --git a/docs/templates/hooks/before-nav-content.tpl b/docs/templates/hooks/search-box.tpl
similarity index 99%
rename from docs/templates/hooks/before-nav-content.tpl
rename to docs/templates/hooks/search-box.tpl
index 616a41041..189de0cf8 100644
--- a/docs/templates/hooks/before-nav-content.tpl
+++ b/docs/templates/hooks/search-box.tpl
@@ -1,4 +1,3 @@
-
 <div class="stork-wrapper">
 	<input data-stork="emanote-search" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
 	<div data-stork="emanote-search-output" class="stork-output"></div>

From 69a26055e32fc90f44d78912a66792c660adf7c2 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 31 Jul 2022 15:32:31 +0200
Subject: [PATCH 12/54] Add search snippets to docs

---
 docs/demo/neuron-layout.md | 4 ++++
 docs/index.yaml            | 1 +
 2 files changed, 5 insertions(+)

diff --git a/docs/demo/neuron-layout.md b/docs/demo/neuron-layout.md
index 4e83522f5..81ed2ae8d 100644
--- a/docs/demo/neuron-layout.md
+++ b/docs/demo/neuron-layout.md
@@ -1,6 +1,10 @@
 ---
 template:
   name: /templates/layouts/note
+page:
+  headHtml: |
+    <snippet var="js.prism" />
+    <snippet var="js.stork-search-note" />
 ---
 
 # Neuron-like layout
diff --git a/docs/index.yaml b/docs/index.yaml
index 13688aa6b..9272d7fc4 100644
--- a/docs/index.yaml
+++ b/docs/index.yaml
@@ -5,3 +5,4 @@ page:
   siteTitle: Emanote
   headHtml: |
     <snippet var="js.prism" />
+    <snippet var="js.stork-search" />

From a6c915d189ac24ae72e17d99d66ce5e2f167aa04 Mon Sep 17 00:00:00 2001
From: "kalle (jag)" <kalle.f8@proton.me>
Date: Sun, 31 Jul 2022 15:19:15 +0200
Subject: [PATCH 13/54] Update default/templates/layouts/note.tpl

---
 default/templates/layouts/note.tpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index e9fc567fc..ba7ed83ac 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -6,7 +6,7 @@
 
 <apply template="base">
   <bind tag="head-main">
-    <link rel="stylesheet" href="/${ema:emanoteStaticLayerUrl}/inverted-tree.css" />
+    <link rel="stylesheet" href="${ema:emanoteStaticLayerUrl}/inverted-tree.css" />
   </bind>
   <bind tag="body-main">
     <div class="${containerClass}">

From b3489fffbd6d9694a95a7b81f2f155783d5867c5 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 31 Jul 2022 15:42:47 +0200
Subject: [PATCH 14/54] Moved from hook to component, and enable via snippet

---
 default/index.yaml                                           | 5 +++++
 .../templates/components/stork-search.tpl                    | 3 ++-
 default/templates/layouts/book.tpl                           | 2 +-
 default/templates/layouts/note.tpl                           | 2 +-
 4 files changed, 9 insertions(+), 3 deletions(-)
 rename docs/templates/hooks/search-box.tpl => default/templates/components/stork-search.tpl (73%)

diff --git a/default/index.yaml b/default/index.yaml
index aabb9ec64..6e1076c57 100644
--- a/default/index.yaml
+++ b/default/index.yaml
@@ -121,6 +121,11 @@ js:
     <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
     <!-- Stork-search styling base stylings -->
     <style>
+      .stork-wrapper.hidden {
+        /* undo the hidden styling, in effect enabling the search box */
+        display: block;
+      }
+
       .stork-wrapper .stork-output {
         margin-top: 0;
         border-radius: 0;
diff --git a/docs/templates/hooks/search-box.tpl b/default/templates/components/stork-search.tpl
similarity index 73%
rename from docs/templates/hooks/search-box.tpl
rename to default/templates/components/stork-search.tpl
index 189de0cf8..08cbbcefa 100644
--- a/docs/templates/hooks/search-box.tpl
+++ b/default/templates/components/stork-search.tpl
@@ -1,4 +1,5 @@
-<div class="stork-wrapper">
+<!-- the "hidden" is disabled via "js.stork-search" snippet -->
+<div class="stork-wrapper hidden">
 	<input data-stork="emanote-search" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
 	<div data-stork="emanote-search-output" class="stork-output"></div>
 </div>
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 25cbe0bd8..9900bc7f6 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -10,7 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <apply template="hooks/search-box" />
+          <apply template="components/stork-search" />
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index ba7ed83ac..f645a97b7 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -12,7 +12,7 @@
     <div class="${containerClass}">
       <div class="mt-2 md:mt-4">
         <apply template="components/note-uptree" />
-        <apply template="hooks/search-box" />
+        <apply template="components/stork-search" />
         <div class="md:shadow-2xl md:mb-8">
           <div class="flex-1 w-full overflow-x-auto bg-white">
             <main class="px-4 py-4">

From 64cc2f1278da2dcff5f095404a5e54dddfc2b6f7 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 31 Jul 2022 16:05:06 +0200
Subject: [PATCH 15/54] Fixed borders

---
 default/index.yaml | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/default/index.yaml b/default/index.yaml
index 6e1076c57..1a61ddb3e 100644
--- a/default/index.yaml
+++ b/default/index.yaml
@@ -176,7 +176,18 @@ js:
     <snippet var="js.stork-search-base"/>
     <!-- Stork-search styling, specific to Emanote's book layout -->
     <style>
+      .stork-wrapper > input {
+        border-top-color: rgba(209,213,219,var(--tw-border-opacity));
+        border-left-color: transparent;
+        border-right-color: transparent;
+      }
+
       @media (min-width: 768px) {
+        .stork-wrapper > input {
+          border-top-color: transparent;
+          border-right-color: rgba(209,213,219,var(--tw-border-opacity));
+        }
+
         .stork-wrapper .stork-output {
           top: 0;
           left: calc(100% - 1px);
@@ -218,6 +229,12 @@ js:
       .stork-wrapper .stork-output {
         position: absolute;
       }
+
+      .stork-wrapper > input {
+        border-top-color: transparent;
+        border-left-color: transparent;
+        border-right-color: transparent;
+      }
     </style>
   # This adds styling for the "default layout", and should
   # be used if you rely on Emanote's default layout.

From ba3363f15d7c8565e75f183044095297535e33ce Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 16:01:20 -0400
Subject: [PATCH 16/54] Add stork to nix shell

---
 flake.nix | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flake.nix b/flake.nix
index a2f082f36..4aedbcc63 100644
--- a/flake.nix
+++ b/flake.nix
@@ -29,7 +29,8 @@
           buildTools = hp: {
             inherit (pkgs)
               treefmt
-              nixpkgs-fmt;
+              nixpkgs-fmt
+              stork;
             inherit (hp)
               cabal-fmt
               ormolu;

From 12cf3a129da289607126655517a2947f3381ac93 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 17:32:19 -0400
Subject: [PATCH 17/54] Build stork.st index in Haskell

We just add a route for Stork index, and let Haskell build it behind the
scenes. Performance and correctness is 100% yet.
---
 default/index.yaml                   |  9 ++--
 emanote.cabal                        |  3 ++
 flake.lock                           |  8 ++--
 flake.nix                            |  2 +-
 src/Emanote/Route/SiteRoute/Class.hs |  1 +
 src/Emanote/Route/SiteRoute/Type.hs  |  9 ++++
 src/Emanote/View/Stork.hs            | 67 ++++++++++++++++++++++++++++
 src/Emanote/View/Template.hs         | 28 +++++++-----
 8 files changed, 105 insertions(+), 22 deletions(-)
 create mode 100644 src/Emanote/View/Stork.hs

diff --git a/default/index.yaml b/default/index.yaml
index 1a61ddb3e..3184af9bf 100644
--- a/default/index.yaml
+++ b/default/index.yaml
@@ -15,7 +15,7 @@ template:
   # Layout specific settings
   layout:
     # For base.tpl
-    base:  
+    base:
       # The class to apply for <body> element when using base.tpl (used by note and book layouts)
       bodyClass: bg-gray-400 overflow-y-scroll
     note:
@@ -52,10 +52,10 @@ pandoc:
     emanote:inline-tag:a/red/tag: bg-red-100
     emanote:placeholder-message: text-gray-400 border-t-2 inline-block pt-0.5
     emanote:error: text-l bg-red-100 p-2 border-2 border-black m-2 font-mono
-    emanote:error:aside: font-mono align-top text-xs mr-1 tracking-tighter opacity-50 hover:opacity-100 
+    emanote:error:aside: font-mono align-top text-xs mr-1 tracking-tighter opacity-50 hover:opacity-100
     # You can also add your own class -> style mappings. We provide a sample below.
     sticky-note: px-3 py-1 rounded shadow bg-yellow-100 mx-2 transform -skew-y-1 scale-95 hover:scale-100 hover:border-yellow-400 hover:shadow-lg border-t-8 border-yellow-200 mb-8 mt-8
-    note: p-2 mb-3 rounded shadow bg-gray-100 w-full float-none md:float-right md:w-1/2 md:clear-both 
+    note: p-2 mb-3 rounded shadow bg-gray-100 w-full float-none md:float-right md:w-1/2 md:clear-both
     highlight-block: px-3 py-1 mb-3 rounded bg-${theme}-100 hover:border-${theme}-400 hover:shadow border-t-8 border-${theme}-200
     highlight-inline: bg-yellow-200 px-2 py-0.5 rounded-xl
     center: flex justify-center items-center mx-auto
@@ -160,7 +160,7 @@ js:
           (function() {
             const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
             const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
-            const indexUrl = baseUrl + 'stork.st';
+            const indexUrl = baseUrl + '-/stork.st';
             if (document.readyState !== 'complete') {
               window.addEventListener('load', function() {
                 stork.register(indexName, indexUrl);
@@ -244,4 +244,3 @@ js:
 emanote:
   # Whether to automatically treat folder notes as a folgezettel parent of its contents
   folder-folgezettel: true
-
diff --git a/emanote.cabal b/emanote.cabal
index e25b21276..34f01b277 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -126,6 +126,7 @@ common library-common
     , pandoc-types
     , parsec
     , path-tree              >=0.2
+    , process-extras
     , profunctors
     , relude                 >=1.0
     , shower
@@ -135,6 +136,7 @@ common library-common
     , tailwind               >=0.3
     , text
     , time
+    , tomland
     , unionmount             >=0.2
     , unliftio
     , unordered-containers
@@ -196,6 +198,7 @@ library
     Emanote.View.Common
     Emanote.View.Export
     Emanote.View.LiveServerFiles
+    Emanote.View.Stork
     Emanote.View.TagIndex
     Emanote.View.TaskIndex
     Emanote.View.Template
diff --git a/flake.lock b/flake.lock
index 4eacc51cd..bbe8e1374 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,16 +3,16 @@
     "ema": {
       "flake": false,
       "locked": {
-        "lastModified": 1658331162,
-        "narHash": "sha256-13PYisWFUbrS+HOegqBMA1SRuG5fZNOVXccAlfOIylY=",
+        "lastModified": 1659387161,
+        "narHash": "sha256-7RZT1J6E2GSomxNMBi6VX+0l0xa9uKj0caaUJ2wSK1w=",
         "owner": "srid",
         "repo": "ema",
-        "rev": "67da7235a14e93e468112bb8d2217fb0e092304b",
+        "rev": "fcba11484741a211633a58d995baa749ca473c50",
         "type": "github"
       },
       "original": {
         "owner": "srid",
-        "ref": "multisite",
+        "ref": "multisite-siteOutputIO",
         "repo": "ema",
         "type": "github"
       }
diff --git a/flake.nix b/flake.nix
index 4aedbcc63..ed38b769d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -10,7 +10,7 @@
     haskell-flake.url = "github:srid/haskell-flake";
 
     # Haskell dependency overrides
-    ema.url = "github:srid/ema/multisite";
+    ema.url = "github:srid/ema/multisite-siteOutputIO";
     ema.flake = false;
     tailwind-haskell.url = "github:srid/tailwind-haskell/master";
     tailwind-haskell.inputs.nixpkgs.follows = "nixpkgs";
diff --git a/src/Emanote/Route/SiteRoute/Class.hs b/src/Emanote/Route/SiteRoute/Class.hs
index 5907c3b9c..367184274 100644
--- a/src/Emanote/Route/SiteRoute/Class.hs
+++ b/src/Emanote/Route/SiteRoute/Class.hs
@@ -60,6 +60,7 @@ emanoteGeneratableRoutes model =
                       NE.filter (not . null) $ NE.inits tagPath
          in VirtualRoute_Index :
             VirtualRoute_Export :
+            VirtualRoute_StorkIndex :
             VirtualRoute_TaskIndex :
             (VirtualRoute_TagIndex <$> toList tagPaths)
    in htmlRoutes
diff --git a/src/Emanote/Route/SiteRoute/Type.hs b/src/Emanote/Route/SiteRoute/Type.hs
index 0dfe10548..75297bee8 100644
--- a/src/Emanote/Route/SiteRoute/Type.hs
+++ b/src/Emanote/Route/SiteRoute/Type.hs
@@ -24,6 +24,7 @@ data VirtualRoute
   = VirtualRoute_Index
   | VirtualRoute_TagIndex [HT.TagNode]
   | VirtualRoute_Export
+  | VirtualRoute_StorkIndex
   | VirtualRoute_TaskIndex
   deriving stock (Eq, Ord, Show, Generic)
   deriving anyclass (ToJSON)
@@ -65,6 +66,7 @@ decodeVirtualRoute fp =
   (VirtualRoute_Index <$ decodeIndexR fp)
     <|> (VirtualRoute_TagIndex <$> decodeTagIndexR fp)
     <|> (VirtualRoute_Export <$ decodeExportR fp)
+    <|> (VirtualRoute_StorkIndex <$ decodeStorkIndexR fp)
     <|> (VirtualRoute_TaskIndex <$ decodeTaskIndexR fp)
 
 decodeIndexR :: FilePath -> Maybe ()
@@ -77,6 +79,11 @@ decodeExportR fp = do
   "-" :| ["export.json"] <- R.unRoute <$> R.decodeAnyRoute fp
   pass
 
+decodeStorkIndexR :: FilePath -> Maybe ()
+decodeStorkIndexR fp = do
+  "-" :| ["stork.st"] <- R.unRoute <$> R.decodeAnyRoute fp
+  pass
+
 decodeTagIndexR :: FilePath -> Maybe [HT.TagNode]
 decodeTagIndexR fp = do
   "-" :| "tags" : tagPath <- pure $ R.unRoute $ R.decodeHtmlRoute fp
@@ -97,6 +104,8 @@ encodeVirtualRoute = \case
     R.encodeRoute $ R.R @() @'Ext.Html $ "-" :| ["all"]
   VirtualRoute_Export ->
     R.encodeRoute $ R.R @Ext.SourceExt @'Ext.AnyExt $ "-" :| ["export.json"]
+  VirtualRoute_StorkIndex ->
+    R.encodeRoute $ R.R @Ext.SourceExt @'Ext.AnyExt $ "-" :| ["stork.st"]
   VirtualRoute_TaskIndex ->
     R.encodeRoute $ R.R @() @'Ext.Html $ "-" :| ["tasks"]
 
diff --git a/src/Emanote/View/Stork.hs b/src/Emanote/View/Stork.hs
new file mode 100644
index 000000000..ce100e240
--- /dev/null
+++ b/src/Emanote/View/Stork.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Emanote.View.Stork
+  ( renderStorkIndex,
+  )
+where
+
+import Control.Monad.Logger (MonadLoggerIO)
+import Data.IxSet.Typed qualified as Ix
+import Emanote.Model (Model)
+import Emanote.Model.Note qualified as N
+import Emanote.Model.Title qualified as Tit
+import Emanote.Model.Type qualified as M
+import Emanote.Prelude (log, logW)
+import Emanote.Route qualified as R
+import Emanote.Source.Loc qualified as Loc
+import Optics.Core ((^.))
+import Relude
+import System.FilePath ((</>))
+import System.Process.ByteString (readProcessWithExitCode)
+import System.Which (staticWhich)
+import Toml
+
+storkBin :: FilePath
+storkBin = $(staticWhich "stork")
+
+data Input = Input
+  { inputFiles :: [File]
+  }
+  deriving stock (Eq, Show)
+
+data File = File
+  { filePath :: FilePath,
+    fileUrl :: Text,
+    fileTitle :: Text
+  }
+  deriving stock (Eq, Show)
+
+fileCodec :: TomlCodec File
+fileCodec =
+  File
+    <$> Toml.string "path" .= filePath
+    <*> Toml.text "url" .= fileUrl
+    <*> Toml.text "title" .= fileTitle
+
+inputCodec :: TomlCodec Input
+inputCodec =
+  Input
+    <$> Toml.list fileCodec "input.files" .= inputFiles
+
+renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => Model -> m LByteString
+renderStorkIndex model = do
+  -- TODO: this should retrieve from cache if model hasn't changed
+  logW "Generating search index using Stork (this may be expensive)"
+  let storkToml = Toml.encode inputCodec $ Input $ storkFiles model
+  (_, !index, _) <- liftIO $ readProcessWithExitCode storkBin ["build", "-t", "--input", "-", "--output", "-"] (encodeUtf8 storkToml)
+  log "Done generating Stork index"
+  pure $ toLazy index
+
+storkFiles :: Model -> [File]
+storkFiles model =
+  let baseDir = Loc.locPath . Loc.primaryLayer $ model ^. M.modelLayers
+   in Ix.toList (model ^. M.modelNotes) <&> \note ->
+        File
+          ((baseDir </>) $ R.withLmlRoute R.encodeRoute $ note ^. N.noteRoute)
+          (toText $ R.encodeRoute $ N.noteHtmlRoute note)
+          (Tit.toPlain $ note ^. N.noteTitle)
diff --git a/src/Emanote/View/Template.hs b/src/Emanote/View/Template.hs
index db743d12e..7c06fbaa0 100644
--- a/src/Emanote/View/Template.hs
+++ b/src/Emanote/View/Template.hs
@@ -1,5 +1,6 @@
 module Emanote.View.Template (emanoteSiteOutput, render) where
 
+import Control.Monad.Logger (MonadLoggerIO)
 import Data.Aeson.Types qualified as Aeson
 import Data.List (partition)
 import Data.List.NonEmpty qualified as NE
@@ -22,6 +23,7 @@ import Emanote.Route.SiteRoute qualified as SR
 import Emanote.Route.SiteRoute.Class (indexRoute)
 import Emanote.View.Common qualified as C
 import Emanote.View.Export (renderGraphExport)
+import Emanote.View.Stork (renderStorkIndex)
 import Emanote.View.TagIndex qualified as TagIndex
 import Emanote.View.TaskIndex qualified as TaskIndex
 import Heist qualified as H
@@ -37,10 +39,10 @@ import Relude
 import Text.Pandoc.Builder qualified as B
 import Text.Pandoc.Definition (Pandoc (..))
 
-emanoteSiteOutput :: Prism' FilePath SiteRoute -> ModelEma -> SR.SiteRoute -> Ema.Asset LByteString
-emanoteSiteOutput rp model' r =
+emanoteSiteOutput :: (MonadIO m, MonadLoggerIO m) => Prism' FilePath SiteRoute -> ModelEma -> SR.SiteRoute -> m (Ema.Asset LByteString)
+emanoteSiteOutput rp model' r = do
   let model = M.withRoutePrism rp model'
-   in render model r <&> fixStaticUrl
+  render model r <&> fmap fixStaticUrl
   where
     -- See the FIXME in more-head.tpl.
     fixStaticUrl :: LByteString -> LByteString
@@ -62,7 +64,7 @@ emanoteSiteOutput rp model' r =
           guard $ not $ T.null prefix
           pure prefix
 
-render :: Model -> SR.SiteRoute -> Ema.Asset LByteString
+render :: (MonadIO m, MonadLoggerIO m) => Model -> SR.SiteRoute -> m (Ema.Asset LByteString)
 render m sr =
   let setErrorPageMeta =
         MN.noteMeta .~ SData.mergeAesons (withTemplateName "/templates/error" :| [withSiteTitle "Emanote Error"])
@@ -73,14 +75,14 @@ render m sr =
                 MN.missingNote hereRoute (toText urlPath)
                   & setErrorPageMeta
                   & MN.noteTitle .~ "! Missing link"
-          Ema.AssetGenerated Ema.Html $ renderLmlHtml m note404
+          pure $ Ema.AssetGenerated Ema.Html $ renderLmlHtml m note404
         SR.SiteRoute_AmbiguousR urlPath notes -> do
           let noteAmb =
                 MN.ambiguousNoteURL urlPath notes
                   & setErrorPageMeta
                   & MN.noteTitle .~ "! Ambiguous link"
-          Ema.AssetGenerated Ema.Html $ renderLmlHtml m noteAmb
-        SR.SiteRoute_ResourceRoute r -> renderResourceRoute m r
+          pure $ Ema.AssetGenerated Ema.Html $ renderLmlHtml m noteAmb
+        SR.SiteRoute_ResourceRoute r -> pure $ renderResourceRoute m r
         SR.SiteRoute_VirtualRoute r -> renderVirtualRoute m r
 
 renderResourceRoute :: Model -> SR.ResourceRoute -> Ema.Asset LByteString
@@ -95,16 +97,18 @@ renderResourceRoute m = \case
   SR.ResourceRoute_StaticFile _ fpAbs ->
     Ema.AssetStatic fpAbs
 
-renderVirtualRoute :: Model -> SR.VirtualRoute -> Ema.Asset LByteString
+renderVirtualRoute :: (MonadIO m, MonadLoggerIO m) => Model -> SR.VirtualRoute -> m (Ema.Asset LByteString)
 renderVirtualRoute m = \case
   SR.VirtualRoute_TagIndex mtag ->
-    Ema.AssetGenerated Ema.Html $ TagIndex.renderTagIndex m mtag
+    pure $ Ema.AssetGenerated Ema.Html $ TagIndex.renderTagIndex m mtag
   SR.VirtualRoute_Index ->
-    Ema.AssetGenerated Ema.Html $ renderSRIndex m
+    pure $ Ema.AssetGenerated Ema.Html $ renderSRIndex m
   SR.VirtualRoute_Export ->
-    Ema.AssetGenerated Ema.Other $ renderGraphExport m
+    pure $ Ema.AssetGenerated Ema.Other $ renderGraphExport m
+  SR.VirtualRoute_StorkIndex ->
+    Ema.AssetGenerated Ema.Other <$> renderStorkIndex m
   SR.VirtualRoute_TaskIndex ->
-    Ema.AssetGenerated Ema.Html $ TaskIndex.renderTasks m
+    pure $ Ema.AssetGenerated Ema.Html $ TaskIndex.renderTasks m
 
 renderSRIndex :: Model -> LByteString
 renderSRIndex model = do

From b216807f212cb13561c966750625b1f4231ba3c5 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 17:37:49 -0400
Subject: [PATCH 18/54] Fix autoformat

---
 src/Emanote/View/Stork.hs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Emanote/View/Stork.hs b/src/Emanote/View/Stork.hs
index ce100e240..0157383f6 100644
--- a/src/Emanote/View/Stork.hs
+++ b/src/Emanote/View/Stork.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE TemplateHaskell #-}
 
 module Emanote.View.Stork
@@ -19,7 +20,7 @@ import Relude
 import System.FilePath ((</>))
 import System.Process.ByteString (readProcessWithExitCode)
 import System.Which (staticWhich)
-import Toml
+import Toml (TomlCodec, encode, list, string, text, (.=))
 
 storkBin :: FilePath
 storkBin = $(staticWhich "stork")

From 79f04dd0caa0c1317a4186901c1e546f9f089cfc Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 17:42:42 -0400
Subject: [PATCH 19/54] Fix hlint warning

---
 src/Emanote/View/Stork.hs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Emanote/View/Stork.hs b/src/Emanote/View/Stork.hs
index 0157383f6..0301b7468 100644
--- a/src/Emanote/View/Stork.hs
+++ b/src/Emanote/View/Stork.hs
@@ -25,7 +25,7 @@ import Toml (TomlCodec, encode, list, string, text, (.=))
 storkBin :: FilePath
 storkBin = $(staticWhich "stork")
 
-data Input = Input
+newtype Input = Input
   { inputFiles :: [File]
   }
   deriving stock (Eq, Show)

From 62ff42ed3f24caae594258c52f74433bed928ae4 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 17:53:22 -0400
Subject: [PATCH 20/54] Up ver

---
 emanote.cabal | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emanote.cabal b/emanote.cabal
index 34f01b277..c4543d94e 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               emanote
-version:            0.6.19.0
+version:            0.6.20.0
 license:            AGPL-3.0-only
 copyright:          2021 Sridhar Ratnakumar
 maintainer:         srid@srid.ca

From 5dc1c38ab2767550e26838b6ddfeefffe1f3bfae Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 1 Aug 2022 17:53:28 -0400
Subject: [PATCH 21/54] nix: add stork as runtime dependency

---
 flake.nix | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/flake.nix b/flake.nix
index ed38b769d..4e099ee61 100644
--- a/flake.nix
+++ b/flake.nix
@@ -48,6 +48,8 @@
             inherit (inputs'.tailwind-haskell.packages)
               tailwind;
           };
+          modifier = drv: with pkgs.haskell.lib;
+            addBuildDepends drv [ pkgs.stork ];
         };
         packages.test =
           pkgs.runCommand "emanote-test" { } ''

From dc82292bade41cf85b11d7758ad3888c4991a531 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Tue, 2 Aug 2022 07:20:44 +0200
Subject: [PATCH 22/54] Added missing stork-search from headHtml

---
 docs/demo/orgmode.yaml  | 1 +
 docs/resources/zk.md    | 1 +
 docs/tips/js/math.md    | 3 ++-
 docs/tips/js/mermaid.md | 3 ++-
 4 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/docs/demo/orgmode.yaml b/docs/demo/orgmode.yaml
index 26aea6e1d..025f8a4f3 100644
--- a/docs/demo/orgmode.yaml
+++ b/docs/demo/orgmode.yaml
@@ -2,3 +2,4 @@ page:
   headHtml: |
     <snippet var="js.prism" />
     <snippet var="js.mathjax" />
+    <snippet var="js.stork-search" />
diff --git a/docs/resources/zk.md b/docs/resources/zk.md
index dac791339..c627886c8 100644
--- a/docs/resources/zk.md
+++ b/docs/resources/zk.md
@@ -3,6 +3,7 @@ page:
   headHtml: |
     <snippet var="js.highlightjs" />
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/languages/ini.min.js"></script>
+    <snippet var="js.stork-search" />
 ---
 
 # zk
diff --git a/docs/tips/js/math.md b/docs/tips/js/math.md
index 9782eba25..dc53895c2 100644
--- a/docs/tips/js/math.md
+++ b/docs/tips/js/math.md
@@ -3,6 +3,7 @@ page:
   headHtml: |
     <snippet var="js.prism" />
     <snippet var="js.mathjax" />
+    <snippet var="js.stork-search" />
 
 ---
 
@@ -29,4 +30,4 @@ page:
 ## Demo
 
 When $a \ne 0$, there are two solutions to $ax^2 + bx + c = 0$ and they are
-$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
\ No newline at end of file
+$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
diff --git a/docs/tips/js/mermaid.md b/docs/tips/js/mermaid.md
index ab9810256..70e08a80f 100644
--- a/docs/tips/js/mermaid.md
+++ b/docs/tips/js/mermaid.md
@@ -3,6 +3,7 @@ page:
   headHtml: |
     <snippet var="js.highlightjs" />
     <snippet var="js.mermaid" />
+    <snippet var="js.stork-search" />
 ---
 
 # Mermaid Diagrams
@@ -89,4 +90,4 @@ gantt
     Describe gantt syntax               :after doc1, 3d
     Add gantt diagram to demo page      :20h
     Add another diagram to demo page    :48h
-```
\ No newline at end of file
+```

From b82c6ff497052cdbcf445a0efdb81335267c08dd Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Wed, 3 Aug 2022 13:51:10 -0400
Subject: [PATCH 23/54] Advance ema

---
 flake.lock | 8 ++++----
 flake.nix  | 3 ++-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/flake.lock b/flake.lock
index bbe8e1374..19ef28d41 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,16 +3,16 @@
     "ema": {
       "flake": false,
       "locked": {
-        "lastModified": 1659387161,
-        "narHash": "sha256-7RZT1J6E2GSomxNMBi6VX+0l0xa9uKj0caaUJ2wSK1w=",
+        "lastModified": 1659481510,
+        "narHash": "sha256-R8KdVEaWxeAGRuNs6FIn0Zge+E0L+mtzfNgcaflzuTA=",
         "owner": "srid",
         "repo": "ema",
-        "rev": "fcba11484741a211633a58d995baa749ca473c50",
+        "rev": "94b19ea2a04f3fa08261a8a07fefde3f117c2a74",
         "type": "github"
       },
       "original": {
         "owner": "srid",
-        "ref": "multisite-siteOutputIO",
+        "ref": "multisite",
         "repo": "ema",
         "type": "github"
       }
diff --git a/flake.nix b/flake.nix
index 4e099ee61..84c6fa582 100644
--- a/flake.nix
+++ b/flake.nix
@@ -10,7 +10,7 @@
     haskell-flake.url = "github:srid/haskell-flake";
 
     # Haskell dependency overrides
-    ema.url = "github:srid/ema/multisite-siteOutputIO";
+    ema.url = "github:srid/ema/multisite";
     ema.flake = false;
     tailwind-haskell.url = "github:srid/tailwind-haskell/master";
     tailwind-haskell.inputs.nixpkgs.follows = "nixpkgs";
@@ -42,6 +42,7 @@
               ema;
           };
           overrides = self: super: with pkgs.haskell.lib; {
+            ema = dontCheck super.ema;
             heist-emanote = dontCheck (doJailbreak (unmarkBroken super.heist-emanote)); # Tests are broken.
             ixset-typed = unmarkBroken super.ixset-typed;
             pandoc-link-context = unmarkBroken super.pandoc-link-context;

From bcd9ee7c48ee71a465fadca56794ab2f9a622c8e Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 17:04:20 +0200
Subject: [PATCH 24/54] Revert "Added missing stork-search from headHtml"

This reverts commit dc82292bade41cf85b11d7758ad3888c4991a531.
---
 docs/demo/orgmode.yaml  | 1 -
 docs/resources/zk.md    | 1 -
 docs/tips/js/math.md    | 3 +--
 docs/tips/js/mermaid.md | 3 +--
 4 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/docs/demo/orgmode.yaml b/docs/demo/orgmode.yaml
index 025f8a4f3..26aea6e1d 100644
--- a/docs/demo/orgmode.yaml
+++ b/docs/demo/orgmode.yaml
@@ -2,4 +2,3 @@ page:
   headHtml: |
     <snippet var="js.prism" />
     <snippet var="js.mathjax" />
-    <snippet var="js.stork-search" />
diff --git a/docs/resources/zk.md b/docs/resources/zk.md
index c627886c8..dac791339 100644
--- a/docs/resources/zk.md
+++ b/docs/resources/zk.md
@@ -3,7 +3,6 @@ page:
   headHtml: |
     <snippet var="js.highlightjs" />
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/languages/ini.min.js"></script>
-    <snippet var="js.stork-search" />
 ---
 
 # zk
diff --git a/docs/tips/js/math.md b/docs/tips/js/math.md
index dc53895c2..9782eba25 100644
--- a/docs/tips/js/math.md
+++ b/docs/tips/js/math.md
@@ -3,7 +3,6 @@ page:
   headHtml: |
     <snippet var="js.prism" />
     <snippet var="js.mathjax" />
-    <snippet var="js.stork-search" />
 
 ---
 
@@ -30,4 +29,4 @@ page:
 ## Demo
 
 When $a \ne 0$, there are two solutions to $ax^2 + bx + c = 0$ and they are
-$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
+$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
\ No newline at end of file
diff --git a/docs/tips/js/mermaid.md b/docs/tips/js/mermaid.md
index 70e08a80f..ab9810256 100644
--- a/docs/tips/js/mermaid.md
+++ b/docs/tips/js/mermaid.md
@@ -3,7 +3,6 @@ page:
   headHtml: |
     <snippet var="js.highlightjs" />
     <snippet var="js.mermaid" />
-    <snippet var="js.stork-search" />
 ---
 
 # Mermaid Diagrams
@@ -90,4 +89,4 @@ gantt
     Describe gantt syntax               :after doc1, 3d
     Add gantt diagram to demo page      :20h
     Add another diagram to demo page    :48h
-```
+```
\ No newline at end of file

From d7d7cb5512853cb2f03b99490fe95081fccfa03f Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 17:22:51 +0200
Subject: [PATCH 25/54] Moved back to be in templates instead of snippet

---
 default/index.yaml                            | 124 ------------------
 .../components/stork-search-js-book.tpl       |  49 +++++++
 .../components/stork-search-js-note.tpl       |  13 ++
 .../templates/components/stork-search-js.tpl  |  20 +++
 default/templates/components/stork-search.tpl |   3 +-
 default/templates/layouts/book.tpl            |   1 +
 default/templates/layouts/note.tpl            |  13 ++
 docs/demo/neuron-layout.md                    |   4 -
 docs/index.yaml                               |   1 -
 9 files changed, 97 insertions(+), 131 deletions(-)
 create mode 100644 default/templates/components/stork-search-js-book.tpl
 create mode 100644 default/templates/components/stork-search-js-note.tpl
 create mode 100644 default/templates/components/stork-search-js.tpl

diff --git a/default/index.yaml b/default/index.yaml
index a94d98e3e..5047a09cd 100644
--- a/default/index.yaml
+++ b/default/index.yaml
@@ -123,130 +123,6 @@ js:
     </script>
     <script async="" id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
 
-  stork-search-base: |
-    <link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
-    <!-- Stork-search styling base stylings -->
-    <style>
-      .stork-wrapper.hidden {
-        /* undo the hidden styling, in effect enabling the search box */
-        display: block;
-      }
-
-      .stork-wrapper .stork-output {
-        margin-top: 0;
-        border-radius: 0;
-        position: sticky;
-        border-color: rgba(99,102,241,var(--tw-border-opacity));
-        --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
-        box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
-      }
-
-      .stork-wrapper .stork-message {
-        padding: 0.5rem 1rem;
-        border-radius: 0;
-        border-color: rgba(99,102,241,var(--tw-border-opacity));
-      }
-
-      .stork-wrapper .stork-close-button {
-        top: 0;
-        margin: 0.8em 0.6em;
-      }
-
-      .stork-wrapper .stork-close-button svg {
-        top: unset;
-        margin-left: auto;
-        margin-right: auto;
-      }
-    </style>
-    <!-- Stork search scripts, styling added in more-head.tpl -->
-    <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
-    <ema:metadata>
-      <with var="template">
-        <script data-emanote-base-url="${value:baseUrl}">
-          (function() {
-            const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
-            const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
-            const indexUrl = baseUrl + '-/stork.st';
-            if (document.readyState !== 'complete') {
-              window.addEventListener('load', function() {
-                stork.register(indexName, indexUrl);
-              });
-            } else {
-              stork.register(indexName, indexUrl, {forceOverwrite: true});
-            }
-          })();
-        </script>
-      </with>
-    </ema:metadata>
-  stork-search-book: |
-    <snippet var="js.stork-search-base"/>
-    <!-- Stork-search styling, specific to Emanote's book layout -->
-    <style>
-      .stork-wrapper > input {
-        border-top-color: rgba(209,213,219,var(--tw-border-opacity));
-        border-left-color: transparent;
-        border-right-color: transparent;
-      }
-
-      @media (min-width: 768px) {
-        .stork-wrapper > input {
-          border-top-color: transparent;
-          border-right-color: rgba(209,213,219,var(--tw-border-opacity));
-        }
-
-        .stork-wrapper .stork-output {
-          top: 0;
-          left: calc(100% - 1px);
-          position: absolute;
-          width: calc(768px - 12rem);
-        }
-
-        .stork-wrapper {
-          margin-right: -1px;
-        }
-      }
-
-      @media (min-width: 1024px) {
-        .stork-wrapper .stork-output {
-          width: calc(1024px - 12rem);
-        }
-      }
-
-      @media (min-width: 1280px) {
-        .stork-wrapper .stork-output {
-          width: calc(1280px - 16rem);
-        }
-      }
-
-      @media (min-width: 1536px) {
-        .stork-wrapper .stork-output {
-          width: calc(1536px - 16rem);
-        }
-      }
-
-      nav#sidebar {
-        z-index: 100;
-      }
-    </style>
-  stork-search-note: |
-    <snippet var="js.stork-search-base"/>
-    <!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
-    <style>
-      .stork-wrapper .stork-output {
-        position: absolute;
-      }
-
-      .stork-wrapper > input {
-        border-top-color: transparent;
-        border-left-color: transparent;
-        border-right-color: transparent;
-      }
-    </style>
-  # This adds styling for the "default layout", and should
-  # be used if you rely on Emanote's default layout.
-  stork-search: |
-    <snippet var="js.stork-search-book"/>
-
 emanote:
   # Whether to automatically treat folder notes as a folgezettel parent of its contents
   folder-folgezettel: true
diff --git a/default/templates/components/stork-search-js-book.tpl b/default/templates/components/stork-search-js-book.tpl
new file mode 100644
index 000000000..85d20e882
--- /dev/null
+++ b/default/templates/components/stork-search-js-book.tpl
@@ -0,0 +1,49 @@
+<apply template="components/stork-search-js" />
+<!-- Stork-search styling, specific to Emanote's book layout -->
+<style>
+	.stork-wrapper > input {
+		border-top-color: rgba(209,213,219,var(--tw-border-opacity));
+		border-left-color: transparent;
+		border-right-color: transparent;
+	}
+
+	@media (min-width: 768px) {
+		.stork-wrapper > input {
+			border-top-color: transparent;
+			border-right-color: rgba(209,213,219,var(--tw-border-opacity));
+		}
+
+		.stork-wrapper .stork-output {
+			top: 0;
+			left: calc(100% - 1px);
+			position: absolute;
+			width: calc(768px - 12rem);
+		}
+
+		.stork-wrapper {
+			margin-right: -1px;
+		}
+	}
+
+	@media (min-width: 1024px) {
+		.stork-wrapper .stork-output {
+			width: calc(1024px - 12rem);
+		}
+	}
+
+	@media (min-width: 1280px) {
+		.stork-wrapper .stork-output {
+			width: calc(1280px - 16rem);
+		}
+	}
+
+	@media (min-width: 1536px) {
+		.stork-wrapper .stork-output {
+			width: calc(1536px - 16rem);
+		}
+	}
+
+	nav#sidebar {
+		z-index: 100;
+	}
+</style>
diff --git a/default/templates/components/stork-search-js-note.tpl b/default/templates/components/stork-search-js-note.tpl
new file mode 100644
index 000000000..2b4f20cd0
--- /dev/null
+++ b/default/templates/components/stork-search-js-note.tpl
@@ -0,0 +1,13 @@
+<apply template="components/stork-search-js" />
+<!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
+<style>
+	.stork-wrapper .stork-output {
+		position: absolute;
+	}
+
+	.stork-wrapper > input {
+		border-top-color: transparent;
+		border-left-color: transparent;
+		border-right-color: transparent;
+	}
+</style>
diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork-search-js.tpl
new file mode 100644
index 000000000..54df28a94
--- /dev/null
+++ b/default/templates/components/stork-search-js.tpl
@@ -0,0 +1,20 @@
+<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
+<script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
+<ema:metadata>
+	<with var="template">
+		<script data-emanote-base-url="${value:baseUrl}">
+			(function() {
+				const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
+				const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
+				const indexUrl = baseUrl + '-/stork.st';
+				if (document.readyState !== 'complete') {
+					window.addEventListener('load', function() {
+						stork.register(indexName, indexUrl);
+					});
+				} else {
+					stork.register(indexName, indexUrl, {forceOverwrite: true});
+				}
+			})();
+		</script>
+	</with>
+</ema:metadata>
diff --git a/default/templates/components/stork-search.tpl b/default/templates/components/stork-search.tpl
index 08cbbcefa..189de0cf8 100644
--- a/default/templates/components/stork-search.tpl
+++ b/default/templates/components/stork-search.tpl
@@ -1,5 +1,4 @@
-<!-- the "hidden" is disabled via "js.stork-search" snippet -->
-<div class="stork-wrapper hidden">
+<div class="stork-wrapper">
 	<input data-stork="emanote-search" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
 	<div data-stork="emanote-search-output" class="stork-output"></div>
 </div>
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 9900bc7f6..aa63d914d 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -77,4 +77,5 @@
       <apply template="components/footer" />
     </div>
   </bind>
+  <apply template="components/stork-search-js-book" />
 </apply>
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index f645a97b7..84803ec37 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -31,4 +31,17 @@
       </div>
     </div>
   </bind>
+  <apply template="components/stork-search-js-note" />
+  <!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
+  <style>
+    .stork-wrapper .stork-output {
+      position: absolute;
+    }
+
+    .stork-wrapper > input {
+      border-top-color: transparent;
+      border-left-color: transparent;
+      border-right-color: transparent;
+    }
+  </style>
 </apply>
diff --git a/docs/demo/neuron-layout.md b/docs/demo/neuron-layout.md
index 81ed2ae8d..4e83522f5 100644
--- a/docs/demo/neuron-layout.md
+++ b/docs/demo/neuron-layout.md
@@ -1,10 +1,6 @@
 ---
 template:
   name: /templates/layouts/note
-page:
-  headHtml: |
-    <snippet var="js.prism" />
-    <snippet var="js.stork-search-note" />
 ---
 
 # Neuron-like layout
diff --git a/docs/index.yaml b/docs/index.yaml
index c227db7f8..38dd0734a 100644
--- a/docs/index.yaml
+++ b/docs/index.yaml
@@ -5,4 +5,3 @@ page:
   siteTitle: Emanote
   headHtml: |
     <snippet var="js.highlightjs" />
-    <snippet var="js.stork-search" />

From 460a1f2a0ada991d75c6dc1d357e5a8eb14708fa Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sat, 13 Aug 2022 12:18:14 -0400
Subject: [PATCH 26/54] Fix compilation

---
 src/Emanote.hs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Emanote.hs b/src/Emanote.hs
index 3ee1dbdb2..c381188f5 100644
--- a/src/Emanote.hs
+++ b/src/Emanote.hs
@@ -50,7 +50,7 @@ instance IsRoute SiteRoute where
 instance EmaSite SiteRoute where
   type SiteArg SiteRoute = EmanoteConfig
   siteInput = emanoteSiteInput
-  siteOutput rp m r = pure $ View.emanoteSiteOutput rp m r
+  siteOutput rp m r = View.emanoteSiteOutput rp m r
 
 defaultEmanoteConfig :: CLI.Cli -> EmanoteConfig
 defaultEmanoteConfig cli =

From 4957829f125a465a053cfcd49f452bd69bec429e Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 18:39:48 +0200
Subject: [PATCH 27/54] Fixed template

---
 .../templates/components/stork-search-js-book.tpl  |  2 +-
 .../templates/components/stork-search-js-note.tpl  |  2 +-
 default/templates/layouts/book.tpl                 |  2 +-
 default/templates/layouts/note.tpl                 | 14 +-------------
 4 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/default/templates/components/stork-search-js-book.tpl b/default/templates/components/stork-search-js-book.tpl
index 85d20e882..622dbe364 100644
--- a/default/templates/components/stork-search-js-book.tpl
+++ b/default/templates/components/stork-search-js-book.tpl
@@ -1,4 +1,4 @@
-<apply template="components/stork-search-js" />
+<apply template="stork-search-js" />
 <!-- Stork-search styling, specific to Emanote's book layout -->
 <style>
 	.stork-wrapper > input {
diff --git a/default/templates/components/stork-search-js-note.tpl b/default/templates/components/stork-search-js-note.tpl
index 2b4f20cd0..880d96d4f 100644
--- a/default/templates/components/stork-search-js-note.tpl
+++ b/default/templates/components/stork-search-js-note.tpl
@@ -1,4 +1,4 @@
-<apply template="components/stork-search-js" />
+<apply template="stork-search-js" />
 <!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
 <style>
 	.stork-wrapper .stork-output {
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index aa63d914d..c5904068b 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -76,6 +76,6 @@
       </div>
       <apply template="components/footer" />
     </div>
+    <apply template="components/stork-search-js-book" />
   </bind>
-  <apply template="components/stork-search-js-book" />
 </apply>
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index 84803ec37..cd4f3d2da 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -30,18 +30,6 @@
         <apply template="components/footer" />
       </div>
     </div>
+    <apply template="components/stork-search-js-note" />
   </bind>
-  <apply template="components/stork-search-js-note" />
-  <!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
-  <style>
-    .stork-wrapper .stork-output {
-      position: absolute;
-    }
-
-    .stork-wrapper > input {
-      border-top-color: transparent;
-      border-left-color: transparent;
-      border-right-color: transparent;
-    }
-  </style>
 </apply>

From d957084be1377b18676093a208dadec84d1dc5f6 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 19:06:16 +0200
Subject: [PATCH 28/54] lint fix

---
 src/Emanote.hs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Emanote.hs b/src/Emanote.hs
index c381188f5..cbb642a02 100644
--- a/src/Emanote.hs
+++ b/src/Emanote.hs
@@ -50,7 +50,7 @@ instance IsRoute SiteRoute where
 instance EmaSite SiteRoute where
   type SiteArg SiteRoute = EmanoteConfig
   siteInput = emanoteSiteInput
-  siteOutput rp m r = View.emanoteSiteOutput rp m r
+  siteOutput = View.emanoteSiteOutput
 
 defaultEmanoteConfig :: CLI.Cli -> EmanoteConfig
 defaultEmanoteConfig cli =

From a30533932c7abddaeb2c0233f0b3cd6ebb492413 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sat, 13 Aug 2022 13:44:36 -0400
Subject: [PATCH 29/54] nix: Use master branch of ema

---
 flake.lock | 8 ++++----
 flake.nix  | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/flake.lock b/flake.lock
index 19ef28d41..5593f087c 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,16 +3,16 @@
     "ema": {
       "flake": false,
       "locked": {
-        "lastModified": 1659481510,
-        "narHash": "sha256-R8KdVEaWxeAGRuNs6FIn0Zge+E0L+mtzfNgcaflzuTA=",
+        "lastModified": 1660337803,
+        "narHash": "sha256-5HKR+T8/OuNzATvdA/ZmTOQmEVODAfTkna+0VkYxpG8=",
         "owner": "srid",
         "repo": "ema",
-        "rev": "94b19ea2a04f3fa08261a8a07fefde3f117c2a74",
+        "rev": "459d3899e0b9ea13e23c81126279dc62530b994c",
         "type": "github"
       },
       "original": {
         "owner": "srid",
-        "ref": "multisite",
+        "ref": "master",
         "repo": "ema",
         "type": "github"
       }
diff --git a/flake.nix b/flake.nix
index 84c6fa582..ffa81536c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -10,7 +10,7 @@
     haskell-flake.url = "github:srid/haskell-flake";
 
     # Haskell dependency overrides
-    ema.url = "github:srid/ema/multisite";
+    ema.url = "github:srid/ema/master";
     ema.flake = false;
     tailwind-haskell.url = "github:srid/tailwind-haskell/master";
     tailwind-haskell.inputs.nixpkgs.follows = "nixpkgs";

From f93922e7fb952a0d5b63271caa9e160eba7f99c2 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 20:12:40 +0200
Subject: [PATCH 30/54] Added missing styling

---
 .../templates/components/stork-search-js.tpl  | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork-search-js.tpl
index 54df28a94..f44e859b4 100644
--- a/default/templates/components/stork-search-js.tpl
+++ b/default/templates/components/stork-search-js.tpl
@@ -18,3 +18,31 @@
 		</script>
 	</with>
 </ema:metadata>
+<!-- Stork-search styling base stylings -->
+<style>
+	.stork-wrapper .stork-output {
+		margin-top: 0;
+		border-radius: 0;
+		position: sticky;
+		border-color: rgba(99,102,241,var(--tw-border-opacity));
+		--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
+		box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
+	}
+
+	.stork-wrapper .stork-message {
+		padding: 0.5rem 1rem;
+		border-radius: 0;
+		border-color: rgba(99,102,241,var(--tw-border-opacity));
+	}
+
+	.stork-wrapper .stork-close-button {
+		top: 0;
+		margin: 0.8em 0.6em;
+	}
+
+	.stork-wrapper .stork-close-button svg {
+		top: unset;
+		margin-left: auto;
+		margin-right: auto;
+	}
+</style>

From 9f47d007797851555e59e5d53a2d156d39f897da Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 21:38:48 +0200
Subject: [PATCH 31/54] Changed to modal

---
 default/templates/base.tpl                    |  1 +
 default/templates/components/breadcrumbs.tpl  |  7 ++-
 .../templates/components/stork-search-css.tpl | 18 +++++++
 .../components/stork-search-js-book.tpl       | 49 -------------------
 .../components/stork-search-js-note.tpl       | 13 -----
 .../templates/components/stork-search-js.tpl  | 43 ++++++----------
 .../templates/components/stork-search-nav.tpl |  8 +++
 default/templates/components/stork-search.tpl | 30 ++++++++++--
 default/templates/layouts/book.tpl            |  3 +-
 default/templates/layouts/note.tpl            |  5 +-
 10 files changed, 78 insertions(+), 99 deletions(-)
 create mode 100644 default/templates/components/stork-search-css.tpl
 delete mode 100644 default/templates/components/stork-search-js-book.tpl
 delete mode 100644 default/templates/components/stork-search-js-note.tpl
 create mode 100644 default/templates/components/stork-search-nav.tpl

diff --git a/default/templates/base.tpl b/default/templates/base.tpl
index 7a85417ba..92660a578 100644
--- a/default/templates/base.tpl
+++ b/default/templates/base.tpl
@@ -44,6 +44,7 @@
 
 <body class="${bodyClass}">
   <body-main />
+  <apply template="components/stork-search" />
 </body>
 
 </html>
diff --git a/default/templates/components/breadcrumbs.tpl b/default/templates/components/breadcrumbs.tpl
index 0bf79750f..30b315e63 100644
--- a/default/templates/components/breadcrumbs.tpl
+++ b/default/templates/components/breadcrumbs.tpl
@@ -25,6 +25,11 @@
         </ema:breadcrumbs>
       </ul>
     </div>
+    <button
+      class="inline px-2 py-1 text-white bg-${theme}-100 outline-none cursor-pointer focus:outline-none"
+      title="Search documents" type="button"
+      onclick="toggleSearch()"
+      >🔍</button>
     <button
       class="inline px-2 py-1 text-white bg-${theme}-600 outline-none cursor-pointer focus:outline-none"
       title="Toggle sidebar" type="button" onclick="toggleHidden('sidebar')">
@@ -40,4 +45,4 @@
       }
     </script>
   </div>
-</nav>
\ No newline at end of file
+</nav>
diff --git a/default/templates/components/stork-search-css.tpl b/default/templates/components/stork-search-css.tpl
new file mode 100644
index 000000000..0ca3701e4
--- /dev/null
+++ b/default/templates/components/stork-search-css.tpl
@@ -0,0 +1,18 @@
+<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/flat.css" />
+<!-- Custom Stork-search styling for Emanote -->
+<style>
+	#stork-search-container {
+		z-index: 1000;
+		background-color: rgb(15 23 42/.8);
+	}
+
+	.stork-overflow-hidden-important {
+		overflow: hidden !important;
+	}
+
+	.stork-wrapper-flat .stork-close-button svg {
+		/* small bugfix in Stork's stock styling */
+		margin: auto;
+	}
+</style>
+
diff --git a/default/templates/components/stork-search-js-book.tpl b/default/templates/components/stork-search-js-book.tpl
deleted file mode 100644
index 622dbe364..000000000
--- a/default/templates/components/stork-search-js-book.tpl
+++ /dev/null
@@ -1,49 +0,0 @@
-<apply template="stork-search-js" />
-<!-- Stork-search styling, specific to Emanote's book layout -->
-<style>
-	.stork-wrapper > input {
-		border-top-color: rgba(209,213,219,var(--tw-border-opacity));
-		border-left-color: transparent;
-		border-right-color: transparent;
-	}
-
-	@media (min-width: 768px) {
-		.stork-wrapper > input {
-			border-top-color: transparent;
-			border-right-color: rgba(209,213,219,var(--tw-border-opacity));
-		}
-
-		.stork-wrapper .stork-output {
-			top: 0;
-			left: calc(100% - 1px);
-			position: absolute;
-			width: calc(768px - 12rem);
-		}
-
-		.stork-wrapper {
-			margin-right: -1px;
-		}
-	}
-
-	@media (min-width: 1024px) {
-		.stork-wrapper .stork-output {
-			width: calc(1024px - 12rem);
-		}
-	}
-
-	@media (min-width: 1280px) {
-		.stork-wrapper .stork-output {
-			width: calc(1280px - 16rem);
-		}
-	}
-
-	@media (min-width: 1536px) {
-		.stork-wrapper .stork-output {
-			width: calc(1536px - 16rem);
-		}
-	}
-
-	nav#sidebar {
-		z-index: 100;
-	}
-</style>
diff --git a/default/templates/components/stork-search-js-note.tpl b/default/templates/components/stork-search-js-note.tpl
deleted file mode 100644
index 880d96d4f..000000000
--- a/default/templates/components/stork-search-js-note.tpl
+++ /dev/null
@@ -1,13 +0,0 @@
-<apply template="stork-search-js" />
-<!-- Stork-search styling, specific to Emanote's Neuron-like layout -->
-<style>
-	.stork-wrapper .stork-output {
-		position: absolute;
-	}
-
-	.stork-wrapper > input {
-		border-top-color: transparent;
-		border-left-color: transparent;
-		border-right-color: transparent;
-	}
-</style>
diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork-search-js.tpl
index f44e859b4..ffe6583ce 100644
--- a/default/templates/components/stork-search-js.tpl
+++ b/default/templates/components/stork-search-js.tpl
@@ -1,8 +1,20 @@
-<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/basic.css" />
 <script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
 <ema:metadata>
 	<with var="template">
 		<script data-emanote-base-url="${value:baseUrl}">
+			function toggleSearch() {
+				document.getElementById('stork-search-container').classList.toggle('hidden');
+				const shown = document.body.classList.toggle('stork-overflow-hidden-important');
+				if (shown) {
+					document.getElementById('stork-search-input').focus();
+				}
+			}
+
+			function clearSearch() {
+				document.getElementById('stork-search-container').classList.add('hidden');
+				document.body.classList.remove('stork-overflow-hidden-important');
+			}
+
 			(function() {
 				const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
 				const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
@@ -12,37 +24,10 @@
 						stork.register(indexName, indexUrl);
 					});
 				} else {
+					// Override existing on Ema's hot-reload
 					stork.register(indexName, indexUrl, {forceOverwrite: true});
 				}
 			})();
 		</script>
 	</with>
 </ema:metadata>
-<!-- Stork-search styling base stylings -->
-<style>
-	.stork-wrapper .stork-output {
-		margin-top: 0;
-		border-radius: 0;
-		position: sticky;
-		border-color: rgba(99,102,241,var(--tw-border-opacity));
-		--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
-		box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
-	}
-
-	.stork-wrapper .stork-message {
-		padding: 0.5rem 1rem;
-		border-radius: 0;
-		border-color: rgba(99,102,241,var(--tw-border-opacity));
-	}
-
-	.stork-wrapper .stork-close-button {
-		top: 0;
-		margin: 0.8em 0.6em;
-	}
-
-	.stork-wrapper .stork-close-button svg {
-		top: unset;
-		margin-left: auto;
-		margin-right: auto;
-	}
-</style>
diff --git a/default/templates/components/stork-search-nav.tpl b/default/templates/components/stork-search-nav.tpl
new file mode 100644
index 000000000..c96a0d276
--- /dev/null
+++ b/default/templates/components/stork-search-nav.tpl
@@ -0,0 +1,8 @@
+<div class="px-2 pt-2">
+	<button type="button"
+		class="p-2 bg-white text-gray-400 border-gray-200 rounded w-full ring-1 ring-gray-200 text-sm flex items-center"
+		onclick="toggleSearch()">
+		<span>🔍 Search...</span>
+		<span class="ml-auto pl-3 flex-none text-xs font-semibold">Ctrl K</span>
+	</button>
+</div>
diff --git a/default/templates/components/stork-search.tpl b/default/templates/components/stork-search.tpl
index 189de0cf8..67745f00d 100644
--- a/default/templates/components/stork-search.tpl
+++ b/default/templates/components/stork-search.tpl
@@ -1,4 +1,28 @@
-<div class="stork-wrapper">
-	<input data-stork="emanote-search" class="px-4 py-2 block w-full rounded-none border border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 focus:outline-none" placeholder="Search..." />
-	<div data-stork="emanote-search-output" class="stork-output"></div>
+<div id="stork-search-container"
+	class="hidden fixed w-screen h-screen inset-0 backdrop-filter backdrop-blur-sm">
+	<div
+	  class="fixed w-screen h-screen inset-0"
+		onclick="toggleSearch()"></div>
+
+	<div class="container mx-auto p-10 mt-10">
+		<div class="stork-wrapper-flat container mx-auto">
+			<input id="stork-search-input" data-stork="emanote-search" class="stork-input" placeholder="Search..." />
+
+			<button class="stork-close-button"
+				onclick="clearSearch()">
+				<svg height="0.8em" viewBox="0 0 23 24" xmlns="http://www.w3.org/2000/svg">
+					<g fill="none" fill-rule="evenodd" stroke-linecap="round">
+					<g transform="translate(-700 -149)" stroke="currentcolor" stroke-width="4">
+					<line id="a" x1="702.5" x2="720" y1="152.5" y2="170"></line>
+					<line transform="translate(711 161) rotate(-90) translate(-711 -161)" x1="702.5" x2="720" y1="152.5" y2="170"></line>
+					</g>
+					</g>
+				</svg>
+			</button>
+
+			<div data-stork="emanote-search-output" class="stork-output"></div>
+		</div>
+	</div>
 </div>
+<apply template="stork-search-css" />
+<apply template="stork-search-js" />
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index c5904068b..4e99076fe 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -10,7 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <apply template="components/stork-search" />
+          <apply template="components/stork-search-nav" />
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
@@ -76,6 +76,5 @@
       </div>
       <apply template="components/footer" />
     </div>
-    <apply template="components/stork-search-js-book" />
   </bind>
 </apply>
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index cd4f3d2da..4796ea346 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -12,9 +12,11 @@
     <div class="${containerClass}">
       <div class="mt-2 md:mt-4">
         <apply template="components/note-uptree" />
-        <apply template="components/stork-search" />
         <div class="md:shadow-2xl md:mb-8">
           <div class="flex-1 w-full overflow-x-auto bg-white">
+            <div class="px-2 pt-2">
+              <apply template="components/stork-search-nav" />
+            </div>
             <main class="px-4 py-4">
               <apply template="components/note-title" />
               <apply template="components/note-body" />
@@ -30,6 +32,5 @@
         <apply template="components/footer" />
       </div>
     </div>
-    <apply template="components/stork-search-js-note" />
   </bind>
 </apply>

From a5fe67b1d5cc1f45620df33e3455f58b4b524637 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sat, 13 Aug 2022 21:49:20 +0200
Subject: [PATCH 32/54] Added listener for Ctrl+K

---
 default/templates/components/stork-search-js.tpl | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork-search-js.tpl
index ffe6583ce..fd201d708 100644
--- a/default/templates/components/stork-search-js.tpl
+++ b/default/templates/components/stork-search-js.tpl
@@ -23,6 +23,13 @@
 					window.addEventListener('load', function() {
 						stork.register(indexName, indexUrl);
 					});
+
+					document.addEventListener('keydown', event => {
+						if ((event.key == 'k' || event.key == 'K') && event.ctrlKey) {
+							toggleSearch();
+							event.preventDefault();
+						}
+					});
 				} else {
 					// Override existing on Ema's hot-reload
 					stork.register(indexName, indexUrl, {forceOverwrite: true});

From 2c2be4698dbeccea408854e905bdfb7bf28c7f34 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 10:50:27 -0400
Subject: [PATCH 33/54] Update nix inputs to use Stock 1.5.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

• Updated input 'haskell-flake':
    'github:srid/haskell-flake/4c0b0ff295f0b97238a600d2381c37ee46b67f9c' (2022-05-31)
  → 'github:srid/haskell-flake/1ca2be3c354ef2a3296cac7e54ae21e1d6ead6d7' (2022-08-12)
• Updated input 'nixpkgs':
    'path:/nix/store/a885zpv9ys2p2x7qnzqvxlsy321mclip-source?lastModified=1657447684&narHash=sha256-FCP9AuU1q6PE3vOeM5SFf58f%2fUKPBAsoSGDUGamNBbo=&rev=5f43d8b088d3771274bcfb69d3c7435b1121ac88' (2022-07-10)
  → 'path:/nix/store/1ha33ma070pyxw5kkcx61qi7ypzzxzah-source?lastModified=1659077768&narHash=sha256-P0XIHBVty6WIuIrk2DZNvLcYev9956y1prT4zL212H8=&rev=2a93ea177c3d7700b934bf95adfe00c435f696b8' (2022-07-29)
• Updated input 'tailwind-haskell':
    'github:srid/tailwind-haskell/96f4c7f8c59eb1103b75ff7d4a753d2d4b9c1ee7' (2022-07-14)
  → 'github:srid/tailwind-haskell/09a102164b1a4559892277ff38efdc9b949c5433' (2022-07-21)
---
 flake.lock | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/flake.lock b/flake.lock
index 5593f087c..c792dbb97 100644
--- a/flake.lock
+++ b/flake.lock
@@ -55,11 +55,11 @@
     },
     "haskell-flake": {
       "locked": {
-        "lastModified": 1654001497,
-        "narHash": "sha256-GfrpyoQrVT9Z/j9its8BQs3I5O5X5Lc2IkK922bz7zg=",
+        "lastModified": 1660319056,
+        "narHash": "sha256-MX6PLEtXVyXXUEk3t1e0c20XRL4m4u9TFET2X0TpTdE=",
         "owner": "srid",
         "repo": "haskell-flake",
-        "rev": "4c0b0ff295f0b97238a600d2381c37ee46b67f9c",
+        "rev": "1ca2be3c354ef2a3296cac7e54ae21e1d6ead6d7",
         "type": "github"
       },
       "original": {
@@ -70,10 +70,10 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1657447684,
-        "narHash": "sha256-FCP9AuU1q6PE3vOeM5SFf58f/UKPBAsoSGDUGamNBbo=",
-        "path": "/nix/store/a885zpv9ys2p2x7qnzqvxlsy321mclip-source",
-        "rev": "5f43d8b088d3771274bcfb69d3c7435b1121ac88",
+        "lastModified": 1659077768,
+        "narHash": "sha256-P0XIHBVty6WIuIrk2DZNvLcYev9956y1prT4zL212H8=",
+        "path": "/nix/store/1ha33ma070pyxw5kkcx61qi7ypzzxzah-source",
+        "rev": "2a93ea177c3d7700b934bf95adfe00c435f696b8",
         "type": "path"
       },
       "original": {
@@ -98,11 +98,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1657796772,
-        "narHash": "sha256-4JD3a9frE26VjaJysyd+DIHUJUiWsx/bFrk8tsGMSpU=",
+        "lastModified": 1658431129,
+        "narHash": "sha256-AtTxP0AMXdeU0ZTJP5R1lCtx5az3ejrmGP1klEdu1qU=",
         "owner": "srid",
         "repo": "tailwind-haskell",
-        "rev": "96f4c7f8c59eb1103b75ff7d4a753d2d4b9c1ee7",
+        "rev": "09a102164b1a4559892277ff38efdc9b949c5433",
         "type": "github"
       },
       "original": {

From b308b8db9006ead61a74ebda2f08cddae20d794a Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 14 Aug 2022 16:54:13 +0200
Subject: [PATCH 34/54] Use /dev/stdout instead of --output=-

---
 src/Emanote/View/Stork.hs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/Emanote/View/Stork.hs b/src/Emanote/View/Stork.hs
index 0301b7468..ee3ab4b9c 100644
--- a/src/Emanote/View/Stork.hs
+++ b/src/Emanote/View/Stork.hs
@@ -54,7 +54,9 @@ renderStorkIndex model = do
   -- TODO: this should retrieve from cache if model hasn't changed
   logW "Generating search index using Stork (this may be expensive)"
   let storkToml = Toml.encode inputCodec $ Input $ storkFiles model
-  (_, !index, _) <- liftIO $ readProcessWithExitCode storkBin ["build", "-t", "--input", "-", "--output", "-"] (encodeUtf8 storkToml)
+  -- NOTE: Cannot use "--output -" due to bug in Rust or Stork:
+  -- https://github.com/jameslittle230/stork/issues/262
+  (_, !index, _) <- liftIO $ readProcessWithExitCode storkBin ["build", "-t", "--input", "-", "--output", "/dev/stdout"] (encodeUtf8 storkToml)
   log "Done generating Stork index"
   pure $ toLazy index
 

From 2b8ff2540dfbbf52b574f5257c12d7cd62ab886b Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 11:22:15 -0400
Subject: [PATCH 35/54] nix: Unmark stork as broken (on intel mac only)

---
 flake.nix     |  9 +++++----
 nix/stork.nix | 10 ++++++++++
 2 files changed, 15 insertions(+), 4 deletions(-)
 create mode 100644 nix/stork.nix

diff --git a/flake.nix b/flake.nix
index ffa81536c..17cff59d1 100644
--- a/flake.nix
+++ b/flake.nix
@@ -22,20 +22,21 @@
         haskell-flake.flakeModule
         ./nix/emanote.nix
         ./nix/docker.nix
+        ./nix/stork.nix
       ];
-      perSystem = { pkgs, inputs', self', ... }: {
+      perSystem = { system, pkgs, inputs', self', ... }: {
         haskellProjects.default = {
           root = ./.;
           buildTools = hp: {
             inherit (pkgs)
               treefmt
-              nixpkgs-fmt
-              stork;
+              nixpkgs-fmt;
             inherit (hp)
               cabal-fmt
               ormolu;
             inherit (inputs'.tailwind-haskell.packages)
               tailwind;
+            inherit (self'.packages) stork;
           };
           source-overrides = {
             inherit (inputs)
@@ -50,7 +51,7 @@
               tailwind;
           };
           modifier = drv: with pkgs.haskell.lib;
-            addBuildDepends drv [ pkgs.stork ];
+            addBuildDepends drv [ self'.packages.stork ];
         };
         packages.test =
           pkgs.runCommand "emanote-test" { } ''
diff --git a/nix/stork.nix b/nix/stork.nix
new file mode 100644
index 000000000..bfc1d94bc
--- /dev/null
+++ b/nix/stork.nix
@@ -0,0 +1,10 @@
+{ ... }:
+
+{
+  perSystem = { system, pkgs, ... }: {
+    packages.stork =
+      # Stork is marked as broken on intel mac, but it does work.
+      # Unfortunately we cannot test this code PATH due to lack of CI for intel mac (#335).
+      if system == "x86_64-darwin" then pkgs.stork.overrideAttrs (_oa: { meta.broken = false; }) else pkgs.stork;
+  };
+}

From 0d96667f514c4bb8829f9e4430080664ceeeab63 Mon Sep 17 00:00:00 2001
From: Kalle Fagerberg <kalle.f8@proton.me>
Date: Sun, 14 Aug 2022 17:23:21 +0200
Subject: [PATCH 36/54] Added hotkeys Cmd+K and Esc

---
 default/templates/components/stork-search-js.tpl | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork-search-js.tpl
index fd201d708..b220b14c8 100644
--- a/default/templates/components/stork-search-js.tpl
+++ b/default/templates/components/stork-search-js.tpl
@@ -2,10 +2,11 @@
 <ema:metadata>
 	<with var="template">
 		<script data-emanote-base-url="${value:baseUrl}">
+			window.searchShown = false;
 			function toggleSearch() {
 				document.getElementById('stork-search-container').classList.toggle('hidden');
-				const shown = document.body.classList.toggle('stork-overflow-hidden-important');
-				if (shown) {
+				window.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
+				if (window.searchShown) {
 					document.getElementById('stork-search-input').focus();
 				}
 			}
@@ -13,6 +14,7 @@
 			function clearSearch() {
 				document.getElementById('stork-search-container').classList.add('hidden');
 				document.body.classList.remove('stork-overflow-hidden-important');
+				window.searchShown = false;
 			}
 
 			(function() {
@@ -25,7 +27,10 @@
 					});
 
 					document.addEventListener('keydown', event => {
-						if ((event.key == 'k' || event.key == 'K') && event.ctrlKey) {
+						if (window.searchShown && event.key === 'Escape') {
+							clearSearch();
+							event.preventDefault();
+						} else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
 							toggleSearch();
 							event.preventDefault();
 						}

From 6c5c6041b6a999e912f1f91ed7f65dbf91b74120 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 11:33:40 -0400
Subject: [PATCH 37/54] refactor: rename module

---
 emanote.cabal                        | 2 +-
 flake.nix                            | 2 +-
 src/Emanote/{View => Model}/Stork.hs | 2 +-
 src/Emanote/View/Template.hs         | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)
 rename src/Emanote/{View => Model}/Stork.hs (98%)

diff --git a/emanote.cabal b/emanote.cabal
index 99ed79a24..b2d81e6bc 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -169,6 +169,7 @@ library
     Emanote.Model.QuerySpec
     Emanote.Model.SData
     Emanote.Model.StaticFile
+    Emanote.Model.Stork
     Emanote.Model.Task
     Emanote.Model.Title
     Emanote.Model.Type
@@ -198,7 +199,6 @@ library
     Emanote.View.Common
     Emanote.View.Export
     Emanote.View.LiveServerFiles
-    Emanote.View.Stork
     Emanote.View.TagIndex
     Emanote.View.TaskIndex
     Emanote.View.Template
diff --git a/flake.nix b/flake.nix
index 17cff59d1..3a8b3bcac 100644
--- a/flake.nix
+++ b/flake.nix
@@ -24,7 +24,7 @@
         ./nix/docker.nix
         ./nix/stork.nix
       ];
-      perSystem = { system, pkgs, inputs', self', ... }: {
+      perSystem = { pkgs, inputs', self', ... }: {
         haskellProjects.default = {
           root = ./.;
           buildTools = hp: {
diff --git a/src/Emanote/View/Stork.hs b/src/Emanote/Model/Stork.hs
similarity index 98%
rename from src/Emanote/View/Stork.hs
rename to src/Emanote/Model/Stork.hs
index ee3ab4b9c..19acf58ba 100644
--- a/src/Emanote/View/Stork.hs
+++ b/src/Emanote/Model/Stork.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE TemplateHaskell #-}
 
-module Emanote.View.Stork
+module Emanote.Model.Stork
   ( renderStorkIndex,
   )
 where
diff --git a/src/Emanote/View/Template.hs b/src/Emanote/View/Template.hs
index 7c06fbaa0..a50d86f34 100644
--- a/src/Emanote/View/Template.hs
+++ b/src/Emanote/View/Template.hs
@@ -15,6 +15,7 @@ import Emanote.Model.Graph qualified as G
 import Emanote.Model.Meta qualified as Meta
 import Emanote.Model.Note qualified as MN
 import Emanote.Model.SData qualified as SData
+import Emanote.Model.Stork (renderStorkIndex)
 import Emanote.Model.Title qualified as Tit
 import Emanote.Pandoc.BuiltinFilters (prepareNoteDoc)
 import Emanote.Route qualified as R
@@ -23,7 +24,6 @@ import Emanote.Route.SiteRoute qualified as SR
 import Emanote.Route.SiteRoute.Class (indexRoute)
 import Emanote.View.Common qualified as C
 import Emanote.View.Export (renderGraphExport)
-import Emanote.View.Stork (renderStorkIndex)
 import Emanote.View.TagIndex qualified as TagIndex
 import Emanote.View.TaskIndex qualified as TaskIndex
 import Heist qualified as H

From 2f603bb8124d31f547908774e0430d5c106dbeeb Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 12:11:35 -0400
Subject: [PATCH 38/54] Cache stork index for reuse (when notes haven't been
 changed)

This is more of a hacky implementation aimed towards establishing
correctness first. It is also not concurrency-friendly. All of this need
to be fixed next.
---
 src/Emanote/Model/Stork.hs    | 35 +++++++++++++++++++++++++--------
 src/Emanote/Model/Type.hs     |  5 +++--
 src/Emanote/Source/Dynamic.hs |  5 +++--
 src/Emanote/Source/Patch.hs   | 37 ++++++++++++++++++++---------------
 4 files changed, 54 insertions(+), 28 deletions(-)

diff --git a/src/Emanote/Model/Stork.hs b/src/Emanote/Model/Stork.hs
index 19acf58ba..6cc96b2f8 100644
--- a/src/Emanote/Model/Stork.hs
+++ b/src/Emanote/Model/Stork.hs
@@ -3,6 +3,7 @@
 
 module Emanote.Model.Stork
   ( renderStorkIndex,
+    runStork,
   )
 where
 
@@ -12,7 +13,7 @@ import Emanote.Model (Model)
 import Emanote.Model.Note qualified as N
 import Emanote.Model.Title qualified as Tit
 import Emanote.Model.Type qualified as M
-import Emanote.Prelude (log, logW)
+import Emanote.Prelude (log, logD, logW)
 import Emanote.Route qualified as R
 import Emanote.Source.Loc qualified as Loc
 import Optics.Core ((^.))
@@ -51,13 +52,31 @@ inputCodec =
 
 renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => Model -> m LByteString
 renderStorkIndex model = do
-  -- TODO: this should retrieve from cache if model hasn't changed
-  logW "Generating search index using Stork (this may be expensive)"
-  let storkToml = Toml.encode inputCodec $ Input $ storkFiles model
-  -- NOTE: Cannot use "--output -" due to bug in Rust or Stork:
-  -- https://github.com/jameslittle230/stork/issues/262
-  (_, !index, _) <- liftIO $ readProcessWithExitCode storkBin ["build", "-t", "--input", "-", "--output", "/dev/stdout"] (encodeUtf8 storkToml)
-  log "Done generating Stork index"
+  let indexTVar = model ^. M.modelStorkIndex
+  readTVarIO indexTVar >>= \case
+    Just index -> do
+      logD "STORK: Returning cached search index"
+      pure index
+    Nothing -> do
+      -- TODO: What if there are concurrent reads? We probably need a lock.
+      -- And we want to encapsulate this whole thing.
+      logW "STORK: Generating search index (this may be expensive)"
+      index <- runStork $ Input $ storkFiles model
+      atomically $ modifyTVar' indexTVar $ \_ -> Just index
+      log "STORK: Done generating search index"
+      pure index
+
+runStork :: MonadIO m => Input -> m LByteString
+runStork input = do
+  let storkToml = Toml.encode inputCodec input
+  (_, !index, _) <-
+    liftIO $
+      readProcessWithExitCode
+        storkBin
+        -- NOTE: Cannot use "--output -" due to bug in Rust or Stork:
+        -- https://github.com/jameslittle230/stork/issues/262
+        ["build", "-t", "--input", "-", "--output", "/dev/stdout"]
+        (encodeUtf8 storkToml)
   pure $ toLazy index
 
 storkFiles :: Model -> [File]
diff --git a/src/Emanote/Model/Type.hs b/src/Emanote/Model/Type.hs
index 930166c93..0035ca5ea 100644
--- a/src/Emanote/Model/Type.hs
+++ b/src/Emanote/Model/Type.hs
@@ -63,7 +63,8 @@ data ModelT encF = Model
     _modelStaticFiles :: IxStaticFile,
     _modelTasks :: IxTask,
     _modelNav :: [Tree Slug],
-    _modelHeistTemplate :: TemplateState
+    _modelHeistTemplate :: TemplateState,
+    _modelStorkIndex :: TVar (Maybe LByteString)
   }
   deriving stock (Generic)
 
@@ -90,7 +91,7 @@ withRoutePrism enc Model {..} =
   let _modelRoutePrism = Identity enc
    in Model {..}
 
-emptyModel :: Set Loc -> Some Ema.CLI.Action -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> ModelEma
+emptyModel :: Set Loc -> Some Ema.CLI.Action -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> TVar (Maybe LByteString) -> ModelEma
 emptyModel layers act ren ctw instanceId =
   Model Status_Loading layers act (Const ()) ren ctw instanceId Ix.empty Ix.empty Ix.empty Ix.empty mempty mempty def
 
diff --git a/src/Emanote/Source/Dynamic.hs b/src/Emanote/Source/Dynamic.hs
index 8298db068..e778108cd 100644
--- a/src/Emanote/Source/Dynamic.hs
+++ b/src/Emanote/Source/Dynamic.hs
@@ -53,15 +53,16 @@ emanoteSiteInput :: (MonadUnliftIO m, MonadLoggerIO m) => Some Ema.CLI.Action ->
 emanoteSiteInput cliAct EmanoteConfig {..} = do
   defaultLayer <- Loc.defaultLayer <$> liftIO Paths_emanote.getDataDir
   instanceId <- liftIO UUID.nextRandom
+  storkIndexTVar <- newTVarIO mempty
   let layers = Loc.userLayers (CLI.layers _emanoteConfigCli) <> one defaultLayer
-      initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId
+      initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId storkIndexTVar
   Dynamic
     <$> UM.unionMount
       (layers & Set.map (id &&& Loc.locPath))
       Pattern.filePatterns
       Pattern.ignorePatterns
       initialModel
-      (mapFsChanges $ Patch.patchModel layers _emanoteConfigNoteFn)
+      (mapFsChanges $ Patch.patchModel layers _emanoteConfigNoteFn storkIndexTVar)
 
 type ChangeHandler tag model m =
   tag ->
diff --git a/src/Emanote/Source/Patch.hs b/src/Emanote/Source/Patch.hs
index 96137967a..3b4ca782f 100644
--- a/src/Emanote/Source/Patch.hs
+++ b/src/Emanote/Source/Patch.hs
@@ -35,6 +35,7 @@ patchModel ::
   (MonadIO m, MonadLogger m, MonadLoggerIO m) =>
   LocLayers ->
   (N.Note -> N.Note) ->
+  TVar (Maybe LByteString) ->
   -- | Type of the file being changed
   R.FileType R.SourceExt ->
   -- | Path to the file being changed
@@ -42,19 +43,20 @@ patchModel ::
   -- | Specific change to the file, along with its paths from other "layers"
   UM.FileAction (NonEmpty (Loc, FilePath)) ->
   m (ModelEma -> ModelEma)
-patchModel layers noteF fpType fp action = do
+patchModel layers noteF storkIndexTVar fpType fp action = do
   logger <- askLoggerIO
   now <- liftIO getCurrentTime
   -- Prefix all patch logging with timestamp.
   let newLogger loc src lvl s =
         logger loc src lvl $ fromString (formatTime defaultTimeLocale "[%H:%M:%S] " now) <> s
-  runLoggingT (patchModel' layers noteF fpType fp action) newLogger
+  runLoggingT (patchModel' layers noteF storkIndexTVar fpType fp action) newLogger
 
 -- | Map a filesystem change to the corresponding model change.
 patchModel' ::
   (MonadIO m, MonadLogger m) =>
   LocLayers ->
   (N.Note -> N.Note) ->
+  TVar (Maybe LByteString) ->
   -- | Type of the file being changed
   R.FileType R.SourceExt ->
   -- | Path to the file being changed
@@ -62,25 +64,28 @@ patchModel' ::
   -- | Specific change to the file, along with its paths from other "layers"
   UM.FileAction (NonEmpty (Loc, FilePath)) ->
   m (ModelEma -> ModelEma)
-patchModel' layers noteF fpType fp action = do
+patchModel' layers noteF storkIndexTVar fpType fp action = do
+  let clearStorkIndex = atomically $ writeTVar storkIndexTVar mempty
   case fpType of
     R.LMLType lmlType -> do
       case R.mkLMLRouteFromKnownFilePath lmlType fp of
         Nothing ->
           pure id -- Impossible
-        Just r -> case action of
-          UM.Refresh refreshAction overlays -> do
-            let fpAbs = locResolve $ head overlays
-                -- TODO: This should automatically be computed, instead of being passed.
-                -- We need access to the model though! With dependency management to boot.
-                -- Until this, `layers` is threaded through as a hack.
-                currentLayerPath = locPath $ primaryLayer layers
-            s <- readRefreshedFile refreshAction fpAbs
-            note <- N.parseNote currentLayerPath r fpAbs (decodeUtf8 s)
-            pure $ M.modelInsertNote $ noteF note
-          UM.Delete -> do
-            log $ "Removing note: " <> toText fp
-            pure $ M.modelDeleteNote r
+        Just r -> do
+          clearStorkIndex
+          case action of
+            UM.Refresh refreshAction overlays -> do
+              let fpAbs = locResolve $ head overlays
+                  -- TODO: This should automatically be computed, instead of being passed.
+                  -- We need access to the model though! With dependency management to boot.
+                  -- Until this, `layers` is threaded through as a hack.
+                  currentLayerPath = locPath $ primaryLayer layers
+              s <- readRefreshedFile refreshAction fpAbs
+              note <- N.parseNote currentLayerPath r fpAbs (decodeUtf8 s)
+              pure $ M.modelInsertNote $ noteF note
+            UM.Delete -> do
+              log $ "Removing note: " <> toText fp
+              pure $ M.modelDeleteNote r
     R.Yaml ->
       case R.mkRouteFromFilePath fp of
         Nothing ->

From e683c784eda3dd7c035428ad192d98f4519d6470 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 12:28:37 -0400
Subject: [PATCH 39/54] Refactor: re-organize around, do newtype wrappers

---
 emanote.cabal                    |  1 +
 src/Emanote/Model/Stork.hs       | 65 ++----------------------
 src/Emanote/Model/Stork/Index.hs | 84 ++++++++++++++++++++++++++++++++
 src/Emanote/Model/Type.hs        |  5 +-
 src/Emanote/Source/Dynamic.hs    |  3 +-
 src/Emanote/Source/Patch.hs      | 17 +++++--
 6 files changed, 106 insertions(+), 69 deletions(-)
 create mode 100644 src/Emanote/Model/Stork/Index.hs

diff --git a/emanote.cabal b/emanote.cabal
index b2d81e6bc..f050ff83b 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -170,6 +170,7 @@ library
     Emanote.Model.SData
     Emanote.Model.StaticFile
     Emanote.Model.Stork
+    Emanote.Model.Stork.Index
     Emanote.Model.Task
     Emanote.Model.Title
     Emanote.Model.Type
diff --git a/src/Emanote/Model/Stork.hs b/src/Emanote/Model/Stork.hs
index 6cc96b2f8..c0b209557 100644
--- a/src/Emanote/Model/Stork.hs
+++ b/src/Emanote/Model/Stork.hs
@@ -1,83 +1,24 @@
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE TemplateHaskell #-}
-
 module Emanote.Model.Stork
   ( renderStorkIndex,
-    runStork,
   )
 where
 
 import Control.Monad.Logger (MonadLoggerIO)
 import Data.IxSet.Typed qualified as Ix
-import Emanote.Model (Model)
 import Emanote.Model.Note qualified as N
+import Emanote.Model.Stork.Index (File (File), Input (Input), readOrBuildStorkIndex)
 import Emanote.Model.Title qualified as Tit
+import Emanote.Model.Type (Model)
 import Emanote.Model.Type qualified as M
-import Emanote.Prelude (log, logD, logW)
 import Emanote.Route qualified as R
 import Emanote.Source.Loc qualified as Loc
 import Optics.Core ((^.))
 import Relude
 import System.FilePath ((</>))
-import System.Process.ByteString (readProcessWithExitCode)
-import System.Which (staticWhich)
-import Toml (TomlCodec, encode, list, string, text, (.=))
-
-storkBin :: FilePath
-storkBin = $(staticWhich "stork")
-
-newtype Input = Input
-  { inputFiles :: [File]
-  }
-  deriving stock (Eq, Show)
-
-data File = File
-  { filePath :: FilePath,
-    fileUrl :: Text,
-    fileTitle :: Text
-  }
-  deriving stock (Eq, Show)
-
-fileCodec :: TomlCodec File
-fileCodec =
-  File
-    <$> Toml.string "path" .= filePath
-    <*> Toml.text "url" .= fileUrl
-    <*> Toml.text "title" .= fileTitle
-
-inputCodec :: TomlCodec Input
-inputCodec =
-  Input
-    <$> Toml.list fileCodec "input.files" .= inputFiles
 
 renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => Model -> m LByteString
 renderStorkIndex model = do
-  let indexTVar = model ^. M.modelStorkIndex
-  readTVarIO indexTVar >>= \case
-    Just index -> do
-      logD "STORK: Returning cached search index"
-      pure index
-    Nothing -> do
-      -- TODO: What if there are concurrent reads? We probably need a lock.
-      -- And we want to encapsulate this whole thing.
-      logW "STORK: Generating search index (this may be expensive)"
-      index <- runStork $ Input $ storkFiles model
-      atomically $ modifyTVar' indexTVar $ \_ -> Just index
-      log "STORK: Done generating search index"
-      pure index
-
-runStork :: MonadIO m => Input -> m LByteString
-runStork input = do
-  let storkToml = Toml.encode inputCodec input
-  (_, !index, _) <-
-    liftIO $
-      readProcessWithExitCode
-        storkBin
-        -- NOTE: Cannot use "--output -" due to bug in Rust or Stork:
-        -- https://github.com/jameslittle230/stork/issues/262
-        ["build", "-t", "--input", "-", "--output", "/dev/stdout"]
-        (encodeUtf8 storkToml)
-  pure $ toLazy index
+  readOrBuildStorkIndex (model ^. M.modelStorkIndex) (Input $ storkFiles model)
 
 storkFiles :: Model -> [File]
 storkFiles model =
diff --git a/src/Emanote/Model/Stork/Index.hs b/src/Emanote/Model/Stork/Index.hs
new file mode 100644
index 000000000..eeecd27ba
--- /dev/null
+++ b/src/Emanote/Model/Stork/Index.hs
@@ -0,0 +1,84 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Emanote.Model.Stork.Index
+  ( IndexVar,
+    newIndex,
+    clearStorkIndex,
+    readOrBuildStorkIndex,
+    File (File),
+    Input (Input),
+  )
+where
+
+import Control.Monad.Logger (MonadLoggerIO)
+import Emanote.Prelude (log, logD, logW)
+import Relude
+import System.Process.ByteString (readProcessWithExitCode)
+import System.Which (staticWhich)
+import Toml (TomlCodec, encode, list, string, text, (.=))
+
+-- | In-memory Stork index tracked in a @TVar@
+newtype IndexVar = IndexVar (TVar (Maybe LByteString))
+
+newIndex :: MonadIO m => m IndexVar
+newIndex =
+  IndexVar <$> newTVarIO mempty
+
+clearStorkIndex :: (MonadIO m) => IndexVar -> m ()
+clearStorkIndex (IndexVar var) = atomically $ writeTVar var mempty
+
+readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Input -> m LByteString
+readOrBuildStorkIndex (IndexVar indexVar) input = do
+  readTVarIO indexVar >>= \case
+    Just index -> do
+      logD "STORK: Returning cached search index"
+      pure index
+    Nothing -> do
+      -- TODO: What if there are concurrent reads? We probably need a lock.
+      -- And we want to encapsulate this whole thing.
+      logW "STORK: Generating search index (this may be expensive)"
+      index <- runStork input
+      atomically $ modifyTVar' indexVar $ \_ -> Just index
+      log "STORK: Done generating search index"
+      pure index
+
+storkBin :: FilePath
+storkBin = $(staticWhich "stork")
+
+runStork :: MonadIO m => Input -> m LByteString
+runStork input = do
+  let storkToml = Toml.encode inputCodec input
+  (_, !index, _) <-
+    liftIO $
+      readProcessWithExitCode
+        storkBin
+        -- NOTE: Cannot use "--output -" due to bug in Rust or Stork:
+        -- https://github.com/jameslittle230/stork/issues/262
+        ["build", "-t", "--input", "-", "--output", "/dev/stdout"]
+        (encodeUtf8 storkToml)
+  pure $ toLazy index
+
+newtype Input = Input
+  { inputFiles :: [File]
+  }
+  deriving stock (Eq, Show)
+
+data File = File
+  { filePath :: FilePath,
+    fileUrl :: Text,
+    fileTitle :: Text
+  }
+  deriving stock (Eq, Show)
+
+fileCodec :: TomlCodec File
+fileCodec =
+  File
+    <$> Toml.string "path" .= filePath
+    <*> Toml.text "url" .= fileUrl
+    <*> Toml.text "title" .= fileTitle
+
+inputCodec :: TomlCodec Input
+inputCodec =
+  Input
+    <$> Toml.list fileCodec "input.files" .= inputFiles
diff --git a/src/Emanote/Model/Type.hs b/src/Emanote/Model/Type.hs
index 0035ca5ea..262f889ac 100644
--- a/src/Emanote/Model/Type.hs
+++ b/src/Emanote/Model/Type.hs
@@ -27,6 +27,7 @@ import Emanote.Model.StaticFile
   ( IxStaticFile,
     StaticFile (StaticFile),
   )
+import Emanote.Model.Stork.Index qualified as Stork
 import Emanote.Model.Task (IxTask)
 import Emanote.Model.Task qualified as Task
 import Emanote.Model.Title qualified as Tit
@@ -64,7 +65,7 @@ data ModelT encF = Model
     _modelTasks :: IxTask,
     _modelNav :: [Tree Slug],
     _modelHeistTemplate :: TemplateState,
-    _modelStorkIndex :: TVar (Maybe LByteString)
+    _modelStorkIndex :: Stork.IndexVar
   }
   deriving stock (Generic)
 
@@ -91,7 +92,7 @@ withRoutePrism enc Model {..} =
   let _modelRoutePrism = Identity enc
    in Model {..}
 
-emptyModel :: Set Loc -> Some Ema.CLI.Action -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> TVar (Maybe LByteString) -> ModelEma
+emptyModel :: Set Loc -> Some Ema.CLI.Action -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> Stork.IndexVar -> ModelEma
 emptyModel layers act ren ctw instanceId =
   Model Status_Loading layers act (Const ()) ren ctw instanceId Ix.empty Ix.empty Ix.empty Ix.empty mempty mempty def
 
diff --git a/src/Emanote/Source/Dynamic.hs b/src/Emanote/Source/Dynamic.hs
index e778108cd..f2290fb89 100644
--- a/src/Emanote/Source/Dynamic.hs
+++ b/src/Emanote/Source/Dynamic.hs
@@ -20,6 +20,7 @@ import Ema (Dynamic (..))
 import Ema.CLI qualified
 import Emanote.CLI qualified as CLI
 import Emanote.Model.Note (Note)
+import Emanote.Model.Stork.Index qualified as Stork
 import Emanote.Model.Type qualified as Model
 import Emanote.Pandoc.Renderer (EmanotePandocRenderers)
 import Emanote.Prelude (chainM)
@@ -53,7 +54,7 @@ emanoteSiteInput :: (MonadUnliftIO m, MonadLoggerIO m) => Some Ema.CLI.Action ->
 emanoteSiteInput cliAct EmanoteConfig {..} = do
   defaultLayer <- Loc.defaultLayer <$> liftIO Paths_emanote.getDataDir
   instanceId <- liftIO UUID.nextRandom
-  storkIndexTVar <- newTVarIO mempty
+  storkIndexTVar <- Stork.newIndex
   let layers = Loc.userLayers (CLI.layers _emanoteConfigCli) <> one defaultLayer
       initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId storkIndexTVar
   Dynamic
diff --git a/src/Emanote/Source/Patch.hs b/src/Emanote/Source/Patch.hs
index 3b4ca782f..8a9acfa89 100644
--- a/src/Emanote/Source/Patch.hs
+++ b/src/Emanote/Source/Patch.hs
@@ -14,6 +14,7 @@ import Data.Time (defaultTimeLocale, formatTime, getCurrentTime)
 import Emanote.Model qualified as M
 import Emanote.Model.Note qualified as N
 import Emanote.Model.SData qualified as SD
+import Emanote.Model.Stork.Index qualified as Stork
 import Emanote.Model.Type (ModelEma)
 import Emanote.Prelude
   ( BadInput (BadInput),
@@ -35,7 +36,7 @@ patchModel ::
   (MonadIO m, MonadLogger m, MonadLoggerIO m) =>
   LocLayers ->
   (N.Note -> N.Note) ->
-  TVar (Maybe LByteString) ->
+  Stork.IndexVar ->
   -- | Type of the file being changed
   R.FileType R.SourceExt ->
   -- | Path to the file being changed
@@ -56,7 +57,7 @@ patchModel' ::
   (MonadIO m, MonadLogger m) =>
   LocLayers ->
   (N.Note -> N.Note) ->
-  TVar (Maybe LByteString) ->
+  Stork.IndexVar ->
   -- | Type of the file being changed
   R.FileType R.SourceExt ->
   -- | Path to the file being changed
@@ -65,14 +66,22 @@ patchModel' ::
   UM.FileAction (NonEmpty (Loc, FilePath)) ->
   m (ModelEma -> ModelEma)
 patchModel' layers noteF storkIndexTVar fpType fp action = do
-  let clearStorkIndex = atomically $ writeTVar storkIndexTVar mempty
   case fpType of
     R.LMLType lmlType -> do
       case R.mkLMLRouteFromKnownFilePath lmlType fp of
         Nothing ->
           pure id -- Impossible
         Just r -> do
-          clearStorkIndex
+          -- Stork doesn't support incremental building of index, so we must
+          -- clear it to pave way for a rebuild later when requested.
+          --
+          -- From https://github.com/jameslittle230/stork/discussions/112#discussioncomment-252861
+          --
+          -- > Stork also doesn't support incremental index updates today --
+          -- you'd have to re-index everything when users added a new document,
+          -- which might be prohibitively long.
+          Stork.clearStorkIndex storkIndexTVar
+
           case action of
             UM.Refresh refreshAction overlays -> do
               let fpAbs = locResolve $ head overlays

From 084b873fa287c7cd1a5d3020865a54b839bea0f5 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 12:47:44 -0400
Subject: [PATCH 40/54] Log the time it took for stork to run (1 second here)

---
 src/Emanote/Model/Stork/Index.hs | 16 ++++++++++++++--
 src/Emanote/Source/Dynamic.hs    |  6 +++---
 2 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/Emanote/Model/Stork/Index.hs b/src/Emanote/Model/Stork/Index.hs
index eeecd27ba..1f75f96d9 100644
--- a/src/Emanote/Model/Stork/Index.hs
+++ b/src/Emanote/Model/Stork/Index.hs
@@ -12,7 +12,9 @@ module Emanote.Model.Stork.Index
 where
 
 import Control.Monad.Logger (MonadLoggerIO)
+import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)
 import Emanote.Prelude (log, logD, logW)
+import Numeric (showGFloat)
 import Relude
 import System.Process.ByteString (readProcessWithExitCode)
 import System.Which (staticWhich)
@@ -38,10 +40,20 @@ readOrBuildStorkIndex (IndexVar indexVar) input = do
       -- TODO: What if there are concurrent reads? We probably need a lock.
       -- And we want to encapsulate this whole thing.
       logW "STORK: Generating search index (this may be expensive)"
-      index <- runStork input
+      liftIO $ print =<< getCurrentTime
+      (diff, !index) <- timeIt $ runStork input
+      liftIO $ print =<< getCurrentTime
       atomically $ modifyTVar' indexVar $ \_ -> Just index
-      log "STORK: Done generating search index"
+      log $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds"
       pure index
+  where
+    timeIt :: MonadIO m => m b -> m (Double, b)
+    timeIt m = do
+      t0 <- liftIO getCurrentTime
+      !x <- m
+      t1 <- liftIO getCurrentTime
+      let diff :: NominalDiffTime = diffUTCTime t1 t0
+      pure (realToFrac diff, x)
 
 storkBin :: FilePath
 storkBin = $(staticWhich "stork")
diff --git a/src/Emanote/Source/Dynamic.hs b/src/Emanote/Source/Dynamic.hs
index f2290fb89..e578892f7 100644
--- a/src/Emanote/Source/Dynamic.hs
+++ b/src/Emanote/Source/Dynamic.hs
@@ -54,16 +54,16 @@ emanoteSiteInput :: (MonadUnliftIO m, MonadLoggerIO m) => Some Ema.CLI.Action ->
 emanoteSiteInput cliAct EmanoteConfig {..} = do
   defaultLayer <- Loc.defaultLayer <$> liftIO Paths_emanote.getDataDir
   instanceId <- liftIO UUID.nextRandom
-  storkIndexTVar <- Stork.newIndex
+  storkIndex <- Stork.newIndex
   let layers = Loc.userLayers (CLI.layers _emanoteConfigCli) <> one defaultLayer
-      initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId storkIndexTVar
+      initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId storkIndex
   Dynamic
     <$> UM.unionMount
       (layers & Set.map (id &&& Loc.locPath))
       Pattern.filePatterns
       Pattern.ignorePatterns
       initialModel
-      (mapFsChanges $ Patch.patchModel layers _emanoteConfigNoteFn storkIndexTVar)
+      (mapFsChanges $ Patch.patchModel layers _emanoteConfigNoteFn storkIndex)
 
 type ChangeHandler tag model m =
   tag ->

From dbdef65005a0a5c13bbbfa7b154f0821c172ac1f Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 12:48:11 -0400
Subject: [PATCH 41/54] remove debug

---
 src/Emanote/Model/Stork/Index.hs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/Emanote/Model/Stork/Index.hs b/src/Emanote/Model/Stork/Index.hs
index 1f75f96d9..8bafc1716 100644
--- a/src/Emanote/Model/Stork/Index.hs
+++ b/src/Emanote/Model/Stork/Index.hs
@@ -40,9 +40,7 @@ readOrBuildStorkIndex (IndexVar indexVar) input = do
       -- TODO: What if there are concurrent reads? We probably need a lock.
       -- And we want to encapsulate this whole thing.
       logW "STORK: Generating search index (this may be expensive)"
-      liftIO $ print =<< getCurrentTime
       (diff, !index) <- timeIt $ runStork input
-      liftIO $ print =<< getCurrentTime
       atomically $ modifyTVar' indexVar $ \_ -> Just index
       log $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds"
       pure index

From 415c4af522fc899b3d3509e14dd05a2b35156eda Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 14 Aug 2022 12:49:10 -0400
Subject: [PATCH 42/54] Exclude tvar modification from the measure

---
 src/Emanote/Model/Stork/Index.hs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Emanote/Model/Stork/Index.hs b/src/Emanote/Model/Stork/Index.hs
index 8bafc1716..5179f00b7 100644
--- a/src/Emanote/Model/Stork/Index.hs
+++ b/src/Emanote/Model/Stork/Index.hs
@@ -41,8 +41,8 @@ readOrBuildStorkIndex (IndexVar indexVar) input = do
       -- And we want to encapsulate this whole thing.
       logW "STORK: Generating search index (this may be expensive)"
       (diff, !index) <- timeIt $ runStork input
-      atomically $ modifyTVar' indexVar $ \_ -> Just index
       log $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds"
+      atomically $ modifyTVar' indexVar $ \_ -> Just index
       pure index
   where
     timeIt :: MonadIO m => m b -> m (Double, b)

From b26171d2616755c8b055f1b1acecba0fa662191f Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Mon, 15 Aug 2022 21:07:43 -0400
Subject: [PATCH 43/54] Group stork templates under components/stork

---
 default/templates/base.tpl                                    | 4 ++--
 default/templates/components/{ => stork}/stork-search-css.tpl | 0
 default/templates/components/{ => stork}/stork-search-js.tpl  | 0
 default/templates/components/{ => stork}/stork-search-nav.tpl | 0
 default/templates/components/{ => stork}/stork-search.tpl     | 0
 default/templates/layouts/book.tpl                            | 4 ++--
 default/templates/layouts/note.tpl                            | 4 ++--
 7 files changed, 6 insertions(+), 6 deletions(-)
 rename default/templates/components/{ => stork}/stork-search-css.tpl (100%)
 rename default/templates/components/{ => stork}/stork-search-js.tpl (100%)
 rename default/templates/components/{ => stork}/stork-search-nav.tpl (100%)
 rename default/templates/components/{ => stork}/stork-search.tpl (100%)

diff --git a/default/templates/base.tpl b/default/templates/base.tpl
index 92660a578..f19720607 100644
--- a/default/templates/base.tpl
+++ b/default/templates/base.tpl
@@ -44,7 +44,7 @@
 
 <body class="${bodyClass}">
   <body-main />
-  <apply template="components/stork-search" />
+  <apply template="components/stork/stork-search" />
 </body>
 
-</html>
+</html>
\ No newline at end of file
diff --git a/default/templates/components/stork-search-css.tpl b/default/templates/components/stork/stork-search-css.tpl
similarity index 100%
rename from default/templates/components/stork-search-css.tpl
rename to default/templates/components/stork/stork-search-css.tpl
diff --git a/default/templates/components/stork-search-js.tpl b/default/templates/components/stork/stork-search-js.tpl
similarity index 100%
rename from default/templates/components/stork-search-js.tpl
rename to default/templates/components/stork/stork-search-js.tpl
diff --git a/default/templates/components/stork-search-nav.tpl b/default/templates/components/stork/stork-search-nav.tpl
similarity index 100%
rename from default/templates/components/stork-search-nav.tpl
rename to default/templates/components/stork/stork-search-nav.tpl
diff --git a/default/templates/components/stork-search.tpl b/default/templates/components/stork/stork-search.tpl
similarity index 100%
rename from default/templates/components/stork-search.tpl
rename to default/templates/components/stork/stork-search.tpl
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 4e99076fe..17331fd90 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -10,7 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <apply template="components/stork-search-nav" />
+          <apply template="components/stork/stork-search-nav" />
           <div class="px-2 py-2 text-gray-800">
 
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
@@ -77,4 +77,4 @@
       <apply template="components/footer" />
     </div>
   </bind>
-</apply>
+</apply>
\ No newline at end of file
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index 4796ea346..20ac30963 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -15,7 +15,7 @@
         <div class="md:shadow-2xl md:mb-8">
           <div class="flex-1 w-full overflow-x-auto bg-white">
             <div class="px-2 pt-2">
-              <apply template="components/stork-search-nav" />
+              <apply template="components/stork/stork-search-nav" />
             </div>
             <main class="px-4 py-4">
               <apply template="components/note-title" />
@@ -33,4 +33,4 @@
       </div>
     </div>
   </bind>
-</apply>
+</apply>
\ No newline at end of file

From 029609d3503328e94ea0e697ff04af8c17c8f1d9 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Wed, 17 Aug 2022 19:26:04 -0400
Subject: [PATCH 44/54] design: use icon to initiate search in all 3 places
 (note, book, breadcrumb)

---
 default/templates/components/breadcrumbs.tpl  | 11 +++--
 .../templates/components/stork/stork-icon.tpl |  5 +++
 .../components/stork/stork-search-nav.tpl     |  8 ----
 .../components/stork/stork-search.tpl         | 43 +++++++++----------
 default/templates/layouts/book.tpl            |  5 ++-
 default/templates/layouts/note.tpl            | 12 ++++--
 6 files changed, 42 insertions(+), 42 deletions(-)
 create mode 100644 default/templates/components/stork/stork-icon.tpl
 delete mode 100644 default/templates/components/stork/stork-search-nav.tpl

diff --git a/default/templates/components/breadcrumbs.tpl b/default/templates/components/breadcrumbs.tpl
index 30b315e63..1f64e6dce 100644
--- a/default/templates/components/breadcrumbs.tpl
+++ b/default/templates/components/breadcrumbs.tpl
@@ -25,11 +25,10 @@
         </ema:breadcrumbs>
       </ul>
     </div>
-    <button
-      class="inline px-2 py-1 text-white bg-${theme}-100 outline-none cursor-pointer focus:outline-none"
-      title="Search documents" type="button"
-      onclick="toggleSearch()"
-      >🔍</button>
+    <button class="inline px-2 py-1 bg-gray-50 outline-none cursor-pointer focus:outline-none"
+      title="Search (Ctrl+K)" type="button" onclick="toggleSearch()">
+      <apply template="stork/stork-icon" />
+    </button>
     <button
       class="inline px-2 py-1 text-white bg-${theme}-600 outline-none cursor-pointer focus:outline-none"
       title="Toggle sidebar" type="button" onclick="toggleHidden('sidebar')">
@@ -45,4 +44,4 @@
       }
     </script>
   </div>
-</nav>
+</nav>
\ No newline at end of file
diff --git a/default/templates/components/stork/stork-icon.tpl b/default/templates/components/stork/stork-icon.tpl
new file mode 100644
index 000000000..476771c8a
--- /dev/null
+++ b/default/templates/components/stork/stork-icon.tpl
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" style="width: 1rem;" class="hover:text-${theme}-700" f
+  fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
+  <path stroke-linecap="round" stroke-linejoin="round"
+    d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
+</svg>
\ No newline at end of file
diff --git a/default/templates/components/stork/stork-search-nav.tpl b/default/templates/components/stork/stork-search-nav.tpl
deleted file mode 100644
index c96a0d276..000000000
--- a/default/templates/components/stork/stork-search-nav.tpl
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="px-2 pt-2">
-	<button type="button"
-		class="p-2 bg-white text-gray-400 border-gray-200 rounded w-full ring-1 ring-gray-200 text-sm flex items-center"
-		onclick="toggleSearch()">
-		<span>🔍 Search...</span>
-		<span class="ml-auto pl-3 flex-none text-xs font-semibold">Ctrl K</span>
-	</button>
-</div>
diff --git a/default/templates/components/stork/stork-search.tpl b/default/templates/components/stork/stork-search.tpl
index 67745f00d..8a5420805 100644
--- a/default/templates/components/stork/stork-search.tpl
+++ b/default/templates/components/stork/stork-search.tpl
@@ -1,28 +1,27 @@
 <div id="stork-search-container"
-	class="hidden fixed w-screen h-screen inset-0 backdrop-filter backdrop-blur-sm">
-	<div
-	  class="fixed w-screen h-screen inset-0"
-		onclick="toggleSearch()"></div>
+  class="hidden fixed w-screen h-screen inset-0 backdrop-filter backdrop-blur-sm">
+  <div class="fixed w-screen h-screen inset-0" onclick="toggleSearch()"></div>
 
-	<div class="container mx-auto p-10 mt-10">
-		<div class="stork-wrapper-flat container mx-auto">
-			<input id="stork-search-input" data-stork="emanote-search" class="stork-input" placeholder="Search..." />
+  <div class="container mx-auto p-10 mt-10">
+    <div class="stork-wrapper-flat container mx-auto">
+      <input id="stork-search-input" data-stork="emanote-search" class="stork-input"
+        placeholder="Search (Ctrl+K) ..." />
 
-			<button class="stork-close-button"
-				onclick="clearSearch()">
-				<svg height="0.8em" viewBox="0 0 23 24" xmlns="http://www.w3.org/2000/svg">
-					<g fill="none" fill-rule="evenodd" stroke-linecap="round">
-					<g transform="translate(-700 -149)" stroke="currentcolor" stroke-width="4">
-					<line id="a" x1="702.5" x2="720" y1="152.5" y2="170"></line>
-					<line transform="translate(711 161) rotate(-90) translate(-711 -161)" x1="702.5" x2="720" y1="152.5" y2="170"></line>
-					</g>
-					</g>
-				</svg>
-			</button>
+      <button class="stork-close-button" onclick="clearSearch()">
+        <svg height="0.8em" viewBox="0 0 23 24" xmlns="http://www.w3.org/2000/svg">
+          <g fill="none" fill-rule="evenodd" stroke-linecap="round">
+            <g transform="translate(-700 -149)" stroke="currentcolor" stroke-width="4">
+              <line id="a" x1="702.5" x2="720" y1="152.5" y2="170"></line>
+              <line transform="translate(711 161) rotate(-90) translate(-711 -161)" x1="702.5"
+                x2="720" y1="152.5" y2="170"></line>
+            </g>
+          </g>
+        </svg>
+      </button>
 
-			<div data-stork="emanote-search-output" class="stork-output"></div>
-		</div>
-	</div>
+      <div data-stork="emanote-search-output" class="stork-output"></div>
+    </div>
+  </div>
 </div>
 <apply template="stork-search-css" />
-<apply template="stork-search-js" />
+<apply template="stork-search-js" />
\ No newline at end of file
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 17331fd90..986d71cb8 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -10,9 +10,7 @@
         <!-- Sidebar column -->
         <nav id="sidebar"
           class="flex-shrink hidden leading-relaxed md:block md:sticky md:top-0 md:h-full md:w-48 xl:w-64">
-          <apply template="components/stork/stork-search-nav" />
           <div class="px-2 py-2 text-gray-800">
-
             <div id="indexing-links" class="flex flex-row float-right p-2 space-x-2 text-gray-500">
               <a href="${ema:tagIndexUrl}" title="View tags">
                 <svg style="width: 1rem;" class="hover:text-${theme}-700" fill="none"
@@ -30,6 +28,9 @@
                   </path>
                 </svg>
               </a>
+              <a title="Search (Ctrl+K)" class="cursor-pointer" onclick="toggleSearch()">
+                <apply template="components/stork/stork-icon" />
+              </a>
             </div>
 
             <div id="site-logo" class="pl-2">
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index 20ac30963..932f63f2a 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -12,11 +12,15 @@
     <div class="${containerClass}">
       <div class="mt-2 md:mt-4">
         <apply template="components/note-uptree" />
-        <div class="md:shadow-2xl md:mb-8">
+
+        <div class="relative md:shadow-2xl md:mb-8">
+
+          <div class="absolute -top-6 right-0 flex flex-row items-center justify-center">
+            <a title="Search (Ctrl+K)" class="cursor-pointer" onclick="toggleSearch()">
+              <apply template="components/stork/stork-icon" />
+            </a>
+          </div>
           <div class="flex-1 w-full overflow-x-auto bg-white">
-            <div class="px-2 pt-2">
-              <apply template="components/stork/stork-search-nav" />
-            </div>
             <main class="px-4 py-4">
               <apply template="components/note-title" />
               <apply template="components/note-body" />

From 92bacc8f2096d69b402cf8585109ee7198d32ac7 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Fri, 19 Aug 2022 16:48:57 -0400
Subject: [PATCH 45/54] Update ema

---
 emanote.cabal | 3 ++-
 flake.lock    | 6 +++---
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/emanote.cabal b/emanote.cabal
index f050ff83b..3be68cbcc 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -103,7 +103,7 @@ common library-common
     , data-default
     , dependent-sum
     , directory
-    , ema                    >=0.7.2
+    , ema                    >=0.8
     , filepath
     , filepattern
     , fsnotify
@@ -142,6 +142,7 @@ common library-common
     , unordered-containers
     , uri-encode
     , url-slug
+    , stm
     , uuid
     , which
     , with-utf8
diff --git a/flake.lock b/flake.lock
index c792dbb97..e9621af06 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,11 +3,11 @@
     "ema": {
       "flake": false,
       "locked": {
-        "lastModified": 1660337803,
-        "narHash": "sha256-5HKR+T8/OuNzATvdA/ZmTOQmEVODAfTkna+0VkYxpG8=",
+        "lastModified": 1660941244,
+        "narHash": "sha256-gGhvmSjjr07u8uWaBIv6F5WE+VCGngSVXN9mhb4BmQo=",
         "owner": "srid",
         "repo": "ema",
-        "rev": "459d3899e0b9ea13e23c81126279dc62530b994c",
+        "rev": "d74daa4d0d1f7a14cebd415787166fe7909fc33b",
         "type": "github"
       },
       "original": {

From 2de10a81f4e5645464725e4264c7a09d741851b9 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Fri, 19 Aug 2022 16:49:04 -0400
Subject: [PATCH 46/54] Inc major ver

---
 emanote.cabal | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emanote.cabal b/emanote.cabal
index 3be68cbcc..63e16f12d 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               emanote
-version:            0.6.23.1
+version:            0.7.0.0
 license:            AGPL-3.0-only
 copyright:          2021 Sridhar Ratnakumar
 maintainer:         srid@srid.ca

From 1374943922bb8444d7bfe299a5b540956634f07c Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Fri, 19 Aug 2022 16:53:47 -0400
Subject: [PATCH 47/54] Format!

---
 emanote.cabal | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/emanote.cabal b/emanote.cabal
index 63e16f12d..b63e068e4 100644
--- a/emanote.cabal
+++ b/emanote.cabal
@@ -131,6 +131,7 @@ common library-common
     , relude                 >=1.0
     , shower
     , some
+    , stm
     , tagged
     , tagtree
     , tailwind               >=0.3
@@ -142,7 +143,6 @@ common library-common
     , unordered-containers
     , uri-encode
     , url-slug
-    , stm
     , uuid
     , which
     , with-utf8

From a383d1c27345e911b33443b61f83af7b4ee0d48a Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Fri, 19 Aug 2022 18:37:34 -0400
Subject: [PATCH 48/54] Workaround the unicode issue with tomland

Resolves #336
---
 src/Emanote/Model/Stork/Index.hs | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/Emanote/Model/Stork/Index.hs b/src/Emanote/Model/Stork/Index.hs
index 5179f00b7..d80f95712 100644
--- a/src/Emanote/Model/Stork/Index.hs
+++ b/src/Emanote/Model/Stork/Index.hs
@@ -12,6 +12,7 @@ module Emanote.Model.Stork.Index
 where
 
 import Control.Monad.Logger (MonadLoggerIO)
+import Data.Text qualified as T
 import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime)
 import Emanote.Prelude (log, logD, logW)
 import Numeric (showGFloat)
@@ -58,7 +59,7 @@ storkBin = $(staticWhich "stork")
 
 runStork :: MonadIO m => Input -> m LByteString
 runStork input = do
-  let storkToml = Toml.encode inputCodec input
+  let storkToml = handleTomlandBug $ Toml.encode inputCodec input
   (_, !index, _) <-
     liftIO $
       readProcessWithExitCode
@@ -68,6 +69,15 @@ runStork input = do
         ["build", "-t", "--input", "-", "--output", "/dev/stdout"]
         (encodeUtf8 storkToml)
   pure $ toLazy index
+  where
+    handleTomlandBug =
+      -- HACK: Deal with tomland's bug.
+      -- https://github.com/EmaApps/emanote/issues/336
+      -- https://github.com/kowainik/tomland/issues/408
+      --
+      -- This could be problematic if the user literally uses \\U in their note
+      -- title (but why would they?)
+      T.replace "\\\\U" "\\U"
 
 newtype Input = Input
   { inputFiles :: [File]

From 5959f67c1306b0d1f44c33c9028f615ee3df6c4b Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Fri, 19 Aug 2022 18:42:59 -0400
Subject: [PATCH 49/54] docs: add full-text search

Very basic intro. Could be expanded.
---
 docs/guide/search.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 docs/guide/search.md

diff --git a/docs/guide/search.md b/docs/guide/search.md
new file mode 100644
index 000000000..1c027febc
--- /dev/null
+++ b/docs/guide/search.md
@@ -0,0 +1,5 @@
+# Full-text search
+
+Emanote provides client-side full-text search using [Stork](https://stork-search.net/)[^1]. The keyboard shortcut `Ctrl+K` (or `⌘K` on macOS) can be used to access the search input. Search should work in both live server and the statically generated site.
+
+[^1]: The Stork index file can be accessed at [`-/stork.st`](-/stork.st). 
\ No newline at end of file

From ebaa0b6d960d4195b3871feb76dcef06d44de1e6 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 21 Aug 2022 11:23:25 -0400
Subject: [PATCH 50/54] refactor: merge stork js/css into a single file, and
 include that in <head> (not <body>)

---
 default/templates/base.tpl                    |  1 +
 .../components/stork/stork-search-css.tpl     | 18 ------
 .../components/stork/stork-search-head.tpl    | 64 +++++++++++++++++++
 .../components/stork/stork-search-js.tpl      | 45 -------------
 .../components/stork/stork-search.tpl         |  4 +-
 5 files changed, 66 insertions(+), 66 deletions(-)
 delete mode 100644 default/templates/components/stork/stork-search-css.tpl
 create mode 100644 default/templates/components/stork/stork-search-head.tpl
 delete mode 100644 default/templates/components/stork/stork-search-js.tpl

diff --git a/default/templates/base.tpl b/default/templates/base.tpl
index f19720607..cfbff4e25 100644
--- a/default/templates/base.tpl
+++ b/default/templates/base.tpl
@@ -34,6 +34,7 @@
   <apply template="/templates/hooks/more-head" />
 
   <head-main />
+  <apply template="components/stork/stork-search-head" />
 </head>
 
 <!-- DoNotFormat -->
diff --git a/default/templates/components/stork/stork-search-css.tpl b/default/templates/components/stork/stork-search-css.tpl
deleted file mode 100644
index 0ca3701e4..000000000
--- a/default/templates/components/stork/stork-search-css.tpl
+++ /dev/null
@@ -1,18 +0,0 @@
-<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/flat.css" />
-<!-- Custom Stork-search styling for Emanote -->
-<style>
-	#stork-search-container {
-		z-index: 1000;
-		background-color: rgb(15 23 42/.8);
-	}
-
-	.stork-overflow-hidden-important {
-		overflow: hidden !important;
-	}
-
-	.stork-wrapper-flat .stork-close-button svg {
-		/* small bugfix in Stork's stock styling */
-		margin: auto;
-	}
-</style>
-
diff --git a/default/templates/components/stork/stork-search-head.tpl b/default/templates/components/stork/stork-search-head.tpl
new file mode 100644
index 000000000..a552500c3
--- /dev/null
+++ b/default/templates/components/stork/stork-search-head.tpl
@@ -0,0 +1,64 @@
+<link rel="stylesheet" href="https://files.stork-search.net/releases/v1.5.0/flat.css" />
+<!-- Custom Stork-search styling for Emanote -->
+<style>
+  #stork-search-container {
+    z-index: 1000;
+    background-color: rgb(15 23 42/.8);
+  }
+
+  .stork-overflow-hidden-important {
+    overflow: hidden !important;
+  }
+
+  .stork-wrapper-flat .stork-close-button svg {
+    /* small bugfix in Stork's stock styling */
+    margin: auto;
+  }
+</style>
+
+
+<script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
+<ema:metadata>
+  <with var="template">
+    <script data-emanote-base-url="${value:baseUrl}">
+      window.searchShown = false;
+      function toggleSearch() {
+        document.getElementById('stork-search-container').classList.toggle('hidden');
+        window.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
+        if (window.searchShown) {
+          document.getElementById('stork-search-input').focus();
+        }
+      }
+
+      function clearSearch() {
+        document.getElementById('stork-search-container').classList.add('hidden');
+        document.body.classList.remove('stork-overflow-hidden-important');
+        window.searchShown = false;
+      }
+
+      (function () {
+        const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
+        const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
+        const indexUrl = baseUrl + '-/stork.st';
+        if (document.readyState !== 'complete') {
+          window.addEventListener('load', function () {
+            stork.register(indexName, indexUrl);
+          });
+
+          document.addEventListener('keydown', event => {
+            if (window.searchShown && event.key === 'Escape') {
+              clearSearch();
+              event.preventDefault();
+            } else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
+              toggleSearch();
+              event.preventDefault();
+            }
+          });
+        } else {
+          // Override existing on Ema's hot-reload
+          stork.register(indexName, indexUrl, { forceOverwrite: true });
+        }
+      })();
+    </script>
+  </with>
+</ema:metadata>
\ No newline at end of file
diff --git a/default/templates/components/stork/stork-search-js.tpl b/default/templates/components/stork/stork-search-js.tpl
deleted file mode 100644
index b220b14c8..000000000
--- a/default/templates/components/stork/stork-search-js.tpl
+++ /dev/null
@@ -1,45 +0,0 @@
-<script src="https://files.stork-search.net/releases/v1.5.0/stork.js"></script>
-<ema:metadata>
-	<with var="template">
-		<script data-emanote-base-url="${value:baseUrl}">
-			window.searchShown = false;
-			function toggleSearch() {
-				document.getElementById('stork-search-container').classList.toggle('hidden');
-				window.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
-				if (window.searchShown) {
-					document.getElementById('stork-search-input').focus();
-				}
-			}
-
-			function clearSearch() {
-				document.getElementById('stork-search-container').classList.add('hidden');
-				document.body.classList.remove('stork-overflow-hidden-important');
-				window.searchShown = false;
-			}
-
-			(function() {
-				const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
-				const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
-				const indexUrl = baseUrl + '-/stork.st';
-				if (document.readyState !== 'complete') {
-					window.addEventListener('load', function() {
-						stork.register(indexName, indexUrl);
-					});
-
-					document.addEventListener('keydown', event => {
-						if (window.searchShown && event.key === 'Escape') {
-							clearSearch();
-							event.preventDefault();
-						} else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
-							toggleSearch();
-							event.preventDefault();
-						}
-					});
-				} else {
-					// Override existing on Ema's hot-reload
-					stork.register(indexName, indexUrl, {forceOverwrite: true});
-				}
-			})();
-		</script>
-	</with>
-</ema:metadata>
diff --git a/default/templates/components/stork/stork-search.tpl b/default/templates/components/stork/stork-search.tpl
index 8a5420805..f879eb755 100644
--- a/default/templates/components/stork/stork-search.tpl
+++ b/default/templates/components/stork/stork-search.tpl
@@ -22,6 +22,4 @@
       <div data-stork="emanote-search-output" class="stork-output"></div>
     </div>
   </div>
-</div>
-<apply template="stork-search-css" />
-<apply template="stork-search-js" />
\ No newline at end of file
+</div>
\ No newline at end of file

From 1bde67133f58b746d6f897e7e4ac817268b4661d Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 21 Aug 2022 11:24:28 -0400
Subject: [PATCH 51/54] add right padding to search icon in mobile view of note
 layout

---
 default/templates/layouts/note.tpl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index 932f63f2a..6f2fa08f9 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -15,7 +15,7 @@
 
         <div class="relative md:shadow-2xl md:mb-8">
 
-          <div class="absolute -top-6 right-0 flex flex-row items-center justify-center">
+          <div class="absolute -top-6 right-1 md:right-0 flex flex-row items-center justify-center">
             <a title="Search (Ctrl+K)" class="cursor-pointer" onclick="toggleSearch()">
               <apply template="components/stork/stork-icon" />
             </a>

From f83d1caa81c0716813101f35ab3f578f0cc4c2e9 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 21 Aug 2022 11:25:31 -0400
Subject: [PATCH 52/54] remove close button

---
 default/templates/components/stork/stork-search.tpl | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/default/templates/components/stork/stork-search.tpl b/default/templates/components/stork/stork-search.tpl
index f879eb755..49a8724d0 100644
--- a/default/templates/components/stork/stork-search.tpl
+++ b/default/templates/components/stork/stork-search.tpl
@@ -6,19 +6,6 @@
     <div class="stork-wrapper-flat container mx-auto">
       <input id="stork-search-input" data-stork="emanote-search" class="stork-input"
         placeholder="Search (Ctrl+K) ..." />
-
-      <button class="stork-close-button" onclick="clearSearch()">
-        <svg height="0.8em" viewBox="0 0 23 24" xmlns="http://www.w3.org/2000/svg">
-          <g fill="none" fill-rule="evenodd" stroke-linecap="round">
-            <g transform="translate(-700 -149)" stroke="currentcolor" stroke-width="4">
-              <line id="a" x1="702.5" x2="720" y1="152.5" y2="170"></line>
-              <line transform="translate(711 161) rotate(-90) translate(-711 -161)" x1="702.5"
-                x2="720" y1="152.5" y2="170"></line>
-            </g>
-          </g>
-        </svg>
-      </button>
-
       <div data-stork="emanote-search-output" class="stork-output"></div>
     </div>
   </div>

From c5649715f21400eab1221f835713c4c1e0c27fe8 Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 21 Aug 2022 11:36:01 -0400
Subject: [PATCH 53/54] Namespace the stork JS functions

---
 .../components/stork/stork-search-head.tpl    | 74 ++++++++++---------
 .../components/stork/stork-search.tpl         |  2 +-
 default/templates/layouts/book.tpl            |  3 +-
 default/templates/layouts/note.tpl            |  3 +-
 4 files changed, 44 insertions(+), 38 deletions(-)

diff --git a/default/templates/components/stork/stork-search-head.tpl b/default/templates/components/stork/stork-search-head.tpl
index a552500c3..a81fc8f22 100644
--- a/default/templates/components/stork/stork-search-head.tpl
+++ b/default/templates/components/stork/stork-search-head.tpl
@@ -21,44 +21,48 @@
 <ema:metadata>
   <with var="template">
     <script data-emanote-base-url="${value:baseUrl}">
-      window.searchShown = false;
-      function toggleSearch() {
-        document.getElementById('stork-search-container').classList.toggle('hidden');
-        window.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
-        if (window.searchShown) {
-          document.getElementById('stork-search-input').focus();
-        }
-      }
-
-      function clearSearch() {
-        document.getElementById('stork-search-container').classList.add('hidden');
-        document.body.classList.remove('stork-overflow-hidden-important');
-        window.searchShown = false;
-      }
+      window.emanote = {};
+      window.emanote.stork = {
+        searchShown: false,
+        toggleSearch: function () {
+          document.getElementById('stork-search-container').classList.toggle('hidden');
+          window.emanote.stork.searchShown = document.body.classList.toggle('stork-overflow-hidden-important');
+          if (window.emanote.stork.searchShown) {
+            document.getElementById('stork-search-input').focus();
+          }
+        },
+        clearSearch: function () {
+          document.getElementById('stork-search-container').classList.add('hidden');
+          document.body.classList.remove('stork-overflow-hidden-important');
+          window.emanote.stork.searchShown = false;
+        },
 
-      (function () {
-        const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
-        const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
-        const indexUrl = baseUrl + '-/stork.st';
-        if (document.readyState !== 'complete') {
-          window.addEventListener('load', function () {
-            stork.register(indexName, indexUrl);
-          });
+        init: function () {
+          const indexName = 'emanote-search'; // used to match input[data-stork] attribute value
+          const baseUrl = document.currentScript.getAttribute('data-emanote-base-url') || '/';
+          const indexUrl = baseUrl + '-/stork.st';
+          if (document.readyState !== 'complete') {
+            window.addEventListener('load', function () {
+              stork.register(indexName, indexUrl);
+            });
 
-          document.addEventListener('keydown', event => {
-            if (window.searchShown && event.key === 'Escape') {
-              clearSearch();
-              event.preventDefault();
-            } else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
-              toggleSearch();
-              event.preventDefault();
-            }
-          });
-        } else {
-          // Override existing on Ema's hot-reload
-          stork.register(indexName, indexUrl, { forceOverwrite: true });
+            document.addEventListener('keydown', event => {
+              if (window.emanote.stork.searchShown && event.key === 'Escape') {
+                window.emanote.stork.clearSearch();
+                event.preventDefault();
+              } else if ((event.key == 'k' || event.key == 'K') && (event.ctrlKey || event.metaKey)) {
+                window.emanote.stork.toggleSearch();
+                event.preventDefault();
+              }
+            });
+          } else {
+            // Override existing on Ema's hot-reload
+            stork.register(indexName, indexUrl, { forceOverwrite: true });
+          }
         }
-      })();
+      };
+
+      window.emanote.stork.init();
     </script>
   </with>
 </ema:metadata>
\ No newline at end of file
diff --git a/default/templates/components/stork/stork-search.tpl b/default/templates/components/stork/stork-search.tpl
index 49a8724d0..14c0f5e26 100644
--- a/default/templates/components/stork/stork-search.tpl
+++ b/default/templates/components/stork/stork-search.tpl
@@ -1,6 +1,6 @@
 <div id="stork-search-container"
   class="hidden fixed w-screen h-screen inset-0 backdrop-filter backdrop-blur-sm">
-  <div class="fixed w-screen h-screen inset-0" onclick="toggleSearch()"></div>
+  <div class="fixed w-screen h-screen inset-0" onclick="window.emanote.stork.toggleSearch()"></div>
 
   <div class="container mx-auto p-10 mt-10">
     <div class="stork-wrapper-flat container mx-auto">
diff --git a/default/templates/layouts/book.tpl b/default/templates/layouts/book.tpl
index 986d71cb8..cb443051e 100644
--- a/default/templates/layouts/book.tpl
+++ b/default/templates/layouts/book.tpl
@@ -28,7 +28,8 @@
                   </path>
                 </svg>
               </a>
-              <a title="Search (Ctrl+K)" class="cursor-pointer" onclick="toggleSearch()">
+              <a title="Search (Ctrl+K)" class="cursor-pointer"
+                onclick="window.emanote.stork.toggleSearch()">
                 <apply template="components/stork/stork-icon" />
               </a>
             </div>
diff --git a/default/templates/layouts/note.tpl b/default/templates/layouts/note.tpl
index 6f2fa08f9..1932231f7 100644
--- a/default/templates/layouts/note.tpl
+++ b/default/templates/layouts/note.tpl
@@ -16,7 +16,8 @@
         <div class="relative md:shadow-2xl md:mb-8">
 
           <div class="absolute -top-6 right-1 md:right-0 flex flex-row items-center justify-center">
-            <a title="Search (Ctrl+K)" class="cursor-pointer" onclick="toggleSearch()">
+            <a title="Search (Ctrl+K)" class="cursor-pointer"
+              onclick="window.emanote.stork.toggleSearch()">
               <apply template="components/stork/stork-icon" />
             </a>
           </div>

From b97f69021d6d6c72861f3ee3ec0818aef709da5b Mon Sep 17 00:00:00 2001
From: Sridhar Ratnakumar <srid@srid.ca>
Date: Sun, 21 Aug 2022 11:39:10 -0400
Subject: [PATCH 54/54] remove redundant

---
 default/templates/components/stork/stork-search-head.tpl | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/default/templates/components/stork/stork-search-head.tpl b/default/templates/components/stork/stork-search-head.tpl
index a81fc8f22..18de8c2bc 100644
--- a/default/templates/components/stork/stork-search-head.tpl
+++ b/default/templates/components/stork/stork-search-head.tpl
@@ -9,11 +9,6 @@
   .stork-overflow-hidden-important {
     overflow: hidden !important;
   }
-
-  .stork-wrapper-flat .stork-close-button svg {
-    /* small bugfix in Stork's stock styling */
-    margin: auto;
-  }
 </style>