add mono css framework, add some boiler plate, get started on responsive design fixes
This commit is contained in:
58
_posts/2024-01-15-test-post.md
Normal file
58
_posts/2024-01-15-test-post.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
title: "Test Post: Getting Started with Elixir"
|
||||||
|
date: "2024-01-15"
|
||||||
|
author: "Llmex Team"
|
||||||
|
layout: Llmex.PostLayout
|
||||||
|
---
|
||||||
|
|
||||||
|
# Getting Started with Elixir
|
||||||
|
|
||||||
|
Elixir is a dynamic, functional language designed for building scalable and maintainable applications. It leverages the Erlang VM, known for running low-latency, distributed, and fault-tolerant systems.
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Functional Programming**: Immutable data and pattern matching
|
||||||
|
- **Scalability**: Built on the Erlang VM for concurrent processing
|
||||||
|
- **Fault Tolerance**: Supervisor trees and the "let it crash" philosophy
|
||||||
|
- **Developer Productivity**: Powerful metaprogramming and a helpful compiler
|
||||||
|
|
||||||
|
## Basic Syntax
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Greeting do
|
||||||
|
def hello(name) do
|
||||||
|
"Hello, #{name}!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
IO.puts Greeting.hello("World")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern Matching
|
||||||
|
|
||||||
|
One of Elixir's most powerful features is pattern matching:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
# Assign value
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
# Pattern matching
|
||||||
|
{a, b, c} = {:hello, "world", 42}
|
||||||
|
|
||||||
|
# Pattern matching in function definitions
|
||||||
|
def process({:ok, result}), do: result
|
||||||
|
def process({:error, reason}), do: raise(reason)
|
||||||
|
```
|
||||||
|
|
||||||
|
## The Pipe Operator
|
||||||
|
|
||||||
|
Elixir's pipe operator (`|>`) allows for clean, readable code:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
"Elixir rocks!"
|
||||||
|
|> String.upcase()
|
||||||
|
|> String.split()
|
||||||
|
|> Enum.count()
|
||||||
|
```
|
||||||
|
|
||||||
|
This blog post is just a simple introduction to Elixir. Stay tuned for more in-depth tutorials and examples!
|
260
_site/2024-01-10-functional-programming-benefits/index.html
Normal file
260
_site/2024-01-10-functional-programming-benefits/index.html
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http_equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
The Benefits of Functional Programming in Web Development | Llmex Blog
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<meta name="description" content="A deep dive into the advantages of functional programming for web developers, with practical examples in Elixir.">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="mono-all">
|
||||||
|
<header>
|
||||||
|
<nav class="main-nav">
|
||||||
|
<div class="logo-container">
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<button id="mobile-menu-toggle" class="mobile-menu-toggle">
|
||||||
|
<span class="menu-icon"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul id="nav-links" class="nav-links">
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<main>
|
||||||
|
<h1><a inert href="#the-benefits-of-functional-programming-in-web-development" aria-hidden="true" class="anchor" id="the-benefits-of-functional-programming-in-web-development"></a>The Benefits of Functional Programming in Web Development</h1>
|
||||||
|
<p>In the ever-evolving landscape of web development, functional programming has emerged as a powerful paradigm that offers unique advantages over traditional object-oriented approaches. Today, we'll explore why functional programming, particularly in languages like Elixir, is becoming increasingly popular among web developers.</p>
|
||||||
|
<h2><a inert href="#what-is-functional-programming" aria-hidden="true" class="anchor" id="what-is-functional-programming"></a>What is Functional Programming?</h2>
|
||||||
|
<p>Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions. It emphasizes:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Immutability</strong> - Data structures that cannot be changed after creation</li>
|
||||||
|
<li><strong>Pure Functions</strong> - Functions that always return the same output for the same input</li>
|
||||||
|
<li><strong>Higher-Order Functions</strong> - Functions that can take other functions as parameters</li>
|
||||||
|
<li><strong>No Side Effects</strong> - Functions that don't modify external state</li>
|
||||||
|
</ul>
|
||||||
|
<h2><a inert href="#key-benefits-for-web-development" aria-hidden="true" class="anchor" id="key-benefits-for-web-development"></a>Key Benefits for Web Development</h2>
|
||||||
|
<h3><a inert href="#1-predictability-and-debugging" aria-hidden="true" class="anchor" id="1-predictability-and-debugging"></a>1. Predictability and Debugging</h3>
|
||||||
|
<p>One of the most significant advantages of functional programming is predictability. Pure functions make debugging much easier because:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Pure function - always returns the same result</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">calculate_total</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">reduce</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">items</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">0</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">acc</span> <span style="color: #e0e2ea;">-></span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">acc</span> <span style="color: #e0e2ea;">+</span> <span style="color: #e0e2ea;">item</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">price</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="7">
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #9b9ea4;"># Easy to test and debug</span>
|
||||||
|
</span><span class="line" data-line="9"><span style="color: #8cf8f7;">assert</span> <span style="color: #8cf8f7;">calculate_total</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">price: </span><span style="color: #e0e2ea;">10</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">price: </span><span style="color: #e0e2ea;">20</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">]</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea;">==</span> <span style="color: #e0e2ea;">30</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>You don't have to worry about hidden state changes or unexpected side effects. What you see is what you get.</p>
|
||||||
|
<h3><a inert href="#2-concurrency-and-scalability" aria-hidden="true" class="anchor" id="2-concurrency-and-scalability"></a>2. Concurrency and Scalability</h3>
|
||||||
|
<p>Functional programming languages like Elixir excel at handling concurrent operations. The Actor Model and immutable data structures make it safe to run thousands of processes simultaneously:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Spawn multiple processes safely</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">tasks</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea; font-weight: bold;">for</span> <span style="color: #e0e2ea;">url</span> <span style="color: #e0e2ea;"><-</span> <span style="color: #e0e2ea;">urls</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">Task</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">async</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea; font-weight: bold;">fn</span> <span style="color: #e0e2ea;">-></span> <span style="color: #e0e2ea;">HTTPClient</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">get</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">url</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">end</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="5">
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #9b9ea4;"># Collect results without race conditions</span>
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #e0e2ea;">results</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Task</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">await_many</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">tasks</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>This approach is particularly beneficial for web applications that need to handle many simultaneous users or API requests.</p>
|
||||||
|
<h3><a inert href="#3-error-handling-and-resilience" aria-hidden="true" class="anchor" id="3-error-handling-and-resilience"></a>3. Error Handling and Resilience</h3>
|
||||||
|
<p>Functional languages often promote explicit error handling through pattern matching and result types:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">case</span> <span style="color: #e0e2ea;">UserService</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">create_user</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">params</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;">-></span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #8cf8f7;">render_success</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">changeset</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;">-></span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #8cf8f7;">render_errors</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">changeset</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>This explicit approach to error handling makes your applications more robust and easier to maintain.</p>
|
||||||
|
<h3><a inert href="#4-code-reusability-and-composition" aria-hidden="true" class="anchor" id="4-code-reusability-and-composition"></a>4. Code Reusability and Composition</h3>
|
||||||
|
<p>Higher-order functions and function composition lead to highly reusable code:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Composable functions</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process_orders</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">orders</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">orders</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span><span style="color: #8cf8f7;">valid_order?</span><span style="color: #e0e2ea;">/</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span><span style="color: #8cf8f7;">calculate_tax</span><span style="color: #e0e2ea;">/</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">sort_by</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">total</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:desc</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="8">
|
||||||
|
</span><span class="line" data-line="9"><span style="color: #9b9ea4;"># Each step can be tested and reused independently</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h3><a inert href="#5-immutability-benefits" aria-hidden="true" class="anchor" id="5-immutability-benefits"></a>5. Immutability Benefits</h3>
|
||||||
|
<p>Immutable data structures prevent many common bugs:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>No accidental mutations</strong> - Data can't be changed unexpectedly</li>
|
||||||
|
<li><strong>Thread safety</strong> - Multiple processes can safely read the same data</li>
|
||||||
|
<li><strong>Easier reasoning</strong> - You don't have to track state changes over time</li>
|
||||||
|
</ul>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Original list is never modified</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">original_list</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">[</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">3</span><span style="color: #e0e2ea;">]</span>
|
||||||
|
</span><span class="line" data-line="3"><span style="color: #e0e2ea;">new_list</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">map</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">original_list</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">1</span> <span style="color: #e0e2ea;">*</span> <span style="color: #e0e2ea;">2</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4">
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #9b9ea4;"># original_list is still [1, 2, 3]</span>
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #9b9ea4;"># new_list is [2, 4, 6]</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#real-world-applications" aria-hidden="true" class="anchor" id="real-world-applications"></a>Real-World Applications</h2>
|
||||||
|
<h3><a inert href="#api-development" aria-hidden="true" class="anchor" id="api-development"></a>API Development</h3>
|
||||||
|
<p>Functional programming shines in API development where you need to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Transform data between different formats</li>
|
||||||
|
<li>Handle concurrent requests efficiently</li>
|
||||||
|
<li>Maintain consistent state across operations</li>
|
||||||
|
</ul>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">handle_api_request</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">params</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">with</span> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">validated_params</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;"><-</span> <span style="color: #8cf8f7;">validate_params</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">params</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;"><-</span> <span style="color: #8cf8f7;">authenticate_user</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">result</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;"><-</span> <span style="color: #8cf8f7;">process_request</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">validated_params</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">user</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #8cf8f7;">json</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">data: </span><span style="color: #e0e2ea;">result</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea; font-weight: bold;">else</span>
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;">-></span>
|
||||||
|
</span><span class="line" data-line="8"> <span style="color: #8cf8f7;">json</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">conn</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">error: </span><span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="9"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="10"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h3><a inert href="#data-processing-pipelines" aria-hidden="true" class="anchor" id="data-processing-pipelines"></a>Data Processing Pipelines</h3>
|
||||||
|
<p>The pipeline operator and functional composition make data processing elegant:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process_user_analytics</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">raw_data</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea;">raw_data</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">|></span> <span style="color: #8cf8f7;">parse_events</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">|></span> <span style="color: #8cf8f7;">filter_valid_events</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea;">|></span> <span style="color: #8cf8f7;">group_by_user</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea;">|></span> <span style="color: #8cf8f7;">calculate_metrics</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #e0e2ea;">|></span> <span style="color: #8cf8f7;">format_for_output</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h3><a inert href="#real-time-applications" aria-hidden="true" class="anchor" id="real-time-applications"></a>Real-time Applications</h3>
|
||||||
|
<p>With technologies like Phoenix LiveView, functional programming enables powerful real-time features:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">handle_event</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">"update_score"</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">%</span><span style="color: #e0e2ea;">{</span><span style="color: #b3f6c0;">"score"</span> <span style="color: #e0e2ea;">=></span> <span style="color: #e0e2ea;">score</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">socket</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea;">new_state</span> <span style="color: #e0e2ea;">=</span> <span style="color: #8cf8f7;">update_game_state</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">socket</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">game</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">score</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="3">
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #9b9ea4;"># Broadcast to all connected users</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #8cf8f7;">broadcast_update</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">new_state</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6">
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:noreply</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">assign</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">socket</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">game: </span><span style="color: #e0e2ea;">new_state</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">}</span>
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#getting-started-with-functional-web-development" aria-hidden="true" class="anchor" id="getting-started-with-functional-web-development"></a>Getting Started with Functional Web Development</h2>
|
||||||
|
<p>If you're interested in exploring functional programming for web development:</p>
|
||||||
|
<ol>
|
||||||
|
<li><strong>Start with pure functions</strong> - Begin by writing functions without side effects</li>
|
||||||
|
<li><strong>Embrace immutability</strong> - Use immutable data structures wherever possible</li>
|
||||||
|
<li><strong>Learn pattern matching</strong> - Master this powerful feature for control flow</li>
|
||||||
|
<li><strong>Practice composition</strong> - Build complex operations from simple functions</li>
|
||||||
|
<li><strong>Explore functional languages</strong> - Try Elixir, Clojure, or functional JavaScript</li>
|
||||||
|
</ol>
|
||||||
|
<h2><a inert href="#common-misconceptions" aria-hidden="true" class="anchor" id="common-misconceptions"></a>Common Misconceptions</h2>
|
||||||
|
<h3><a inert href="#functional-programming-is-slower" aria-hidden="true" class="anchor" id="functional-programming-is-slower"></a>"Functional Programming is Slower"</h3>
|
||||||
|
<p>Modern functional languages are highly optimized. Elixir's BEAM VM, for example, is designed for high concurrency and fault tolerance.</p>
|
||||||
|
<h3><a inert href="#its-too-academic" aria-hidden="true" class="anchor" id="its-too-academic"></a>"It's Too Academic"</h3>
|
||||||
|
<p>While functional programming has academic roots, it's highly practical for real-world applications. Companies like WhatsApp, Discord, and Pinterest use Elixir in production.</p>
|
||||||
|
<h3><a inert href="#learning-curve-is-too-steep" aria-hidden="true" class="anchor" id="learning-curve-is-too-steep"></a>"Learning Curve is Too Steep"</h3>
|
||||||
|
<p>The concepts might be different, but the payoff in code quality and maintainability is worth the initial investment.</p>
|
||||||
|
<h2><a inert href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
|
||||||
|
<p>Functional programming offers significant benefits for web development, from improved reliability and scalability to better code organization and testing. While it requires a shift in thinking, the advantages—especially for complex, concurrent applications—make it a valuable approach to master.</p>
|
||||||
|
<p>The web development landscape continues to evolve, and functional programming principles are becoming increasingly important. Whether you choose a purely functional language like Elixir or incorporate functional concepts into your existing toolset, understanding these principles will make you a more effective developer.</p>
|
||||||
|
<p>Ready to dive deeper? Start by exploring functional concepts in your current language, or take the plunge with a functional web framework like Phoenix. The future of web development is functional, and the time to start learning is now.</p>
|
||||||
|
|
||||||
|
<time datetime="2024-01-10T00:00:00Z"></time>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<h3>Recent Posts</h3>
|
||||||
|
<ul class="sidebar-posts">
|
||||||
|
<li><a href="/posts/2024-01-15-getting-started-with-tableau">Getting Started with Tableau</a></li>
|
||||||
|
<li><a href="/posts/2024-01-10-functional-programming-benefits">The Benefits of Functional Programming</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Categories</h3>
|
||||||
|
<ul class="sidebar-categories">
|
||||||
|
<li><a href="/posts/elixir">Elixir</a></li>
|
||||||
|
<li><a href="/posts/phoenix">Phoenix</a></li>
|
||||||
|
<li><a href="/posts/functional-programming">Functional Programming</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with Elixir & Tableau.</p>
|
||||||
|
<div class="mobile-nav">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/posts">Posts</a>
|
||||||
|
<a href="/about">About</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('mobile-menu-toggle').addEventListener('click', function() {
|
||||||
|
document.getElementById('nav-links').classList.toggle('active');
|
||||||
|
this.classList.toggle('active');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log(message) {
|
||||||
|
if (true) {
|
||||||
|
console.log(`[web_dev_utils] ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function connect() {
|
||||||
|
try {
|
||||||
|
window.socket = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
window.socket.onmessage = function(e) {
|
||||||
|
if (e.data === "reload") {
|
||||||
|
log("reloading!");
|
||||||
|
location.reload();
|
||||||
|
} else if (e.data === "subscribed") {
|
||||||
|
log("connected and subscribed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.socket.onopen = () => {
|
||||||
|
waitForConnection(() => {
|
||||||
|
log("sending 'subscribe' message");
|
||||||
|
window.socket.send("subscribe")
|
||||||
|
}
|
||||||
|
, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.socket.onclose = () => {
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForConnection(callback, interval) {
|
||||||
|
log("waiting for connection!")
|
||||||
|
if (window.socket.readyState === 1) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
log("setting a timeout")
|
||||||
|
setTimeout(() => waitForConnection(callback, interval), interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("about to connect");
|
||||||
|
connect();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
247
_site/2024-01-15-getting-started-with-tableau/index.html
Normal file
247
_site/2024-01-15-getting-started-with-tableau/index.html
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http_equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
Getting Started with Tableau: Building Static Sites with Elixir | Llmex Blog
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<meta name="description" content="A comprehensive guide to getting started with Tableau static site generator for Elixir developers.">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="mono-all">
|
||||||
|
<header>
|
||||||
|
<nav class="main-nav">
|
||||||
|
<div class="logo-container">
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<button id="mobile-menu-toggle" class="mobile-menu-toggle">
|
||||||
|
<span class="menu-icon"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul id="nav-links" class="nav-links">
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<main>
|
||||||
|
<h1><a inert href="#getting-started-with-tableau-building-static-sites-with-elixir" aria-hidden="true" class="anchor" id="getting-started-with-tableau-building-static-sites-with-elixir"></a>Getting Started with Tableau: Building Static Sites with Elixir</h1>
|
||||||
|
<p>Static site generators have revolutionized how we build and deploy websites, offering the perfect balance of performance, security, and developer experience. Today, we're exploring <strong>Tableau</strong>, a powerful static site generator built specifically for the Elixir ecosystem.</p>
|
||||||
|
<h2><a inert href="#why-tableau" aria-hidden="true" class="anchor" id="why-tableau"></a>Why Tableau?</h2>
|
||||||
|
<p>Tableau brings the elegance and power of Elixir to static site generation. Unlike traditional generators written in Ruby or JavaScript, Tableau leverages:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Functional Programming Paradigms</strong> - Immutable data structures and pure functions</li>
|
||||||
|
<li><strong>Phoenix LiveView Components</strong> - Reusable, testable UI components</li>
|
||||||
|
<li><strong>HEEx Templates</strong> - HTML-aware templates with compile-time checking</li>
|
||||||
|
<li><strong>Pattern Matching</strong> - Elegant data transformation and routing</li>
|
||||||
|
</ul>
|
||||||
|
<h2><a inert href="#setting-up-your-first-tableau-site" aria-hidden="true" class="anchor" id="setting-up-your-first-tableau-site"></a>Setting Up Your First Tableau Site</h2>
|
||||||
|
<p>Getting started with Tableau is straightforward. First, add it to your <code>mix.exs</code> dependencies:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defp</span> <span style="color: #8cf8f7;">deps</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea;">[</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:tableau</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">"~> 0.25"</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">,</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:phoenix_live_view</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">"~> 1.0"</span><span style="color: #e0e2ea;">}</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea;">]</span>
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>Then create your first layout:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyBlog.RootLayout</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Tableau.Layout</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Phoenix.Component</span>
|
||||||
|
</span><span class="line" data-line="4">
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">template</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #8cf8f7;">~</span>H"""
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #e0e2ea;"><span style="color: #8cf8f7;"><!</span>DOCTYPE html<span style="color: #8cf8f7;">></span></span>
|
||||||
|
</span><span class="line" data-line="8"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">html</span> <span style="color: #8cf8f7;">lang</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">"<span style="color: #b3f6c0;">en</span>"</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="9"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">head</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="10"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">meta</span> <span style="color: #8cf8f7;">charset</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">"<span style="color: #b3f6c0;">utf-8</span>"</span> <span style="color: #8cf8f7;">/></span>
|
||||||
|
</span><span class="line" data-line="11"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">title</span><span style="color: #8cf8f7;">></span><span style="color: #8cf8f7;"><%=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">page</span></span></span><span style="color: #e0e2ea;">[</span><span style="color: #8cf8f7;">:title</span><span style="color: #e0e2ea;">]</span><span style="color: #8cf8f7;"> %></span><span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">title</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="12"> <span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">head</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="13"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">body</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="14"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">main</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="15"> <span style="color: #8cf8f7;"><%=</span> <span style="color: #8cf8f7;">render</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">inner_content</span></span></span><span style="color: #8cf8f7;"> %></span>
|
||||||
|
</span><span class="line" data-line="16"> <span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">main</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="17"> <span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">body</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="18"> <span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">html</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="19"> """
|
||||||
|
</span><span class="line" data-line="20"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Phoenix.HTML.Safe</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">to_iodata</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="21"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="22"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#creating-pages-and-posts" aria-hidden="true" class="anchor" id="creating-pages-and-posts"></a>Creating Pages and Posts</h2>
|
||||||
|
<p>Tableau makes it easy to create both static pages and blog posts. Pages are defined as Elixir modules:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyBlog.Pages.Index</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Tableau.Page</span><span style="color: #e0e2ea;">,</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #8cf8f7;">layout: </span><span style="color: #e0e2ea;">MyBlog.RootLayout</span><span style="color: #e0e2ea;">,</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #8cf8f7;">permalink: </span><span style="color: #b3f6c0;">"/"</span>
|
||||||
|
</span><span class="line" data-line="5">
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">template</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #8cf8f7;">~</span>H"""
|
||||||
|
</span><span class="line" data-line="8"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">h1</span><span style="color: #8cf8f7;">></span>Welcome to My Blog<span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">h1</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="9"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">p</span><span style="color: #8cf8f7;">></span>Built with Elixir and Tableau!<span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">p</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="10"> """
|
||||||
|
</span><span class="line" data-line="11"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="12"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>Posts can be written in Markdown with frontmatter, just like other static site generators:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-markdown" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #8cf8f7;"><span style="color: #e0e2ea; font-weight: bold;">---</span></span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #8cf8f7;"><span style="color: #a6dbff;">title</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">"My First Post"</span></span>
|
||||||
|
</span><span class="line" data-line="3"><span style="color: #8cf8f7;"><span style="color: #a6dbff;">date</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">"2024-01-15"</span></span>
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #8cf8f7;"><span style="color: #a6dbff;">layout</span><span style="color: #e0e2ea;">:</span> <span style="color: #b3f6c0;">MyBlog.PostLayout</span></span>
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #8cf8f7;"><span style="color: #8cf8f7;">---</span></span>
|
||||||
|
</span><span class="line" data-line="6"><span style="color: #8cf8f7;"></span>
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #e0e2ea; font-weight: bold;"># Hello, World!</span>
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;"></span>
|
||||||
|
</span><span class="line" data-line="9">This is my first blog post using Tableau.
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#advanced-features" aria-hidden="true" class="anchor" id="advanced-features"></a>Advanced Features</h2>
|
||||||
|
<h3><a inert href="#component-reusability" aria-hidden="true" class="anchor" id="component-reusability"></a>Component Reusability</h3>
|
||||||
|
<p>One of Tableau's strongest features is its integration with Phoenix LiveView components. You can create reusable components and use them across your site:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyBlog.Components.PostCard</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">use</span> <span style="color: #e0e2ea;">Phoenix.Component</span>
|
||||||
|
</span><span class="line" data-line="3">
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #8cf8f7;">attr</span> <span style="color: #8cf8f7;">:post</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">:map</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">required: </span><span style="color: #e0e2ea;">true</span>
|
||||||
|
</span><span class="line" data-line="5">
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">post_card</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">assigns</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="7"> <span style="color: #8cf8f7;">~</span>H"""
|
||||||
|
</span><span class="line" data-line="8"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">article</span> <span style="color: #8cf8f7;">class</span><span style="color: #e0e2ea;">=</span><span style="color: #b3f6c0;">"<span style="color: #b3f6c0;">post-card</span>"</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="9"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">h2</span><span style="color: #8cf8f7;">></span><span style="color: #8cf8f7;"><%=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">title</span><span style="color: #8cf8f7;"> %></span><span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">h2</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="10"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">time</span><span style="color: #8cf8f7;">></span><span style="color: #8cf8f7;"><%=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">date</span><span style="color: #8cf8f7;"> %></span><span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">time</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="11"> <span style="color: #8cf8f7;"><</span><span style="color: #8cf8f7;">p</span><span style="color: #8cf8f7;">></span><span style="color: #8cf8f7;"><%=</span> <span style="color: #e0e2ea;"><span style="color: #e0e2ea;">@<span style="color: #e0e2ea;">post</span></span></span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">excerpt</span><span style="color: #8cf8f7;"> %></span><span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">p</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="12"> <span style="color: #8cf8f7;"></</span><span style="color: #8cf8f7;">article</span><span style="color: #8cf8f7;">></span>
|
||||||
|
</span><span class="line" data-line="13"> """
|
||||||
|
</span><span class="line" data-line="14"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="15"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h3><a inert href="#data-processing" aria-hidden="true" class="anchor" id="data-processing"></a>Data Processing</h3>
|
||||||
|
<p>Leverage Elixir's powerful data processing capabilities to transform and filter your content:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">MyBlog.Posts</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">recent_posts</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">posts</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">limit</span> <span style="color: #e0e2ea;">\\</span> <span style="color: #e0e2ea;">5</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #e0e2ea;">posts</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">sort_by</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">&</span> <span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">date</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:desc</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">Date</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="5"> <span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">take</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">limit</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="6"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="7">
|
||||||
|
</span><span class="line" data-line="8"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">posts_by_tag</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">posts</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">tag</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="9"> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">filter</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">posts</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">tag</span> <span style="color: #e0e2ea; font-weight: bold;">in</span> <span style="color: #e0e2ea;">&</span><span style="color: #e0e2ea;">1</span><span style="color: #e0e2ea;">.</span><span style="color: #e0e2ea;">tags</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="10"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="11"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#performance-and-seo" aria-hidden="true" class="anchor" id="performance-and-seo"></a>Performance and SEO</h2>
|
||||||
|
<p>Tableau generates fully static HTML, which means:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Lightning-fast load times</strong> - No server-side processing required</li>
|
||||||
|
<li><strong>Excellent SEO</strong> - Search engines can easily crawl static HTML</li>
|
||||||
|
<li><strong>CDN-friendly</strong> - Deploy anywhere static files are supported</li>
|
||||||
|
<li><strong>Security</strong> - No server-side vulnerabilities</li>
|
||||||
|
</ul>
|
||||||
|
<h2><a inert href="#deployment" aria-hidden="true" class="anchor" id="deployment"></a>Deployment</h2>
|
||||||
|
<p>Building your site is as simple as running:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-bash" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #8cf8f7;">mix</span> <span style="color: #e0e2ea;">tableau.build</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>The generated site will be in the <code>_site</code> directory, ready to deploy to any static hosting service like Netlify, Vercel, or GitHub Pages.</p>
|
||||||
|
<h2><a inert href="#conclusion" aria-hidden="true" class="anchor" id="conclusion"></a>Conclusion</h2>
|
||||||
|
<p>Tableau brings the power and elegance of Elixir to static site generation. With its integration with Phoenix LiveView, powerful templating system, and functional programming paradigms, it's an excellent choice for developers who want to build fast, maintainable static sites.</p>
|
||||||
|
<p>Whether you're building a personal blog, documentation site, or marketing pages, Tableau provides the tools and flexibility you need while maintaining the reliability and performance that static sites are known for.</p>
|
||||||
|
<p>Ready to get started? Check out the <a href="https://hexdocs.pm/tableau">official Tableau documentation</a> and start building your next static site with Elixir!</p>
|
||||||
|
|
||||||
|
<time datetime="2024-01-15T00:00:00Z"></time>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<aside class="sidebar">
|
||||||
|
<div class="sidebar-content">
|
||||||
|
<h3>Recent Posts</h3>
|
||||||
|
<ul class="sidebar-posts">
|
||||||
|
<li><a href="/posts/2024-01-15-getting-started-with-tableau">Getting Started with Tableau</a></li>
|
||||||
|
<li><a href="/posts/2024-01-10-functional-programming-benefits">The Benefits of Functional Programming</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Categories</h3>
|
||||||
|
<ul class="sidebar-categories">
|
||||||
|
<li><a href="/posts/elixir">Elixir</a></li>
|
||||||
|
<li><a href="/posts/phoenix">Phoenix</a></li>
|
||||||
|
<li><a href="/posts/functional-programming">Functional Programming</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with Elixir & Tableau.</p>
|
||||||
|
<div class="mobile-nav">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<a href="/posts">Posts</a>
|
||||||
|
<a href="/about">About</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('mobile-menu-toggle').addEventListener('click', function() {
|
||||||
|
document.getElementById('nav-links').classList.toggle('active');
|
||||||
|
this.classList.toggle('active');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log(message) {
|
||||||
|
if (true) {
|
||||||
|
console.log(`[web_dev_utils] ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function connect() {
|
||||||
|
try {
|
||||||
|
window.socket = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
window.socket.onmessage = function(e) {
|
||||||
|
if (e.data === "reload") {
|
||||||
|
log("reloading!");
|
||||||
|
location.reload();
|
||||||
|
} else if (e.data === "subscribed") {
|
||||||
|
log("connected and subscribed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.socket.onopen = () => {
|
||||||
|
waitForConnection(() => {
|
||||||
|
log("sending 'subscribe' message");
|
||||||
|
window.socket.send("subscribe")
|
||||||
|
}
|
||||||
|
, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.socket.onclose = () => {
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForConnection(callback, interval) {
|
||||||
|
log("waiting for connection!")
|
||||||
|
if (window.socket.readyState === 1) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
log("setting a timeout")
|
||||||
|
setTimeout(() => waitForConnection(callback, interval), interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("about to connect");
|
||||||
|
connect();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
131
_site/2024-01-15-test-post/index.html
Normal file
131
_site/2024-01-15-test-post/index.html
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http_equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
Test Post: Getting Started with Elixir | Llmex Blog
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<link rel="stylesheet" href="/css/responsive.css">
|
||||||
|
<meta name="description" content="A simple blog built with Elixir and Tableau">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="mono-all">
|
||||||
|
<aside>
|
||||||
|
<nav>
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1><a inert href="#getting-started-with-elixir" aria-hidden="true" class="anchor" id="getting-started-with-elixir"></a>Getting Started with Elixir</h1>
|
||||||
|
<p>Elixir is a dynamic, functional language designed for building scalable and maintainable applications. It leverages the Erlang VM, known for running low-latency, distributed, and fault-tolerant systems.</p>
|
||||||
|
<h2><a inert href="#key-features" aria-hidden="true" class="anchor" id="key-features"></a>Key Features</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Functional Programming</strong>: Immutable data and pattern matching</li>
|
||||||
|
<li><strong>Scalability</strong>: Built on the Erlang VM for concurrent processing</li>
|
||||||
|
<li><strong>Fault Tolerance</strong>: Supervisor trees and the "let it crash" philosophy</li>
|
||||||
|
<li><strong>Developer Productivity</strong>: Powerful metaprogramming and a helpful compiler</li>
|
||||||
|
</ul>
|
||||||
|
<h2><a inert href="#basic-syntax" aria-hidden="true" class="anchor" id="basic-syntax"></a>Basic Syntax</h2>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Greeting</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">hello</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #b3f6c0;">"Hello, <span style="color: #8cf8f7;">#{</span><span style="color: #e0e2ea;">name</span><span style="color: #8cf8f7;">}</span>!"</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="6">
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #e0e2ea;">IO</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">puts</span> <span style="color: #e0e2ea;">Greeting</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">hello</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">"World"</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#pattern-matching" aria-hidden="true" class="anchor" id="pattern-matching"></a>Pattern Matching</h2>
|
||||||
|
<p>One of Elixir's most powerful features is pattern matching:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Assign value</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">1</span>
|
||||||
|
</span><span class="line" data-line="3">
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #9b9ea4;"># Pattern matching</span>
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #e0e2ea;">{</span><span style="color: #e0e2ea;">a</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">c</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:hello</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">"world"</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">42</span><span style="color: #e0e2ea;">}</span>
|
||||||
|
</span><span class="line" data-line="6">
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #9b9ea4;"># Pattern matching in function definitions</span>
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">result</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">result</span>
|
||||||
|
</span><span class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea; font-weight: bold;">raise</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#the-pipe-operator" aria-hidden="true" class="anchor" id="the-pipe-operator"></a>The Pipe Operator</h2>
|
||||||
|
<p>Elixir's pipe operator (<code>|></code>) allows for clean, readable code:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #b3f6c0;">"Elixir rocks!"</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">upcase</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="3"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">split</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">count</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>This blog post is just a simple introduction to Elixir. Stay tuned for more in-depth tutorials and examples!</p>
|
||||||
|
|
||||||
|
<time datetime="2024-01-15T00:00:00Z"></time>
|
||||||
|
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with <a href="https://github.com/artalar/mono" target="_blank">Mono</a> and <a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a></p>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log(message) {
|
||||||
|
if (true) {
|
||||||
|
console.log(`[web_dev_utils] ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function connect() {
|
||||||
|
try {
|
||||||
|
window.socket = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
window.socket.onmessage = function(e) {
|
||||||
|
if (e.data === "reload") {
|
||||||
|
log("reloading!");
|
||||||
|
location.reload();
|
||||||
|
} else if (e.data === "subscribed") {
|
||||||
|
log("connected and subscribed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.socket.onopen = () => {
|
||||||
|
waitForConnection(() => {
|
||||||
|
log("sending 'subscribe' message");
|
||||||
|
window.socket.send("subscribe")
|
||||||
|
}
|
||||||
|
, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.socket.onclose = () => {
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForConnection(callback, interval) {
|
||||||
|
log("waiting for connection!")
|
||||||
|
if (window.socket.readyState === 1) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
log("setting a timeout")
|
||||||
|
setTimeout(() => waitForConnection(callback, interval), interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("about to connect");
|
||||||
|
connect();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
123
_site/about/index.html
Normal file
123
_site/about/index.html
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http_equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
Llmex Blog
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<link rel="stylesheet" href="/css/responsive.css">
|
||||||
|
<meta name="description" content="A simple blog built with Elixir and Tableau">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="mono-all">
|
||||||
|
<aside>
|
||||||
|
<nav>
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section>
|
||||||
|
<h2>About</h2>
|
||||||
|
<p>
|
||||||
|
Welcome to Llmex Blog, a demonstration of building static sites with Elixir's Tableau generator.
|
||||||
|
This blog showcases the power and elegance of functional programming applied to web development.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Technology Stack</h2>
|
||||||
|
<p>This blog is built using:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a> - Static site generator for Elixir</li>
|
||||||
|
<li><a href="https://github.com/artalar/mono" target="_blank">Mono</a> - A brutalist CSS framework for clean, semantic styling</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Our Philosophy</h2>
|
||||||
|
<p>
|
||||||
|
We believe in the power of functional programming to create maintainable,
|
||||||
|
scalable, and elegant solutions. Through this blog, we aim to share insights,
|
||||||
|
tutorials, and experiences from the world of Elixir and functional web development.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="about-contact">
|
||||||
|
<h2>Get in Touch</h2>
|
||||||
|
<p>
|
||||||
|
Interested in contributing or have questions? Feel free to reach out through
|
||||||
|
our community channels or open an issue on our GitHub repository.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with <a href="https://github.com/artalar/mono" target="_blank">Mono</a> and <a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a></p>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log(message) {
|
||||||
|
if (true) {
|
||||||
|
console.log(`[web_dev_utils] ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function connect() {
|
||||||
|
try {
|
||||||
|
window.socket = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
window.socket.onmessage = function(e) {
|
||||||
|
if (e.data === "reload") {
|
||||||
|
log("reloading!");
|
||||||
|
location.reload();
|
||||||
|
} else if (e.data === "subscribed") {
|
||||||
|
log("connected and subscribed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.socket.onopen = () => {
|
||||||
|
waitForConnection(() => {
|
||||||
|
log("sending 'subscribe' message");
|
||||||
|
window.socket.send("subscribe")
|
||||||
|
}
|
||||||
|
, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.socket.onclose = () => {
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForConnection(callback, interval) {
|
||||||
|
log("waiting for connection!")
|
||||||
|
if (window.socket.readyState === 1) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
log("setting a timeout")
|
||||||
|
setTimeout(() => waitForConnection(callback, interval), interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("about to connect");
|
||||||
|
connect();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
9
_site/css/LICENSE.md
Normal file
9
_site/css/LICENSE.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022-present Artyom Arutyunyan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -1,5 +1,16 @@
|
|||||||
/* Copied from the origin (https://github.com/artalar/mono) for fast interaction and fixing. Reatom docs is a good playground form mono. */
|
/* Copied from the origin (https://github.com/artalar/mono) for fast interaction and fixing. Reatom docs is a good playground form mono. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--mono-main: #10a040;
|
||||||
|
--mono-back: #000000;
|
||||||
|
/* --mono-accent: #007acc;
|
||||||
|
--mono-muted: #666666;
|
||||||
|
--mono-border: #cccccc;
|
||||||
|
--mono-success: #28a745;
|
||||||
|
--mono-warning: #ffc107;
|
||||||
|
--mono-error: #dc3545; */
|
||||||
|
}
|
||||||
|
|
||||||
.mono-all,
|
.mono-all,
|
||||||
.mono,
|
.mono,
|
||||||
.mono-all *,
|
.mono-all *,
|
||||||
@@ -23,7 +34,6 @@
|
|||||||
color: var(--mono-back);
|
color: var(--mono-back);
|
||||||
background-color: var(--mono-main);
|
background-color: var(--mono-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.mono,
|
a.mono,
|
||||||
.mono-all a {
|
.mono-all a {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -87,7 +97,7 @@ body.mono-all,
|
|||||||
.mono-all body {
|
.mono-all body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: min(calc(100vw + 20rem), 85rem);
|
/* min-width: min(calc(100vw + 20rem), 85rem); */
|
||||||
scroll-snap-type: x mandatory;
|
scroll-snap-type: x mandatory;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -98,6 +108,7 @@ main.mono,
|
|||||||
margin: auto;
|
margin: auto;
|
||||||
width: min(100vw, 65rem);
|
width: min(100vw, 65rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
@@ -107,6 +118,7 @@ aside.mono,
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 20rem;
|
width: 20rem;
|
||||||
min-width: 20rem;
|
min-width: 20rem;
|
59
_site/css/responsive.css
Normal file
59
_site/css/responsive.css
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* responsive.css - Simple flexbox layout for the Mono theme */
|
||||||
|
|
||||||
|
/* Base flexbox layout */
|
||||||
|
body.mono-all {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content area takes up remaining space */
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar with fixed width */
|
||||||
|
aside {
|
||||||
|
width: 250px;
|
||||||
|
min-width: 250px;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
border-right: 2px solid var(--mono-main);
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make footer stick to bottom of main content */
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile layout adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
body.mono-all {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
display: none; /* Hide sidebar on mobile */
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple print styles */
|
||||||
|
@media print {
|
||||||
|
aside {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,16 @@
|
|||||||
/* Copied from the origin (https://github.com/artalar/mono) for fast interaction and fixing. Reatom docs is a good playground form mono. */
|
/* Copied from the origin (https://github.com/artalar/mono) for fast interaction and fixing. Reatom docs is a good playground form mono. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--mono-main: #10a040;
|
||||||
|
--mono-back: #000000;
|
||||||
|
/* --mono-accent: #007acc;
|
||||||
|
--mono-muted: #666666;
|
||||||
|
--mono-border: #cccccc;
|
||||||
|
--mono-success: #28a745;
|
||||||
|
--mono-warning: #ffc107;
|
||||||
|
--mono-error: #dc3545; */
|
||||||
|
}
|
||||||
|
|
||||||
.mono-all,
|
.mono-all,
|
||||||
.mono,
|
.mono,
|
||||||
.mono-all *,
|
.mono-all *,
|
||||||
@@ -23,7 +34,6 @@
|
|||||||
color: var(--mono-back);
|
color: var(--mono-back);
|
||||||
background-color: var(--mono-main);
|
background-color: var(--mono-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.mono,
|
a.mono,
|
||||||
.mono-all a {
|
.mono-all a {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -87,7 +97,7 @@ body.mono-all,
|
|||||||
.mono-all body {
|
.mono-all body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: min(calc(100vw + 20rem), 85rem);
|
/* min-width: min(calc(100vw + 20rem), 85rem); */
|
||||||
scroll-snap-type: x mandatory;
|
scroll-snap-type: x mandatory;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -96,8 +106,9 @@ main.mono,
|
|||||||
.mono-all main {
|
.mono-all main {
|
||||||
scroll-snap-align: start;
|
scroll-snap-align: start;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: min(100vw, 65rem);
|
/* width: min(100vw, 65rem); */
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
@@ -107,10 +118,11 @@ aside.mono,
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 20rem;
|
/* width: 20rem;
|
||||||
min-width: 20rem;
|
min-width: 20rem;
|
||||||
max-width: 20rem;
|
max-width: 20rem; */
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
@@ -6,5 +6,49 @@
|
|||||||
<description>My beautiful website</description>
|
<description>My beautiful website</description>
|
||||||
<language>en-us</language>
|
<language>en-us</language>
|
||||||
<generator>Tableau v0.26.0</generator>
|
<generator>Tableau v0.26.0</generator>
|
||||||
|
<item>
|
||||||
|
<title>Test Post: Getting Started with Elixir</title>
|
||||||
|
<link>http://localhost:4999/2024-01-15-test-post</link>
|
||||||
|
<pubDate>Mon, 15 Jan 2024 00:00:00 UTC</pubDate>
|
||||||
|
<guid>http://localhost:4999/2024-01-15-test-post</guid>
|
||||||
|
<description><![CDATA[ <h1><a inert href="#getting-started-with-elixir" aria-hidden="true" class="anchor" id="getting-started-with-elixir"></a>Getting Started with Elixir</h1>
|
||||||
|
<p>Elixir is a dynamic, functional language designed for building scalable and maintainable applications. It leverages the Erlang VM, known for running low-latency, distributed, and fault-tolerant systems.</p>
|
||||||
|
<h2><a inert href="#key-features" aria-hidden="true" class="anchor" id="key-features"></a>Key Features</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Functional Programming</strong>: Immutable data and pattern matching</li>
|
||||||
|
<li><strong>Scalability</strong>: Built on the Erlang VM for concurrent processing</li>
|
||||||
|
<li><strong>Fault Tolerance</strong>: Supervisor trees and the "let it crash" philosophy</li>
|
||||||
|
<li><strong>Developer Productivity</strong>: Powerful metaprogramming and a helpful compiler</li>
|
||||||
|
</ul>
|
||||||
|
<h2><a inert href="#basic-syntax" aria-hidden="true" class="anchor" id="basic-syntax"></a>Basic Syntax</h2>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #e0e2ea; font-weight: bold;">defmodule</span> <span style="color: #e0e2ea;">Greeting</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="2"> <span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">hello</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">name</span><span style="color: #e0e2ea;">)</span> <span style="color: #e0e2ea; font-weight: bold;">do</span>
|
||||||
|
</span><span class="line" data-line="3"> <span style="color: #b3f6c0;">"Hello, <span style="color: #8cf8f7;">#{</span><span style="color: #e0e2ea;">name</span><span style="color: #8cf8f7;">}</span>!"</span>
|
||||||
|
</span><span class="line" data-line="4"> <span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #e0e2ea; font-weight: bold;">end</span>
|
||||||
|
</span><span class="line" data-line="6">
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #e0e2ea;">IO</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">puts</span> <span style="color: #e0e2ea;">Greeting</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">hello</span><span style="color: #e0e2ea;">(</span><span style="color: #b3f6c0;">"World"</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#pattern-matching" aria-hidden="true" class="anchor" id="pattern-matching"></a>Pattern Matching</h2>
|
||||||
|
<p>One of Elixir's most powerful features is pattern matching:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #9b9ea4;"># Assign value</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">x</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">1</span>
|
||||||
|
</span><span class="line" data-line="3">
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #9b9ea4;"># Pattern matching</span>
|
||||||
|
</span><span class="line" data-line="5"><span style="color: #e0e2ea;">{</span><span style="color: #e0e2ea;">a</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">b</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">c</span><span style="color: #e0e2ea;">}</span> <span style="color: #e0e2ea;">=</span> <span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:hello</span><span style="color: #e0e2ea;">,</span> <span style="color: #b3f6c0;">"world"</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">42</span><span style="color: #e0e2ea;">}</span>
|
||||||
|
</span><span class="line" data-line="6">
|
||||||
|
</span><span class="line" data-line="7"><span style="color: #9b9ea4;"># Pattern matching in function definitions</span>
|
||||||
|
</span><span class="line" data-line="8"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:ok</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">result</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea;">result</span>
|
||||||
|
</span><span class="line" data-line="9"><span style="color: #e0e2ea; font-weight: bold;">def</span> <span style="color: #8cf8f7;">process</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">{</span><span style="color: #8cf8f7;">:error</span><span style="color: #e0e2ea;">,</span> <span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">}</span><span style="color: #e0e2ea;">)</span><span style="color: #e0e2ea;">,</span> <span style="color: #8cf8f7;">do: </span><span style="color: #e0e2ea; font-weight: bold;">raise</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">reason</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<h2><a inert href="#the-pipe-operator" aria-hidden="true" class="anchor" id="the-pipe-operator"></a>The Pipe Operator</h2>
|
||||||
|
<p>Elixir's pipe operator (<code>|></code>) allows for clean, readable code:</p>
|
||||||
|
<pre class="athl" style="color: #e0e2ea; background-color: #14161b;"><code class="language-elixir" translate="no" tabindex="0"><span class="line" data-line="1"><span style="color: #b3f6c0;">"Elixir rocks!"</span>
|
||||||
|
</span><span class="line" data-line="2"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">upcase</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="3"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">String</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">split</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span><span class="line" data-line="4"><span style="color: #e0e2ea;">|></span> <span style="color: #e0e2ea;">Enum</span><span style="color: #e0e2ea;">.</span><span style="color: #8cf8f7;">count</span><span style="color: #e0e2ea;">(</span><span style="color: #e0e2ea;">)</span>
|
||||||
|
</span></code></pre>
|
||||||
|
<p>This blog post is just a simple introduction to Elixir. Stay tuned for more in-depth tutorials and examples!</p> ]]></description>
|
||||||
|
</item>
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
@@ -7,17 +7,38 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>
|
<title>
|
||||||
Elixir.Llmex
|
Llmex Blog
|
||||||
</title>
|
</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/simple.css">
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<link rel="stylesheet" href="/css/responsive.css">
|
||||||
|
<meta name="description" content="A simple blog built with Elixir and Tableau">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="mono-all">
|
||||||
|
<aside>
|
||||||
|
<nav>
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<section>
|
||||||
|
<h2>Welcome to Llmex Blog</h2>
|
||||||
<p>
|
<p>
|
||||||
hello, world!
|
A simple blog built with Elixir, Phoenix LiveView, and Tableau static site generator.
|
||||||
|
Explore our latest posts and insights on functional programming, web development, and more.
|
||||||
</p>
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with <a href="https://github.com/artalar/mono" target="_blank">Mono</a> and <a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a></p>
|
||||||
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
125
_site/posts/index.html
Normal file
125
_site/posts/index.html
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http_equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>
|
||||||
|
Llmex Blog
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/mono.css">
|
||||||
|
<link rel="stylesheet" href="/css/responsive.css">
|
||||||
|
<meta name="description" content="A simple blog built with Elixir and Tableau">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="mono-all">
|
||||||
|
<aside>
|
||||||
|
<nav>
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>Latest Posts</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<h2><a href="/2024-01-15-test-post">Test Post: Getting Started with Elixir</a></h2>
|
||||||
|
|
||||||
|
<time datetime="2024-01-15T00:00:00Z"></time>
|
||||||
|
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="posts-archive">
|
||||||
|
<p>
|
||||||
|
Browse through our collection of articles on Elixir, functional programming,
|
||||||
|
web development, and software engineering best practices.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="posts-filter">
|
||||||
|
<h3>Categories</h3>
|
||||||
|
<div class="category-tags">
|
||||||
|
<a href="/posts" class="category-tag active">All Posts</a>
|
||||||
|
<a href="/posts/elixir" class="category-tag">Elixir</a>
|
||||||
|
<a href="/posts/phoenix" class="category-tag">Phoenix</a>
|
||||||
|
<a href="/posts/functional-programming" class="category-tag">Functional Programming</a>
|
||||||
|
<a href="/posts/web-development" class="category-tag">Web Development</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Llmex Blog. Built with <a href="https://github.com/artalar/mono" target="_blank">Mono</a> and <a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a></p>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function log(message) {
|
||||||
|
if (true) {
|
||||||
|
console.log(`[web_dev_utils] ${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function connect() {
|
||||||
|
try {
|
||||||
|
window.socket = new WebSocket('ws://' + location.host + '/ws');
|
||||||
|
|
||||||
|
window.socket.onmessage = function(e) {
|
||||||
|
if (e.data === "reload") {
|
||||||
|
log("reloading!");
|
||||||
|
location.reload();
|
||||||
|
} else if (e.data === "subscribed") {
|
||||||
|
log("connected and subscribed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.socket.onopen = () => {
|
||||||
|
waitForConnection(() => {
|
||||||
|
log("sending 'subscribe' message");
|
||||||
|
window.socket.send("subscribe")
|
||||||
|
}
|
||||||
|
, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.socket.onclose = () => {
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function waitForConnection(callback, interval) {
|
||||||
|
log("waiting for connection!")
|
||||||
|
if (window.socket.readyState === 1) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
log("setting a timeout")
|
||||||
|
setTimeout(() => waitForConnection(callback, interval), interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(e);
|
||||||
|
setTimeout(() => connect(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("about to connect");
|
||||||
|
connect();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
@@ -1 +1 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"><url><loc>http://localhost:4999/</loc></url></urlset>
|
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"><url><loc>http://localhost:4999/about</loc></url><url><loc>http://localhost:4999/</loc></url><url><loc>http://localhost:4999/posts</loc></url><url><lastmod>2024-01-15T00:00:00Z</lastmod><loc>http://localhost:4999/2024-01-15-test-post</loc></url></urlset>
|
9
extra/css/LICENSE.md
Normal file
9
extra/css/LICENSE.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022-present Artyom Arutyunyan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
681
extra/css/mono.css
Normal file
681
extra/css/mono.css
Normal file
@@ -0,0 +1,681 @@
|
|||||||
|
/* Copied from the origin (https://github.com/artalar/mono) for fast interaction and fixing. Reatom docs is a good playground form mono. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--mono-main: #10a040;
|
||||||
|
--mono-back: #000000;
|
||||||
|
/* --mono-accent: #007acc;
|
||||||
|
--mono-muted: #666666;
|
||||||
|
--mono-border: #cccccc;
|
||||||
|
--mono-success: #28a745;
|
||||||
|
--mono-warning: #ffc107;
|
||||||
|
--mono-error: #dc3545; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono-all,
|
||||||
|
.mono,
|
||||||
|
.mono-all *,
|
||||||
|
.mono:before,
|
||||||
|
.mono-all *:before,
|
||||||
|
.mono:after,
|
||||||
|
.mono-all *:after {
|
||||||
|
color: var(--mono-main, #111111);
|
||||||
|
background-color: var(--mono-back, #eeeeee);
|
||||||
|
font-family: "Roboto Mono", monospace;
|
||||||
|
line-height: 2.5rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono::selection,
|
||||||
|
.mono-all *::selection {
|
||||||
|
color: var(--mono-back);
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
}
|
||||||
|
a.mono,
|
||||||
|
.mono-all a {
|
||||||
|
position: relative;
|
||||||
|
/* margin-right: 0.5rem; */
|
||||||
|
text-decoration: none;
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 0.25rem solid var(--mono-main);
|
||||||
|
}
|
||||||
|
a.mono:hover,
|
||||||
|
.mono-all a:hover {
|
||||||
|
border-bottom-width: 0.5rem;
|
||||||
|
}
|
||||||
|
a.mono:visited,
|
||||||
|
.mono-all a:visited {
|
||||||
|
border-bottom: 0.1rem solid var(--mono-main);
|
||||||
|
}
|
||||||
|
a.mono:after,
|
||||||
|
.mono-all a:after {
|
||||||
|
content: "00";
|
||||||
|
color: transparent;
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
clip-path: polygon(25% 25%, 75% 50%, 25% 75%);
|
||||||
|
}
|
||||||
|
a.mono:focus:after,
|
||||||
|
.mono-all a:focus:after {
|
||||||
|
clip-path: polygon(
|
||||||
|
0 0,
|
||||||
|
20% 20%,
|
||||||
|
20% 80%,
|
||||||
|
80% 50%,
|
||||||
|
20% 20%,
|
||||||
|
0 0,
|
||||||
|
100% 0,
|
||||||
|
100% 100%,
|
||||||
|
0 100%
|
||||||
|
);
|
||||||
|
background-color: unset;
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 5px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
a.mono:active:after,
|
||||||
|
.mono-all a:active:after {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 7px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
b.mono,
|
||||||
|
.mono-all b {
|
||||||
|
font-weight: 600;
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.mono,
|
||||||
|
body.mono-all,
|
||||||
|
.mono-all body {
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
/* min-width: min(calc(100vw + 20rem), 85rem); */
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.mono,
|
||||||
|
.mono-all main {
|
||||||
|
scroll-snap-align: start;
|
||||||
|
margin: auto;
|
||||||
|
width: min(100vw, 65rem);
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.mono,
|
||||||
|
.mono-all aside {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 20rem;
|
||||||
|
min-width: 20rem;
|
||||||
|
max-width: 20rem;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
/* TODO: stick it bottom */
|
||||||
|
footer.mono,
|
||||||
|
.mono-all footer {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
border-top: 1px solid var(--mono-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote.mono,
|
||||||
|
.mono-all blockquote {
|
||||||
|
position: relative;
|
||||||
|
margin: 2rem;
|
||||||
|
margin-left: 3rem;
|
||||||
|
}
|
||||||
|
blockquote.mono:before,
|
||||||
|
.mono-all blockquote:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 1rem;
|
||||||
|
width: 3rem;
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
clip-path: polygon(
|
||||||
|
0 0,
|
||||||
|
calc(100% - 0.25rem) 0,
|
||||||
|
calc(100% - 0.25rem) 0.25rem,
|
||||||
|
100% 0.25rem,
|
||||||
|
100% calc(100% - 0.25rem),
|
||||||
|
1rem calc(100% - 0.25rem),
|
||||||
|
1rem 100%,
|
||||||
|
0.25rem 100%,
|
||||||
|
0.25rem calc(100% - 0.25rem),
|
||||||
|
0 calc(100% - 0.25rem)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.mono,
|
||||||
|
.mono-all button {
|
||||||
|
position: relative;
|
||||||
|
outline: none;
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
border-top: none;
|
||||||
|
height: 2rem;
|
||||||
|
min-width: 5rem;
|
||||||
|
color: var(--mono-main);
|
||||||
|
background-color: var(--mono-back);
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
button.mono + button.mono,
|
||||||
|
.mono-all button + button {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
button.mono:before,
|
||||||
|
.mono-all button:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -0.4rem;
|
||||||
|
left: -0.2rem;
|
||||||
|
width: calc(100% + 0.4rem);
|
||||||
|
height: 0.8rem;
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
clip-path: polygon(
|
||||||
|
0 30%,
|
||||||
|
calc(100% - 0.8rem) 30%,
|
||||||
|
calc(100% - 0.8rem) 0%,
|
||||||
|
100% 50%,
|
||||||
|
calc(100% - 0.8rem) 100%,
|
||||||
|
calc(100% - 0.8rem) 70%,
|
||||||
|
0 70%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
button.mono:focus:after,
|
||||||
|
.mono-all button:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.8rem);
|
||||||
|
height: calc(100% + 0.8rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 5px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.5rem) 0.25rem,
|
||||||
|
calc(100% - 0.5rem) calc(100% - 0.5rem),
|
||||||
|
0.25rem calc(100% - 0.5rem),
|
||||||
|
0.25rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.25rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
button.mono:active:after,
|
||||||
|
.mono-all button:active:after {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 7px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code.mono,
|
||||||
|
.mono-all :not(pre) > code {
|
||||||
|
border: 1px dashed var(--mono-main);
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
}
|
||||||
|
:not(pre) > code.mono:before,
|
||||||
|
.mono-all :not(pre) > code:before,
|
||||||
|
:not(pre) > code.mono:after,
|
||||||
|
.mono-all :not(pre) > code:after {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--mono-back);
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
/* fix empty space when scale */
|
||||||
|
box-shadow: 0 0 0 1px var(--mono-main);
|
||||||
|
/* FIXME: does not work */
|
||||||
|
transform: scaleX(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
details.mono,
|
||||||
|
.mono-all details {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
details[open].mono summary.mono,
|
||||||
|
.mono-all details[open] summary {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
details[open].mono summary.mono::before,
|
||||||
|
.mono-all details[open] summary::before {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.mono,
|
||||||
|
.mono-all h1,
|
||||||
|
h2.mono,
|
||||||
|
.mono-all h2,
|
||||||
|
h3.mono,
|
||||||
|
.mono-all h3,
|
||||||
|
h4.mono,
|
||||||
|
.mono-all h4 {
|
||||||
|
border-left: 4px solid var(--mono-main);
|
||||||
|
border-top: 2px solid var(--mono-main);
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-left: 0.3em;
|
||||||
|
margin: 3rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.mono,
|
||||||
|
.mono-all h1 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
font-size: 2.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.mono,
|
||||||
|
.mono-all h2 {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.mono,
|
||||||
|
.mono-all h3 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"].mono,
|
||||||
|
.mono-all input[type="checkbox"] {
|
||||||
|
position: relative;
|
||||||
|
top: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: inset 0 0 0 1rem var(--mono-main);
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type="checkbox"].mono:before,
|
||||||
|
.mono-all input[type="checkbox"]:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 0.125rem;
|
||||||
|
top: 0.125rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
background-color: var(--mono-back);
|
||||||
|
border: 0.25rem solid var(--mono-back);
|
||||||
|
}
|
||||||
|
input[type="checkbox"].mono:checked:before,
|
||||||
|
.mono-all input[type="checkbox"]:checked:before {
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
}
|
||||||
|
input[type="checkbox"].mono:focus:after,
|
||||||
|
.mono-all input[type="checkbox"]:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.4rem);
|
||||||
|
height: calc(100% + 0.4rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 2px,
|
||||||
|
var(--mono-main) 2px 3px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.4rem) 0.2rem,
|
||||||
|
calc(100% - 0.4rem) calc(100% - 0.4rem),
|
||||||
|
0.2rem calc(100% - 0.4rem),
|
||||||
|
0.2rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.2rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
input[type="checkbox"].mono:active:after,
|
||||||
|
.mono-all input[type="checkbox"]:active:after {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 2px,
|
||||||
|
var(--mono-main) 2px 4px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="color"].mono,
|
||||||
|
.mono-all input[type="color"] {
|
||||||
|
position: relative;
|
||||||
|
outline: none;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
input[type="color"].mono:focus:after,
|
||||||
|
.mono-all input[type="color"]:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.6rem);
|
||||||
|
height: calc(100% + 0.6rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 5px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.5rem) 0.25rem,
|
||||||
|
calc(100% - 0.5rem) calc(100% - 0.5rem),
|
||||||
|
0.25rem calc(100% - 0.5rem),
|
||||||
|
0.25rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.25rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
input[type="color"].mono:active:after,
|
||||||
|
.mono-all input[type="color"]:active:after {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 7px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"].mono,
|
||||||
|
.mono-all input[type="radio"] {
|
||||||
|
position: relative;
|
||||||
|
top: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: inset 0 0 0 1rem var(--mono-main);
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input[type="radio"].mono:before,
|
||||||
|
.mono-all input[type="radio"]:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
left: 0.125rem;
|
||||||
|
top: 0.125rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
background-color: var(--mono-back);
|
||||||
|
border: 0.15rem solid var(--mono-back);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
input[type="radio"].mono:checked:before,
|
||||||
|
.mono-all input[type="radio"]:checked:before {
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
}
|
||||||
|
input[type="radio"].mono:focus:after,
|
||||||
|
.mono-all input[type="radio"]:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.4rem);
|
||||||
|
height: calc(100% + 0.4rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 2px,
|
||||||
|
var(--mono-main) 2px 3px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.4rem) 0.2rem,
|
||||||
|
calc(100% - 0.4rem) calc(100% - 0.4rem),
|
||||||
|
0.2rem calc(100% - 0.4rem),
|
||||||
|
0.2rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.2rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"].mono,
|
||||||
|
.mono-all input[type="range"] {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
outline: none;
|
||||||
|
position: relative;
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
padding: 0.8rem 0.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 20rem;
|
||||||
|
}
|
||||||
|
input[type="range"].mono::-webkit-slider-runnable-track,
|
||||||
|
.mono-all input[type="range"]::-webkit-slider-runnable-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 0.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--mono-main);
|
||||||
|
}
|
||||||
|
input[type="range"].mono::-webkit-slider-thumb,
|
||||||
|
.mono-all input[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
background: var(--mono-back);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: -0.4rem;
|
||||||
|
}
|
||||||
|
input[type="range"].mono:active::-webkit-slider-thumb,
|
||||||
|
.mono-all input[type="range"]:active::-webkit-slider-thumb {
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
}
|
||||||
|
input[type="range"].mono:focus:after,
|
||||||
|
.mono-all input[type="range"]:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.7rem);
|
||||||
|
height: calc(100% + 0.7rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 5px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.5rem) 0.25rem,
|
||||||
|
calc(100% - 0.5rem) calc(100% - 0.5rem),
|
||||||
|
0.25rem calc(100% - 0.5rem),
|
||||||
|
0.25rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.25rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.mono,
|
||||||
|
.mono-all fieldset {
|
||||||
|
border: 0.2rem solid var(--mono-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
legend.mono,
|
||||||
|
.mono-all legend {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.mono,
|
||||||
|
.mono-all li {
|
||||||
|
list-style: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
li.mono + li.mono,
|
||||||
|
.mono-all li + li {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
li.mono:before,
|
||||||
|
.mono-all li:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
left: -1.5rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
color: var(--mono-back);
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
/* box-shadow: 0.4rem -0.4rem 0 -0.1rem var(--mono-main); */
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mono:before,
|
||||||
|
.mono-all p:before {
|
||||||
|
content: " ";
|
||||||
|
width: 1.5rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.mono,
|
||||||
|
.mono-all pre {
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border: 2px dashed var(--mono-main);
|
||||||
|
overflow-x: scroll;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.mono,
|
||||||
|
pre.mono *,
|
||||||
|
.mono-all pre,
|
||||||
|
.mono-all pre * {
|
||||||
|
line-height: 2rem;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pre.mono:hover:before,
|
||||||
|
.mono-all pre:hover:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 0.5rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
opacity: 0.7;
|
||||||
|
clip-path: polygon(
|
||||||
|
0 0,
|
||||||
|
0 65%,
|
||||||
|
65% 65%,
|
||||||
|
65% 0,
|
||||||
|
100% 0,
|
||||||
|
100% 100%,
|
||||||
|
35% 100%,
|
||||||
|
35% 35%,
|
||||||
|
100% 35%,
|
||||||
|
100% 0
|
||||||
|
);
|
||||||
|
} */
|
||||||
|
|
||||||
|
strong.mono,
|
||||||
|
.mono-all strong {
|
||||||
|
font-weight: normal;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 -0.2em var(--mono-main),
|
||||||
|
0 0.25em 0 0 var(--mono-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.mono,
|
||||||
|
.mono-all summary {
|
||||||
|
display: inline;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 100%;
|
||||||
|
list-style: none;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
summary.mono:before,
|
||||||
|
.mono-all summary:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -0.5rem;
|
||||||
|
left: -1.2rem;
|
||||||
|
width: 0.8rem;
|
||||||
|
height: calc(100% + 1rem);
|
||||||
|
background-color: var(--mono-main);
|
||||||
|
clip-path: polygon(
|
||||||
|
25% 0,
|
||||||
|
25% calc(100% - 0.6rem),
|
||||||
|
0 calc(100% - 0.6rem),
|
||||||
|
50% 100%,
|
||||||
|
100% calc(100% - 0.6rem),
|
||||||
|
75% calc(100% - 0.6rem),
|
||||||
|
75% 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
summary.mono:focus:after,
|
||||||
|
.mono-all summary:focus:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: calc(100% + 0.8rem);
|
||||||
|
height: calc(100% + 0.4rem);
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 5px
|
||||||
|
);
|
||||||
|
clip-path: polygon(
|
||||||
|
calc(100% - 0.5rem) 0.25rem,
|
||||||
|
calc(100% - 0.5rem) calc(100% - 0.5rem),
|
||||||
|
0.25rem calc(100% - 0.5rem),
|
||||||
|
0.25rem 100%,
|
||||||
|
100% 100%,
|
||||||
|
100% 0.25rem
|
||||||
|
);
|
||||||
|
}
|
||||||
|
summary.mono:active:after,
|
||||||
|
.mono-all summary:active:after {
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
transparent 0 3px,
|
||||||
|
var(--mono-main) 3px 7px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
table.mono,
|
||||||
|
.mono-all table {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
width: max-content;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.mono,
|
||||||
|
.mono-all td,
|
||||||
|
th.mono,
|
||||||
|
.mono-all th {
|
||||||
|
border-top: 0.5rem solid var(--mono-main);
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.mono,
|
||||||
|
.mono-all ul {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
59
extra/css/responsive.css
Normal file
59
extra/css/responsive.css
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* responsive.css - Simple flexbox layout for the Mono theme */
|
||||||
|
|
||||||
|
/* Base flexbox layout */
|
||||||
|
body.mono-all {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content area takes up remaining space */
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar with fixed width */
|
||||||
|
aside {
|
||||||
|
width: 250px;
|
||||||
|
min-width: 250px;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
border-right: 2px solid var(--mono-main);
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make footer stick to bottom of main content */
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile layout adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
body.mono-all {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
display: none; /* Hide sidebar on mobile */
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple print styles */
|
||||||
|
@media print {
|
||||||
|
aside {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
44
lib/layouts/index_layout.ex
Normal file
44
lib/layouts/index_layout.ex
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
defmodule Llmex.IndexLayout do
|
||||||
|
use Tableau.Layout, layout: Llmex.RootLayout
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<h1><%= @page[:title] || "Latest Posts" %></h1>
|
||||||
|
|
||||||
|
<%= if @page[:description] do %>
|
||||||
|
<p><%= @page[:description] %></p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if assigns[:posts] && length(@posts) > 0 do %>
|
||||||
|
<ul>
|
||||||
|
<%= for post <- @posts do %>
|
||||||
|
<li>
|
||||||
|
<h2><a href={post.permalink}><%= post.title %></a></h2>
|
||||||
|
<%= if post[:date] do %>
|
||||||
|
<time datetime={post[:date]}><%= format_date(post[:date]) %></time>
|
||||||
|
<% end %>
|
||||||
|
<%= if post[:description] do %>
|
||||||
|
<p><%= post[:description] %></p>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% else %>
|
||||||
|
<p>No posts found.</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= {:safe, render(@inner_content)} %>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_date(date_string) when is_binary(date_string) do
|
||||||
|
case Date.from_iso8601(date_string) do
|
||||||
|
{:ok, date} -> Calendar.strftime(date, "%B %d, %Y")
|
||||||
|
{:error, _} -> date_string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_date(%Date{} = date), do: Calendar.strftime(date, "%B %d, %Y")
|
||||||
|
defp format_date(_), do: ""
|
||||||
|
end
|
10
lib/layouts/page_layout.ex
Normal file
10
lib/layouts/page_layout.ex
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
defmodule Llmex.PageLayout do
|
||||||
|
use Tableau.Layout, layout: Llmex.RootLayout
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<%= {:safe, render(@inner_content)} %>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
@@ -5,6 +5,19 @@ defmodule Llmex.PostLayout do
|
|||||||
def template(assigns) do
|
def template(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<%= {:safe, render(@inner_content)} %>
|
<%= {:safe, render(@inner_content)} %>
|
||||||
|
<%= if @page[:date] do %>
|
||||||
|
<time datetime={@page[:date]}><%= format_date(@page[:date]) %></time>
|
||||||
|
<% end %>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp format_date(date_string) when is_binary(date_string) do
|
||||||
|
case Date.from_iso8601(date_string) do
|
||||||
|
{:ok, date} -> Calendar.strftime(date, "%B %d, %Y")
|
||||||
|
{:error, _} -> date_string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format_date(%Date{} = date), do: Calendar.strftime(date, "%B %d, %Y")
|
||||||
|
defp format_date(_), do: ""
|
||||||
end
|
end
|
||||||
|
@@ -13,18 +13,35 @@ defmodule Llmex.RootLayout do
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<title>
|
<title>
|
||||||
<%= [@page[:title], Llmex]
|
<%= [@page[:title], "Llmex Blog"]
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|> Enum.intersperse("|")
|
|> Enum.intersperse("|")
|
||||||
|> Enum.join(" ") %>
|
|> Enum.join(" ") %>
|
||||||
</title>
|
</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/simple.css" />
|
<link rel="stylesheet" href="/css/mono.css" />
|
||||||
|
<link rel="stylesheet" href="/css/responsive.css" />
|
||||||
|
<meta name="description" content={@page[:description] || "A simple blog built with Elixir and Tableau"} />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="mono-all">
|
||||||
|
<aside>
|
||||||
|
<nav>
|
||||||
|
<h1><a href="/">Llmex Blog</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/posts">Posts</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<%= render @inner_content %>
|
<%= render @inner_content %>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© <%= Date.utc_today().year %> Llmex Blog. Built with <a href="https://github.com/artalar/mono" target="_blank">Mono</a> and <a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a></p>
|
||||||
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
44
lib/pages/about.ex
Normal file
44
lib/pages/about.ex
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
defmodule Llmex.Pages.About do
|
||||||
|
use Tableau.Page,
|
||||||
|
layout: Llmex.PageLayout,
|
||||||
|
permalink: "/about"
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<section>
|
||||||
|
<h2>About</h2>
|
||||||
|
<p>
|
||||||
|
Welcome to Llmex Blog, a demonstration of building static sites with Elixir's Tableau generator.
|
||||||
|
This blog showcases the power and elegance of functional programming applied to web development.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Technology Stack</h2>
|
||||||
|
<p>This blog is built using:</p>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://github.com/elixir-tools/tableau" target="_blank">Tableau</a> - Static site generator for Elixir</li>
|
||||||
|
<li><a href="https://github.com/artalar/mono" target="_blank">Mono</a> - A brutalist CSS framework for clean, semantic styling</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Our Philosophy</h2>
|
||||||
|
<p>
|
||||||
|
We believe in the power of functional programming to create maintainable,
|
||||||
|
scalable, and elegant solutions. Through this blog, we aim to share insights,
|
||||||
|
tutorials, and experiences from the world of Elixir and functional web development.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="about-contact">
|
||||||
|
<h2>Get in Touch</h2>
|
||||||
|
<p>
|
||||||
|
Interested in contributing or have questions? Feel free to reach out through
|
||||||
|
our community channels or open an issue on our GitHub repository.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
18
lib/pages/home.ex
Normal file
18
lib/pages/home.ex
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
defmodule Llmex.Pages.Index do
|
||||||
|
use Tableau.Page,
|
||||||
|
layout: Llmex.RootLayout,
|
||||||
|
permalink: "/"
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<section>
|
||||||
|
<h2>Welcome to Llmex Blog</h2>
|
||||||
|
<p>
|
||||||
|
A simple blog built with Elixir, Phoenix LiveView, and Tableau static site generator.
|
||||||
|
Explore our latest posts and insights on functional programming, web development, and more.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
@@ -1,15 +0,0 @@
|
|||||||
defmodule Llmex.HomePage do
|
|
||||||
use Tableau.Page,
|
|
||||||
layout: Llmex.RootLayout,
|
|
||||||
permalink: "/"
|
|
||||||
|
|
||||||
use Phoenix.Component
|
|
||||||
|
|
||||||
def template(assigns) do
|
|
||||||
~H"""
|
|
||||||
<p>
|
|
||||||
hello, world!
|
|
||||||
</p>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
36
lib/pages/index.ex
Normal file
36
lib/pages/index.ex
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
defmodule Llmex.Pages.Index do
|
||||||
|
use Tableau.Page,
|
||||||
|
layout: Llmex.RootLayout,
|
||||||
|
permalink: "/"
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<section class="hero">
|
||||||
|
<h2>Welcome to Llmex Blog</h2>
|
||||||
|
<p>
|
||||||
|
A simple blog built with Elixir, Phoenix LiveView, and Tableau static site generator.
|
||||||
|
Explore our latest posts and insights on functional programming, web development, and more.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="featured-content">
|
||||||
|
<h3>Featured Topics</h3>
|
||||||
|
<div class="topics-grid">
|
||||||
|
<div class="topic-card">
|
||||||
|
<h4>Elixir & Phoenix</h4>
|
||||||
|
<p>Deep dives into the Elixir ecosystem and Phoenix framework.</p>
|
||||||
|
</div>
|
||||||
|
<div class="topic-card">
|
||||||
|
<h4>Functional Programming</h4>
|
||||||
|
<p>Exploring functional programming concepts and patterns.</p>
|
||||||
|
</div>
|
||||||
|
<div class="topic-card">
|
||||||
|
<h4>Web Development</h4>
|
||||||
|
<p>Modern web development techniques and best practices.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
28
lib/pages/posts.ex
Normal file
28
lib/pages/posts.ex
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
defmodule Llmex.Pages.Posts do
|
||||||
|
use Tableau.Page,
|
||||||
|
layout: Llmex.IndexLayout,
|
||||||
|
permalink: "/posts"
|
||||||
|
use Phoenix.Component
|
||||||
|
|
||||||
|
def template(assigns) do
|
||||||
|
~H"""
|
||||||
|
<section class="posts-archive">
|
||||||
|
<p>
|
||||||
|
Browse through our collection of articles on Elixir, functional programming,
|
||||||
|
web development, and software engineering best practices.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="posts-filter">
|
||||||
|
<h3>Categories</h3>
|
||||||
|
<div class="category-tags">
|
||||||
|
<a href="/posts" class="category-tag active">All Posts</a>
|
||||||
|
<a href="/posts/elixir" class="category-tag">Elixir</a>
|
||||||
|
<a href="/posts/phoenix" class="category-tag">Phoenix</a>
|
||||||
|
<a href="/posts/functional-programming" class="category-tag">Functional Programming</a>
|
||||||
|
<a href="/posts/web-development" class="category-tag">Web Development</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user