Compare commits
3 Commits
7a6e4d17ac
...
b3a2400bc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
b3a2400bc2
|
|||
|
573e70a8d2
|
|||
|
6457674de7
|
@@ -32,6 +32,7 @@ RUN npm install -g pnpm@latest-10
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN pnpm install && pnpm run css-build
|
RUN pnpm install && pnpm run css-build
|
||||||
|
RUN npx -y pagefind --site deploy
|
||||||
|
|
||||||
# ==================================================
|
# ==================================================
|
||||||
# Run Image
|
# Run Image
|
||||||
@@ -42,6 +43,7 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY --from=build /build/deploy .
|
COPY --from=build /build/deploy .
|
||||||
COPY --from=css /build/content/static/output.css ./static/output.css
|
COPY --from=css /build/content/static/output.css ./static/output.css
|
||||||
|
COPY --from=css /build/deploy/pagefind ./pagefind
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ struct Run {
|
|||||||
writers: [
|
writers: [
|
||||||
.itemWriter(swim(renderArticle)),
|
.itemWriter(swim(renderArticle)),
|
||||||
.listWriter(swim(renderArticles)),
|
.listWriter(swim(renderArticles)),
|
||||||
|
.listWriter(renderJson, output: "../static/search.json"),
|
||||||
.tagWriter(swim(renderTag), tags: \.metadata.tags),
|
.tagWriter(swim(renderTag), tags: \.metadata.tags),
|
||||||
.yearWriter(swim(renderYear)),
|
.yearWriter(swim(renderYear)),
|
||||||
// Atom feed for all articles, and a feed per tag
|
// Atom feed for all articles, and a feed per tag
|
||||||
@@ -72,5 +73,18 @@ struct Run {
|
|||||||
// All the remaining files that were not parsed to markdown, so for example images, raw html files and css,
|
// All the remaining files that were not parsed to markdown, so for example images, raw html files and css,
|
||||||
// are copied as-is to the output folder.
|
// are copied as-is to the output folder.
|
||||||
.staticFiles()
|
.staticFiles()
|
||||||
|
|
||||||
|
// Run saga again on articles, to collect search index.
|
||||||
|
// try await Saga(input: "content", output: "deploy")
|
||||||
|
// .register(
|
||||||
|
// folder: "articles",
|
||||||
|
// metadata: ArticleMetadata.self,
|
||||||
|
// readers: [.plainReader],
|
||||||
|
// filter: \.public,
|
||||||
|
// writers: [
|
||||||
|
// .listWriter(renderJson, output: "../search.json")
|
||||||
|
// ]
|
||||||
|
// )
|
||||||
|
// .run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ private func generateHeader(_ pageTitle: String, _ extraHeader: NodeConvertible)
|
|||||||
link(href: "/static/style.css", rel: "stylesheet")
|
link(href: "/static/style.css", rel: "stylesheet")
|
||||||
link(href: "/articles/feed.xml", rel: "alternate", title: SiteMetadata.name, type: "application/rss+xml")
|
link(href: "/articles/feed.xml", rel: "alternate", title: SiteMetadata.name, type: "application/rss+xml")
|
||||||
extraHeader
|
extraHeader
|
||||||
script(src: "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js")
|
// script(src: "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js")
|
||||||
|
//
|
||||||
|
Node.raw("""
|
||||||
|
<script src="/pagefind/pagefind-ui.js"></script>
|
||||||
|
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
|
||||||
|
<script>
|
||||||
|
window.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
new PagefindUI({ element: "#search", showSubResults: true });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,19 +40,7 @@ func generateHeader(
|
|||||||
meta(content: "1014", name: "og:image:width"),
|
meta(content: "1014", name: "og:image:width"),
|
||||||
meta(content: "530", name: "og:image:height"),
|
meta(content: "530", name: "og:image:height"),
|
||||||
script(crossorigin: "anonymous", src: "https://kit.fontawesome.com/f209982030.js"),
|
script(crossorigin: "anonymous", src: "https://kit.fontawesome.com/f209982030.js"),
|
||||||
Node.raw("""
|
script(src: "https://cdn.jsdelivr.net/npm/minisearch@7.1.2/dist/umd/index.min.js")
|
||||||
<script>
|
|
||||||
MathJax = {
|
|
||||||
tex: {
|
|
||||||
inlineMath: [['$', '$']]
|
|
||||||
},
|
|
||||||
svg: {
|
|
||||||
fontCache: 'global'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
"""),
|
|
||||||
script(defer: true, src: "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js")
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,8 +105,11 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
|
|||||||
div {
|
div {
|
||||||
renderArticleInfo(context.item)
|
renderArticleInfo(context.item)
|
||||||
}
|
}
|
||||||
|
// Only index the body of the articles for search.
|
||||||
|
div(customAttributes: ["data-pagefind-body": ""]) {
|
||||||
Node.raw(context.item.body)
|
Node.raw(context.item.body)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div(class: "border-t border-light pt-8 mt-16") {
|
div(class: "border-t border-light pt-8 mt-16") {
|
||||||
div(class: "grid lg:grid-cols-2") {
|
div(class: "grid lg:grid-cols-2") {
|
||||||
|
|||||||
@@ -63,6 +63,60 @@ func renderYear<T>(context: PartitionedRenderingContext<T, ArticleMetadata>) ->
|
|||||||
baseRenderArticles(context.items, canocicalURL: "/articles/\(context.key)/", title: "Articles in \(context.key)")
|
baseRenderArticles(context.items, canocicalURL: "/articles/\(context.key)/", title: "Articles in \(context.key)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct SearchData: Encodable {
|
||||||
|
let url: String
|
||||||
|
let title: String
|
||||||
|
let body: String
|
||||||
|
|
||||||
|
init(article: Item<ArticleMetadata>) throws {
|
||||||
|
self.url = article.url
|
||||||
|
self.title = article.title
|
||||||
|
let rawContent: String = try article.absoluteSource.read()
|
||||||
|
self.body = Self.parse(rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grabs the metadata (wrapped within `---`), the first title, and the body of the document.
|
||||||
|
static func parts(from content: String) -> (String?, String?, String) {
|
||||||
|
let scanner = Scanner(string: content)
|
||||||
|
|
||||||
|
var header: String? = nil
|
||||||
|
var title: String? = nil
|
||||||
|
|
||||||
|
if scanner.scanString("---") == "---" {
|
||||||
|
header = scanner.scanUpToString("---")
|
||||||
|
_ = scanner.scanString("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanner.scanString("# ") == "# " {
|
||||||
|
title = scanner.scanUpToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = String(scanner.string[scanner.currentIndex...])
|
||||||
|
|
||||||
|
return (header, title, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func parse(_ content: String) -> String {
|
||||||
|
let (_, _, body) = parts(from: content)
|
||||||
|
return body
|
||||||
|
.replacingOccurrences(of: "\n", with: " ")
|
||||||
|
.replacingOccurrences(of: "#", with: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderJson(_ articles: ItemsRenderingContext<ArticleMetadata>) throws -> String {
|
||||||
|
print(articles.items.count)
|
||||||
|
print(articles.items)
|
||||||
|
let data = try jsonEncoder.encode(articles.items.map(SearchData.init(article:)))
|
||||||
|
return String(data: data, encoding: .utf8)!
|
||||||
|
}
|
||||||
|
|
||||||
|
private let jsonEncoder: JSONEncoder = {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
||||||
|
return encoder
|
||||||
|
}()
|
||||||
|
|
||||||
private func baseRenderArticles(
|
private func baseRenderArticles(
|
||||||
_ articles: [Item<ArticleMetadata>],
|
_ articles: [Item<ArticleMetadata>],
|
||||||
canocicalURL: String,
|
canocicalURL: String,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func renderPage(context: ItemRenderingContext<PageMetadata>) -> Node {
|
|||||||
|
|
||||||
func renderHome(body: String) -> Node {
|
func renderHome(body: String) -> Node {
|
||||||
div {
|
div {
|
||||||
|
div(class: "font-avenir", id: "search") {}
|
||||||
div(class: "my-24 uppercase font-avenir text-[40px] leading-[1.25] font-thin text-center [&>h1>strong]:font-bold") {
|
div(class: "my-24 uppercase font-avenir text-[40px] leading-[1.25] font-thin text-center [&>h1>strong]:font-bold") {
|
||||||
Node.raw(body)
|
Node.raw(body)
|
||||||
}
|
}
|
||||||
|
|||||||
24
content/static/main.js
Normal file
24
content/static/main.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const documents = null;
|
||||||
|
|
||||||
|
var loadDocuments = function () {
|
||||||
|
fetch("/static/search.json")
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to load search index.");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((response) => (this.documents = response))
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
};
|
||||||
|
|
||||||
|
let miniSearch = new MiniSearch({
|
||||||
|
fields: ["title", "body"],
|
||||||
|
storeFields: ["title", "url"],
|
||||||
|
});
|
||||||
|
|
||||||
|
loadDocuments();
|
||||||
|
|
||||||
|
console.log(documents);
|
||||||
|
|
||||||
|
miniSearch.addAll(documents);
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"tailwindcss": "^4.0.8"
|
"tailwindcss": "^4.0.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/cli": "^4.0.8"
|
"@tailwindcss/cli": "^4.0.8",
|
||||||
|
"pagefind": "^1.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
pnpm-lock.yaml
generated
55
pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
|||||||
'@tailwindcss/cli':
|
'@tailwindcss/cli':
|
||||||
specifier: ^4.0.8
|
specifier: ^4.0.8
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
|
pagefind:
|
||||||
|
specifier: ^1.3.0
|
||||||
|
version: 1.3.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.0.8
|
specifier: ^4.0.8
|
||||||
@@ -18,6 +21,31 @@ importers:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@pagefind/darwin-arm64@1.3.0':
|
||||||
|
resolution: {integrity: sha512-365BEGl6ChOsauRjyVpBjXybflXAOvoMROw3TucAROHIcdBvXk9/2AmEvGFU0r75+vdQI4LJdJdpH4Y6Yqaj4A==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@pagefind/darwin-x64@1.3.0':
|
||||||
|
resolution: {integrity: sha512-zlGHA23uuXmS8z3XxEGmbHpWDxXfPZ47QS06tGUq0HDcZjXjXHeLG+cboOy828QIV5FXsm9MjfkP5e4ZNbOkow==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@pagefind/linux-arm64@1.3.0':
|
||||||
|
resolution: {integrity: sha512-8lsxNAiBRUk72JvetSBXs4WRpYrQrVJXjlRRnOL6UCdBN9Nlsz0t7hWstRk36+JqHpGWOKYiuHLzGYqYAqoOnQ==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@pagefind/linux-x64@1.3.0':
|
||||||
|
resolution: {integrity: sha512-hAvqdPJv7A20Ucb6FQGE6jhjqy+vZ6pf+s2tFMNtMBG+fzcdc91uTw7aP/1Vo5plD0dAOHwdxfkyw0ugal4kcQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@pagefind/windows-x64@1.3.0':
|
||||||
|
resolution: {integrity: sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@parcel/watcher-android-arm64@2.5.1':
|
'@parcel/watcher-android-arm64@2.5.1':
|
||||||
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
|
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
@@ -292,6 +320,10 @@ packages:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
|
pagefind@1.3.0:
|
||||||
|
resolution: {integrity: sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
@@ -312,6 +344,21 @@ packages:
|
|||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@pagefind/darwin-arm64@1.3.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pagefind/darwin-x64@1.3.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pagefind/linux-arm64@1.3.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pagefind/linux-x64@1.3.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@pagefind/windows-x64@1.3.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@parcel/watcher-android-arm64@2.5.1':
|
'@parcel/watcher-android-arm64@2.5.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -519,6 +566,14 @@ snapshots:
|
|||||||
|
|
||||||
node-addon-api@7.1.1: {}
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
|
pagefind@1.3.0:
|
||||||
|
optionalDependencies:
|
||||||
|
'@pagefind/darwin-arm64': 1.3.0
|
||||||
|
'@pagefind/darwin-x64': 1.3.0
|
||||||
|
'@pagefind/linux-arm64': 1.3.0
|
||||||
|
'@pagefind/linux-x64': 1.3.0
|
||||||
|
'@pagefind/windows-x64': 1.3.0
|
||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user