diff --git a/.gitattributes b/.gitattributes index 32ba157..fd96767 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -assets/ filter=lfs diff=lfs merge=lfs -text -templates/images/ filter=lfs diff=lfs merge=lfs -text +assets/* filter=lfs diff=lfs merge=lfs -text +templates/images/* filter=lfs diff=lfs merge=lfs -text diff --git a/.helix/languages.toml b/.helix/languages.toml new file mode 100644 index 0000000..ab93542 --- /dev/null +++ b/.helix/languages.toml @@ -0,0 +1,25 @@ +[[language]] +name = "grz" +scope = "scope.grz" +injection-regex = "grz" +file-types = ["grz"] +roots = [] +auto-format = true +comment-token = "//" +indent = { tab-width = 4, unit = " " } +language-servers = ["grezi"] + +[[grammer]] +name = "grz" + +[[language]] +name = "djot" +scope = "scope.djot" +injection-regex = "dj|djot" +file-types = ["dj"] +roots = [] +comment-token = "{%" +indent = { tab-width = 4, unit = " " } + +[[grammar]] +name = "djot" diff --git a/.woodpecker.yml b/.woodpecker.yml index 44ad97f..8218473 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,7 +1,13 @@ steps: - name: build image: journal + environment: + BASE_URL: https://compute.nations.lol + CODE_THEME: emacs + HELIX_RUNTIME: /usr/lib/helix/runtime commands: - bruin-journal-gen volumes: - /var/woodpecker:/var/woodpecker + - /usr/share/fonts:/usr/share/fonts + - /usr/lib/helix/runtime/:/usr/lib/helix/runtime/ diff --git a/assets/giga_chad.jpg b/assets/giga_chad.jpg index 7150f96..81706ac 100644 Binary files a/assets/giga_chad.jpg and b/assets/giga_chad.jpg differ diff --git a/assets/google_cosplay.jpg b/assets/google_cosplay.jpg new file mode 100644 index 0000000..1b1549f --- /dev/null +++ b/assets/google_cosplay.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61bfb2944db74262c41e81d30d2577c7a1d3be9516b7f5b36e79f068c7b47ac6 +size 59430 diff --git a/assets/the_pipeline.svg b/assets/the_pipeline.svg index b8a8fb2..a1c6d1d 100644 --- a/assets/the_pipeline.svg +++ b/assets/the_pipeline.svg @@ -1 +1,3 @@ -
Articles are
served with
Articles are...
Search is powered
by
Search is powered...
Merges to prod
trigger CI
Merges to prod...
If article is good,
it gets merged
to prod
If article is good,...
Large assets
like images are
stored here
Large assets...
Forgejo
Compiles articles to HTML
Compiles articles to HTML
Woodpecker CI
Writers, editors, etc.
Write...
Articles are
statically
compressed
Articles are...
Articles are registered
and updated with
Articles are registered...
Pull requests are opened on
Pull requests are opened on

Djot

Articles are written in
Djot...
Uses git for incremental
rendering, and author
annotations
Uses git for incremental...
GitNGINXBrotli
Compressed articles
are written back to
server
Compressed articles...
MeilisearchGit LFS
\ No newline at end of file +version https://git-lfs.github.com/spec/v1 +oid sha256:ba624db767ebc7902e2103f57bf63cbe37b35d65739c7b1717f44a96fd51d163 +size 96905 diff --git a/src/how_to_run_a_journal.dj b/src/how_to_run_a_journal.dj index 6541ce2..5103fb7 100644 --- a/src/how_to_run_a_journal.dj +++ b/src/how_to_run_a_journal.dj @@ -1,3 +1,5 @@ +`attributes`{published="2024-05-02 08:00"} + # Web-dev, and the Power of Simplicity. Hi! I'm Isaac Mills, I'm the guy managing the infrastructure behind Compute! In this article, I'd like to talk about just that: the infra behind this media outlet, how it all works, and why it is the way it is. @@ -45,7 +47,7 @@ Consider the following: If I'm accepting untrusted code from the public into my Their pipeline checks if the library with your new code compiles to every platform it's compatible with, with every feature enabled. It also makes sure that your code is well-formatted, contains no conflicts of license, uses no libraries banned by the project, and contains no security advisories. The _only_ way this many checks can be done on every git commit, is through CI, GitHub Actions in egui's case. -The way I've described CI so far has probably made it seem like the least simplest thing you could add to your project, but CI is actually _quite_ simple, and it's an excellent example of how capable of a tool you can make by just building simple, on top of simple, on top of simple. Really, CI is just shell scripting with extra steps. Despite it's inherent simplicity, CI can not only serve as a means to filter bugs out of pull requests, but it can also be a simple way to communicate to open source developers _what a project wants_ out of their code. Instead of having to read a big `CONTRIBUTORS.md` file to get an idea of that, developers can know that their code is good quality if it just passes CI. +The way I've described CI so far has probably made it seem like the least simple thing you could add to your project, but CI is actually _quite_ simple, and it's an excellent example of how capable of a tool you can make by just building simple, on top of simple, on top of simple. Really, CI is just shell scripting with extra steps. Despite it's inherent simplicity, CI can serve not only as a means to filter bugs out of pull requests, but it can also be a simple way to communicate to open source developers _what a project wants_ out of their code. Instead of having to read a big `CONTRIBUTORS.md` file to get an idea of that, developers can know that their code is good quality if it just passes CI. Fortunately, the level of CI I've described above isn't required for journalism. Our CI simply compiles our journalists' unreviewed articles, and serves them on an un-indexed (not visible on production) web page so that they and the team can preview their work before merging it. Our CI is also responsible for indexing and publishing finished articles onto our production website. @@ -61,11 +63,11 @@ Because our CI tool is running this code, we can know which articles need compil - Stat the changed files, which is how we know which files need to be compiled, and which files have been deleted - Run a blame on new articles, which is how we figure out who wrote them -- See if we are we have changed the production branch, and index new articles if so +- See if we have changed the production branch, and index new articles if so -Everything I've just described can be done in just ~400 lines of Rust. This is the effect of buidling simple on top of simple, _and making an effort to retain simplicity_ by making the most out of the simple building blocks. +Everything I've described that the program can do so far can be done in just \~400 lines of Rust. This is the effect of buidling simple on top of simple, _and making an effort to retain simplicity_ by making the most out of the simple building blocks. (P.S: Our Rust code now also generates a title card image for each article, so they look better on platforms like Twitter and Discord. By using the same stradegy, I only needed to add \~230 lines to our Rust code) -![Google cosplay is not business-critical](https://programming.dev/pictrs/image/e69b09e9-3ef6-40ff-b47b-e2ba2b4b633a.png) +[![Google cosplay is not business-critical](assets/google_cosplay.jpg)](https://twitter.com/garybernhardt/status/1344341213575483399) My Rust code makes heavy use of git, a very simple yet amazing tool for adding functionality to plain text. diff --git a/templates/article.html b/templates/article.html index 901e1fb..309a512 100644 --- a/templates/article.html +++ b/templates/article.html @@ -1,44 +1,65 @@ - + - + {{title}} - + + + - - - - + + + + + + + + + {% for author in authors %} + + + + {% endfor %} + + - - - + + + + - - + + + + + -
-

{{title}}

-
- {% for author in authors %} - {{author.full_name}}'s Avatar -
{{author.full_name}}
- {% endfor %} +
+
+

{{title}}

+
+ {% for author in authors %} + {{author.full_name}}'s Avatar +
{{author.full_name}}
+ {% endfor %} +
+

+ {{read_time}} minute read ยท Published: {{published}} +

-

{{published}}

-
{{body}}
+
{{body}}

- -

Read more from Compute

-
+
+ +

Get Notified of new articles!

+
+ + + + + + + + +

Read more from Compute

+
+
- + + \ No newline at end of file diff --git a/templates/css/code-theme.css b/templates/css/code-theme.css new file mode 100644 index 0000000..ea7ff6b --- /dev/null +++ b/templates/css/code-theme.css @@ -0,0 +1,273 @@ +.content code .a { + color: #483D8B; +} +.content code .b { + color: #B22222; +} +.content code .c { + color: #008B8B; +} +.content code .d { + color: #483D8B; +} +.content code .e { + color: #228B22; +} +.content code .f { + text-decoration-color: #FF0000; + text-decoration: underline wavy; +} +.content code .g { + text-decoration-color: #008B8B; + text-decoration: underline wavy; +} +.content code .h { + text-decoration-color: #228B22; + text-decoration: underline wavy; +} +.content code .i { + text-decoration-color: #FF8C00; + text-decoration: underline wavy; +} +.content code .j { + color: #EE9A00; +} +.content code .k { + color: #EE0000; +} +.content code .l { + color: #00CD00; +} +.content code .m { + color: #FF0000; +} +.content code .n { + color: #0000FF; +} +.content code .o { + color: #483D8B; +} +.content code .p { + color: #0000FF; +} +.content code .q { + color: #483D8B; +} +.content code .r { + color: #483D8B; +} +.content code .s { + color: #008B8B; +} +.content code .t { + color: #228B22; +} +.content code .u { + color: #A020F0; +} +.content code .v { + color: #483D8B; +} +.content code .w { + font-weight: bold; +} +.content code .x { + color: #FFD700; + font-weight: bold; +} +.content code .y { + color: #0000FF; +} +.content code .z { + color: #A0522D; +} +.content code .aa { + color: #A020F0; +} +.content code .ab { + color: #B22222; +} +.content code .ac { + color: #228B22; +} +.content code .ad { + color: #008B8B; +} +.content code .ae { + color: #999999; +} +.content code .af { + font-style: italic; +} +.content code .ag { + color: #3A5FCD; +} +.content code .ah { + color: #3A5FCD; + text-decoration: underline; +} +.content code .ai { + color: #000000; +} +.content code .aj { + color: #999999; +} +.content code .ak { + color: #008B8B; +} +.content code .al { + color: #008B8B; +} +.content code .am { +} +.content code .an { + color: #008B8B; +} +.content code .ao { + color: #008B8B; +} +.content code .ap { + color: #A020F0; +} +.content code .aq { + color: #000000; +} +.content code .ar { + color: #0000FF; +} +.content code .as { + color: #8B2252; +} +.content code .at { + color: #8B2252; + font-weight: bold; +} +.content code .au { + color: #0000FF; +} +.content code .av { + color: #228B22; +} +.content code .aw { + color: #483D8B; +} +.content pre { + border: 1px solid #999; + color: #000000; + background-color: #FFFFFF; +} +.content code .ay { + color: #000000; + background-color: #FFFFFF; +} +.content code .az { + color: #5C5C5C; + background-color: #E5E5E5; + text-decoration: underline; +} +.content code .ba { + color: #000000; + background-color: #FCFCFC; +} +.content code .bb { + background-color: #BFBFBF; +} +.content code .bc { + color: #FFFFFF; + background-color: #B3B3B3; +} +.content code .bd { + color: #FF0000; + background-color: #000000; +} +.content code .be { + color: #000000; + background-color: #40E0D0; +} +.content code .bf { + color: #FFFFFF; + background-color: #000000; +} +.content code .bg { + color: #FFFFFF; + background-color: #000000; +} +.content code .bh { + background-color: #B4EEB4; +} +.content code .bi { + background-color: #B4EEB4; +} +.content code .bj { + color: #8B0000; + background-color: #FFF8DC; +} +.content code .bk { + color: #999999; +} +.content code .bl { + color: #CCCCCC; +} +.content code .bm { + color: #000000; + background-color: #FFF8DC; +} +.content code .bn { + color: #8B0000; + background-color: #ADD8E6; +} +.content code .bo { + color: #000000; + background-color: #F7F7F7; +} +.content code .bp { + color: #000000; + background-color: #F7F7F7; +} +.content code .bq { + background-color: #FFEC8B; +} +.content code .br { + background-color: #EEDC82; +} +.content code .bs { + color: #000000; + background-color: #BFBFBF; +} +.content code .bt { + color: #333333; + background-color: #E5E5E5; +} +.content pre code { + display: block; + padding: 20px; + width: fit-content; + color: #000000; +} +.content code .bv { + color: #000000; + background-color: #B4EEB4; +} +.content code .bw { + color: #BFBFBF; +} +.content code .bx { + background-color: #F2F2F2; +} +.content code .by { +} +.content code .bz { + color: #000000; +} +.content code .ca { + color: #A0522D; +} +.content code .cb { + color: #483D8B; +} +.content code .cc { + color: #A0522D; +} +.content code .cd { + color: #FF8C00; +} diff --git a/templates/css/compute-c23f91.webflow.css b/templates/css/compute-c23f91.webflow.css index 8587062..764e871 100644 --- a/templates/css/compute-c23f91.webflow.css +++ b/templates/css/compute-c23f91.webflow.css @@ -1,12 +1,15 @@ :root { --white: white; --black: black; + --gap: calc(1rem * 12.5 / 16); } -.w-layout-vflex { - flex-direction: column; - align-items: flex-start; - display: flex; +body { + font-family: 'Arimo', 'Arial', sans-serif; +} + +code { + font-family: 'Fira Code', monospace; } .w-layout-blockcontainer { @@ -44,7 +47,38 @@ } article { - margin: 20px; + display: flex; + flex-direction: column; + align-items: center; + margin: 25px; + gap: var(--gap); +} + +.content section { + display: flex; + flex-direction: column; + gap: var(--gap); + word-wrap: break-word; + margin-top: calc(2 * var(--gap)); + margin-bottom: 0; +} + +.content img { + margin: var(--gap) 0; +} + +.content ul, +.content ol { + display: flex; + flex-direction: column; + padding: 0 0 0 2em; +} + + +.content * { + line-height: 1.5; + margin: 0 0; + padding: 0 0; } h1 { @@ -54,8 +88,6 @@ h1 { } p { - margin-bottom: 10px; - margin-top: 10px; font-size: 18px; } @@ -97,12 +129,10 @@ li { } .flex-block-2.purple { - color: #333; - background-color: #552a85; - flex-flow: column; - justify-content: space-around; padding-top: 0; + align-items: center; display: flex; + background-color: #552a85; overflow-x: hidden; } @@ -177,9 +207,8 @@ li { background-image: linear-gradient(90deg, #f6c415, rgba(246, 196, 21, .3)); -webkit-background-clip: text; background-clip: text; - margin-bottom: 20px; - margin-left: 20px; - margin-right: 20px; + font-size: 64px; + margin-bottom: 8px; } .image { @@ -187,18 +216,32 @@ li { background-clip: border-box; border-radius: 50%; margin-bottom: 0; - margin-left: 20px; margin-right: 10px; } .flex-block-3 { align-items: center; - margin-bottom: 20px; } .text-block-2 { - color: #fff; + color: white; font-size: 18px; + margin-right: 20px; +} + +article>* { + width: 96vw; + max-width: 940px; +} + +#header-elements { + max-width: 940px; + display: flex; + justify-content: space-around; + flex-direction: column; + margin: 20px; + align-items: flex-start; + gap: 14px; } @media screen and (max-width: 991px) { @@ -207,13 +250,16 @@ li { min-height: 0%; } - .grid { - grid-template-columns: 1fr; + article>* { + max-width: min(575px, 85vw); } - .heading-8 { - margin-bottom: 20px; - font-size: 64px; + #header-elements { + max-width: 575px; + } + + .grid { + grid-template-columns: 1fr; } } @@ -235,10 +281,6 @@ li { .heading-3.unwrapped-compute.compute-gradiant { display: none; } - - .heading-8 { - font-size: 64px; - } } @media screen and (max-width: 479px) { diff --git a/templates/images/favicon.ico b/templates/images/favicon.ico index 052c539..8526c70 100644 Binary files a/templates/images/favicon.ico and b/templates/images/favicon.ico differ diff --git a/templates/images/logo-z.svg b/templates/images/logo-z.svg index 1d41aa7..0650c5a 100644 --- a/templates/images/logo-z.svg +++ b/templates/images/logo-z.svg @@ -1 +1,3 @@ - \ No newline at end of file +version https://git-lfs.github.com/spec/v1 +oid sha256:bfa627911ebc3b1ca7519354b6a555d35e83078811558e309c067ad7742cf209 +size 1351 diff --git a/templates/images/webclip.png b/templates/images/webclip.png index 7fd380c..38e1bf3 100644 Binary files a/templates/images/webclip.png and b/templates/images/webclip.png differ diff --git a/templates/js/code_copy.js b/templates/js/code_copy.js new file mode 100644 index 0000000..3373856 --- /dev/null +++ b/templates/js/code_copy.js @@ -0,0 +1,32 @@ +const copyButtonLabel = "Copy Code"; + +// use a class selector if available +if (navigator.clipboard) { + let blocks = document.querySelectorAll("pre"); + + blocks.forEach((block) => { + // only add button if browser supports Clipboard API + let button = document.createElement("button"); + + button.innerText = copyButtonLabel; + block.parentNode.insertBefore(button, block); + + button.addEventListener("click", async (event) => { + await copyCode(block, event.target); + }); + }); +} + +async function copyCode(block, button) { + let code = block.querySelector("code"); + let text = code.innerText; + + await navigator.clipboard.writeText(text); + + // visual feedback that task is completed + button.innerText = "Code Copied"; + + setTimeout(() => { + button.innerText = copyButtonLabel; + }, 700); +} diff --git a/templates/opengraph.svg b/templates/opengraph.svg new file mode 100644 index 0000000..31a961d --- /dev/null +++ b/templates/opengraph.svg @@ -0,0 +1,78 @@ + + + + diff --git a/templates/opengraph.toml b/templates/opengraph.toml new file mode 100644 index 0000000..385f383 --- /dev/null +++ b/templates/opengraph.toml @@ -0,0 +1,10 @@ +faces = ["/usr/share/fonts/noto/NotoSans-Bold.ttf"] + +[[text]] +x = 425.15115 +y = 8.0 +width = 736.84885 +height = 594.60768 +value = "{{title}}" +font = "Noto Sans" +bold = true diff --git a/templates/raw.html b/templates/raw.html deleted file mode 100644 index a85eedb..0000000 --- a/templates/raw.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - {{title}} - - - - - - - - - - - {% for author in authors %} - - - - {% endfor %} - - - - - - - View full webpage - {{body}} - - - \ No newline at end of file