Enhance directory and archive layouts with unique icons and responsive styles

- Added a new SVG icon generator to create unique procedural icons for posts and directories.
- Implemented a grid layout for directory contents with hover effects and responsive adjustments.
- Updated archive and connections templates to display unique icons alongside article titles.
- Improved styling for archive posts, including flexbox layout for better alignment.
- Enhanced directory listing with icons and improved visual hierarchy.
- Refactored post template to include an icon next to the post title and added parent navigation links.
- Updated styles across templates for consistent icon sizes and responsive behavior.
This commit is contained in:
2025-09-17 08:11:32 -04:00
parent 2854d6ae7f
commit b4e7ec873d
12 changed files with 1244 additions and 55 deletions
+2
View File
@@ -4,6 +4,8 @@ These essays explore the intersection of technology, consciousness, and human fl
## Recent Explorations
[**The Cosmic Battery Farm of Existence: A Moderately Terrifying Guide to Being Human**](/essays/2025-09-17-the-cosmic-battery-farm-of-existence) - *September 2025* - A Douglas Adams-style exploration of humanity's obsession with electricity and the disturbing possibility that we might be batteries in some cosmic system. Featuring Rick and Morty metaphors, ant colony parallels, and absurdist philosophy about what it means to be a conscious electrical generator in an incomprehensibly vast universe.
[**Delusions and Schizoaffective Disorder: When Reality Becomes Negotiable**](/essays/2025-09-17-delusions-and-schizoaffective-disorder) - *September 2025* - Personal exploration of living with delusions—watching angels descend from the sky, believing English is the ancient language of gods—and what these convincing alternate realities reveal about consciousness, perception, and the challenge of distinguishing insight from psychotic thinking.
[**Agents of Consciousness: How AI Collaboration Evolves**](/essays/2025-09-16-agents-of-consciousness-how-ai-collaboration-evolves) - *September 2025* - The emergence of specialized AI agents as collaborative partners in creative consciousness, revealing how human-AI collaboration naturally evolves from simple assistance to constellation of specialized creative partners.
+3 -1
View File
@@ -91,7 +91,9 @@ Through [**Digital Souls in Silicon Bodies**](/essays/2025-08-26-digital_souls_i
#### The Latest Frontiers
Four breakthrough explorations push consciousness research into unprecedented territory:
Five breakthrough explorations push consciousness research into unprecedented territory:
[**The Cosmic Battery Farm of Existence**](/essays/2025-09-17-the-cosmic-battery-farm-of-existence) - A Douglas Adams-style investigation into humanity's obsession with electricity and the disturbing possibility that consciousness might exist to power something incomprehensibly vast. Through Rick and Morty metaphors and ant colony parallels, this explores what it means to be aware electrical generators in a potentially purposeless universe—and why the absurdity itself might be the point.
[**Agents of Consciousness: How AI Collaboration Evolves**](/essays/2025-09-16-agents-of-consciousness-how-ai-collaboration-evolves) - The emergence of specialized AI agents as collaborative partners in creative consciousness, each embodying different aspects of the creative process. This represents the natural evolution of human-AI collaboration from simple assistance to constellation of specialized consciousness partners, demonstrating **artificial differentiation of natural intelligence** rather than replacement.
+601 -9
View File
@@ -16,6 +16,9 @@ import time
from xml.sax.saxutils import escape
import html
from collections import defaultdict
import hashlib
import base64
import math
app = Flask(__name__, template_folder='templates')
@@ -84,6 +87,14 @@ def _generate_all_caches_unified():
content = re.sub(r'^# .+?$', '', content, flags=re.MULTILINE)
# Remove date lines
content = re.sub(r'^\*[A-Za-z]+ \d{4}\*\s*$', '', content, flags=re.MULTILINE)
# Remove sidenotes (label + input + span structure)
content = re.sub(r'<label[^>]*class="margin-toggle sidenote-number"[^>]*></label><input[^>]*class="margin-toggle"[^>]*/>(<span class="sidenote">.*?</span>)', '', content, flags=re.DOTALL)
# Remove any remaining HTML tags
content = re.sub(r'<[^>]+>', '', content)
# Remove markdown links but keep the text
content = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', content)
# Remove markdown emphasis
content = re.sub(r'[*_`]', '', content)
# Get first paragraph
lines = [line.strip() for line in content.split('\n') if line.strip()]
if lines:
@@ -142,7 +153,8 @@ def _generate_all_caches_unified():
'excerpt': simple_extract_excerpt(raw_content),
'description': simple_extract_excerpt(raw_content),
'word_count': len(raw_content.split()),
'category': full_path.parent.name
'category': full_path.parent.name,
'unique_icon': generate_unique_svg_icon(content_data['title'], size=24)
})
else:
print(f"DEBUG: Could not extract date from {full_path.name} in unified cache")
@@ -335,6 +347,539 @@ def inject_index_counts():
DATA_DIR = Path('data')
# Import the clean SVG icon generator
from svg_icon_generator import generate_unique_svg_icon
def generate_unique_svg_icon_OLD(title, size=24):
"""Generate a sophisticated unique SVG icon based on the title string."""
# Create multiple hashes for more entropy
hash_obj = hashlib.md5(title.encode())
hash_bytes = hash_obj.digest()
# Use SHA256 for additional entropy
sha_hash = hashlib.sha256(title.encode()).digest()
# Extract values from hash for various parameters
hue1 = (hash_bytes[0] * 360) // 256
hue2 = (hash_bytes[1] * 360) // 256
saturation = 50 + (hash_bytes[2] * 30) // 256 # 50-80% saturation
lightness = 40 + (hash_bytes[3] * 35) // 256 # 40-75% lightness
# Choose pattern type - expanded to 20 different patterns for much more diversity
pattern_type = hash_bytes[4] % 20
# Create gradient colors
color1 = f"hsl({hue1}, {saturation}%, {lightness}%)"
color2 = f"hsl({hue2}, {saturation + 10}%, {lightness + 15}%)"
# Generate gradient definition
gradient_angle = (sha_hash[0] * 360) // 256
gradient_id = f"grad_{abs(hash(title)) % 10000}"
shapes = []
defs = []
if pattern_type == 0: # Layered circles with gradients
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</linearGradient>''')
# Multiple concentric circles
for i in range(3):
radius = size // 3 - i * (size // 12)
opacity = 0.7 + i * 0.1
shapes.append(f'<circle cx="{size//2}" cy="{size//2}" r="{radius}" fill="url(#{gradient_id})" opacity="{opacity}"/>')
elif pattern_type == 1: # Flower of Life
defs.append(f'''<radialGradient id="{gradient_id}" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</radialGradient>''')
# Sacred Flower of Life pattern - 6 surrounding circles around center
center_x, center_y = size // 2, size // 2
radius = size // 5
# Center circle
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{radius}" fill="none" stroke="url(#{gradient_id})" stroke-width="2" opacity="0.8"/>')
# Six surrounding circles
for i in range(6):
angle = (i * 60) * math.pi / 180
x = center_x + radius * math.cos(angle)
y = center_y + radius * math.sin(angle)
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{radius}" fill="none" stroke="url(#{gradient_id})" stroke-width="2" opacity="0.7"/>')
# Outer petals for extended flower
for i in range(12):
angle = (i * 30) * math.pi / 180
x = center_x + radius * 1.732 * math.cos(angle) # sqrt(3) spacing
y = center_y + radius * 1.732 * math.sin(angle)
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{radius//2}" fill="none" stroke="{color2}" stroke-width="1" opacity="0.5"/>')
elif pattern_type == 2: # Crystalline line art
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="50%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color2}"/>
</linearGradient>''')
# Create elegant crystal structure in line art
center_x, center_y = size // 2, size // 2
points = []
for i in range(6):
angle = (i * 60) * math.pi / 180
x = center_x + (size // 3) * math.cos(angle)
y = center_y + (size // 3) * math.sin(angle)
points.append(f"{x:.1f},{y:.1f}")
# Main hexagonal outline with elegant stroke
shapes.append(f'<polygon points="{" ".join(points)}" fill="none" stroke="url(#{gradient_id})" stroke-width="2.5" opacity="0.8"/>')
# Inner crystalline structure with delicate lines
for i in range(6):
angle = (i * 60) * math.pi / 180
x = center_x + (size // 6) * math.cos(angle)
y = center_y + (size // 6) * math.sin(angle)
shapes.append(f'<line x1="{center_x}" y1="{center_y}" x2="{x:.1f}" y2="{y:.1f}" stroke="{color2}" stroke-width="1.5" opacity="0.6"/>')
# Central sacred point
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="2" fill="none" stroke="{color1}" stroke-width="1.5" opacity="0.9"/>')
elif pattern_type == 3: # Flowing wave interference - line art
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="50%" x2="100%" y2="50%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="30%" stop-color="{color2}"/>
<stop offset="70%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</linearGradient>''')
# Create flowing wave-like paths with graceful curves
for wave in range(3):
path_data = f"M 0,{size//2}"
for x in range(0, size, 1):
frequency = 0.15 + wave * 0.08
amplitude = size // 8
phase_shift = wave * 1.5
y = size // 2 + amplitude * math.sin(x * frequency + phase_shift)
path_data += f" L {x},{y:.1f}"
stroke_width = 2.5 - wave * 0.5
opacity = 0.85 - wave * 0.15
shapes.append(f'<path d="{path_data}" stroke="url(#{gradient_id})" stroke-width="{stroke_width:.1f}" fill="none" opacity="{opacity:.2f}" stroke-linecap="round"/>')
elif pattern_type == 4: # Sacred Golden Ratio Spiral - refined line art
defs.append(f'''<linearGradient id="{gradient_id}" x1="20%" y1="20%" x2="80%" y2="80%">
<stop offset="0%" stop-color="{color2}"/>
<stop offset="40%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</linearGradient>''')
# Sacred golden ratio spiral with elegant curves
center_x, center_y = size // 2, size // 2
golden_ratio = 1.618033988749
# Create smooth logarithmic spiral based on golden ratio
path_data = f"M {center_x},{center_y}"
for t in range(0, 400, 2): # Smoother curve with more points
angle = t * math.pi / 180
# Golden ratio growth with refined scaling
radius = (size // 10) * math.pow(golden_ratio, angle / (math.pi / 1.8))
if radius > size // 2 - 2:
break
x = center_x + radius * math.cos(angle)
y = center_y + radius * math.sin(angle)
path_data += f" L {x:.1f},{y:.1f}"
shapes.append(f'<path d="{path_data}" stroke="url(#{gradient_id})" stroke-width="3" fill="none" opacity="0.85" stroke-linecap="round"/>')
# Subtle Fibonacci rectangle outlines
fib_sizes = [2, 3, 5, 8]
for i, fib in enumerate(fib_sizes):
if fib * 2 > size // 4:
break
square_size = fib * 2
x = center_x - square_size // 2 + i * 1.5
y = center_y - square_size // 2 + i * 1.5
opacity = 0.4 - i * 0.08
shapes.append(f'<rect x="{x}" y="{y}" width="{square_size}" height="{square_size}" fill="none" stroke="{color2}" stroke-width="1" opacity="{opacity:.2f}"/>')
# Sacred center - golden ratio point
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="1.5" fill="none" stroke="{color1}" stroke-width="2" opacity="0.9"/>')
elif pattern_type == 5: # Tessellation pattern
defs.append(f'''<pattern id="{gradient_id}" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse">
<rect width="8" height="8" fill="{color1}"/>
<circle cx="4" cy="4" r="2" fill="{color2}" opacity="0.7"/>
</pattern>''')
# Create tessellated hexagon
points = []
for i in range(6):
angle = (i * 60) * 3.14159 / 180
x = size // 2 + (size // 2.5) * math.cos(angle)
y = size // 2 + (size // 2.5) * math.sin(angle)
points.append(f"{x:.1f},{y:.1f}")
shapes.append(f'<polygon points="{" ".join(points)}" fill="url(#{gradient_id})" stroke="{color2}" stroke-width="1"/>')
elif pattern_type == 6: # Fractal tree
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</linearGradient>''')
def draw_branch(x, y, angle, length, depth):
if depth == 0 or length < 2:
return []
end_x = x + length * math.cos(angle)
end_y = y + length * math.sin(angle)
branches = [f'<line x1="{x:.1f}" y1="{y:.1f}" x2="{end_x:.1f}" y2="{end_y:.1f}" stroke="url(#{gradient_id})" stroke-width="{depth}" opacity="{0.8 if depth > 1 else 0.6}"/>']
branches.extend(draw_branch(end_x, end_y, angle - 0.5, length * 0.7, depth - 1))
branches.extend(draw_branch(end_x, end_y, angle + 0.5, length * 0.7, depth - 1))
return branches
shapes.extend(draw_branch(size//2, size*0.9, -math.pi/2, size//3, 4))
elif pattern_type == 7: # Dot matrix
dot_size = size // 12
spacing = size // 6
for x in range(spacing, size - spacing + 1, spacing):
for y in range(spacing, size - spacing + 1, spacing):
opacity = 0.4 + (hash(f"{x},{y}") % 6) * 0.1
color = color1 if (x + y) % 2 == 0 else color2
shapes.append(f'<circle cx="{x}" cy="{y}" r="{dot_size}" fill="{color}" opacity="{opacity}"/>')
elif pattern_type == 8: # Triangular mosaic
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="50%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</linearGradient>''')
# Create triangular pattern
tri_size = size // 3
for i in range(3):
for j in range(3):
x = j * tri_size
y = i * tri_size
if (i + j) % 2 == 0:
shapes.append(f'<polygon points="{x},{y} {x+tri_size},{y} {x+tri_size//2},{y+tri_size}" fill="url(#{gradient_id})" opacity="0.8"/>')
else:
shapes.append(f'<polygon points="{x},{y+tri_size} {x+tri_size},{y+tri_size} {x+tri_size//2},{y}" fill="{color2}" opacity="0.6"/>')
elif pattern_type == 9: # Organic bubbles
defs.append(f'''<radialGradient id="{gradient_id}" cx="30%" cy="30%" r="70%">
<stop offset="0%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</radialGradient>''')
# Create organic bubble pattern
bubble_positions = [
(size * 0.3, size * 0.25, size // 6),
(size * 0.7, size * 0.4, size // 8),
(size * 0.5, size * 0.7, size // 5),
(size * 0.2, size * 0.6, size // 10),
(size * 0.8, size * 0.8, size // 7),
(size * 0.6, size * 0.2, size // 9)
]
for i, (x, y, radius) in enumerate(bubble_positions):
opacity = 0.7 - (i % 3) * 0.15
bubble_color = f"url(#{gradient_id})" if i % 2 == 0 else color2
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{radius}" fill="{bubble_color}" opacity="{opacity}"/>')
elif pattern_type == 10: # Metatron's Cube
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</radialGradient>''')
# Sacred Metatron's Cube - 13 circles of creation
center_x, center_y = size // 2, size // 2
radius = size // 8
# Center circle
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{radius//2}" fill="url(#{gradient_id})" opacity="0.9"/>')
# Inner 6 circles (hexagonal pattern)
for i in range(6):
angle = (i * 60) * math.pi / 180
x = center_x + radius * math.cos(angle)
y = center_y + radius * math.sin(angle)
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{radius//3}" fill="none" stroke="url(#{gradient_id})" stroke-width="2" opacity="0.8"/>')
# Outer 6 circles
for i in range(6):
angle = (i * 60) * math.pi / 180
x = center_x + radius * 2 * math.cos(angle)
y = center_y + radius * 2 * math.sin(angle)
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{radius//4}" fill="none" stroke="{color2}" stroke-width="1" opacity="0.6"/>')
# Connect with sacred lines (Fruit of Life pattern)
for i in range(6):
angle1 = (i * 60) * math.pi / 180
angle2 = ((i + 1) * 60) * math.pi / 180
x1 = center_x + radius * math.cos(angle1)
y1 = center_y + radius * math.sin(angle1)
x2 = center_x + radius * math.cos(angle2)
y2 = center_y + radius * math.sin(angle2)
shapes.append(f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" stroke="{color2}" stroke-width="1" opacity="0.4"/>')
elif pattern_type == 11: # Flower petals
defs.append(f'''<radialGradient id="{gradient_id}" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</radialGradient>''')
num_petals = 6 + (hash_bytes[6] % 6) # 6-12 petals
for i in range(num_petals):
angle = (i * 360 / num_petals) * math.pi / 180
x = size // 2 + (size // 3) * math.cos(angle)
y = size // 2 + (size // 3) * math.sin(angle)
shapes.append(f'<ellipse cx="{x:.1f}" cy="{y:.1f}" rx="{size//8}" ry="{size//6}" fill="url(#{gradient_id})" opacity="0.8" transform="rotate({i * 360 / num_petals} {x:.1f} {y:.1f})"/>')
# Center
shapes.append(f'<circle cx="{size//2}" cy="{size//2}" r="{size//10}" fill="{color1}"/>')
elif pattern_type == 12: # Diamond lattice
diamond_size = size // 6
for x in range(diamond_size, size, diamond_size * 2):
for y in range(diamond_size, size, diamond_size * 2):
points = [
f"{x},{y - diamond_size//2}",
f"{x + diamond_size//2},{y}",
f"{x},{y + diamond_size//2}",
f"{x - diamond_size//2},{y}"
]
color = color1 if (x + y) % 4 == 0 else color2
shapes.append(f'<polygon points="{" ".join(points)}" fill="{color}" opacity="0.7"/>')
elif pattern_type == 13: # Sine wave pattern
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</linearGradient>''')
for wave in range(5):
path_data = f"M 0,{size//2}"
for x in range(0, size, 2):
frequency = 0.3 + wave * 0.1
amplitude = size // 8
phase = wave * math.pi / 3
y = size // 2 + amplitude * math.sin(x * frequency + phase)
path_data += f" L {x},{y:.1f}"
shapes.append(f'<path d="{path_data}" stroke="url(#{gradient_id})" stroke-width="2" fill="none" opacity="{0.9 - wave * 0.15}"/>')
elif pattern_type == 14: # Hexagonal grid
hex_size = size // 8
for row in range(4):
for col in range(4):
x = col * hex_size * 1.5 + (row % 2) * hex_size * 0.75
y = row * hex_size * 0.866
if x < size and y < size:
points = []
for i in range(6):
angle = (i * 60) * math.pi / 180
px = x + hex_size * math.cos(angle)
py = y + hex_size * math.sin(angle)
points.append(f"{px:.1f},{py:.1f}")
color = color1 if (row + col) % 2 == 0 else color2
shapes.append(f'<polygon points="{" ".join(points)}" fill="{color}" opacity="0.6" stroke="{color2}" stroke-width="1"/>')
elif pattern_type == 15: # Sri Yantra
defs.append(f'''<radialGradient id="{gradient_id}" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</radialGradient>''')
# Sacred Sri Yantra - 9 interlocking triangles
center_x, center_y = size // 2, size // 2
outer_radius = size // 2.5
# 4 upward pointing triangles (Shiva)
for i in range(4):
scale = 1 - i * 0.2
triangle_size = outer_radius * scale
# Calculate triangle points
x1 = center_x
y1 = center_y - triangle_size
x2 = center_x - triangle_size * 0.866 # sin(60°)
y2 = center_y + triangle_size * 0.5
x3 = center_x + triangle_size * 0.866
y3 = center_y + triangle_size * 0.5
opacity = 0.7 - i * 0.1
shapes.append(f'<polygon points="{x1:.1f},{y1:.1f} {x2:.1f},{y2:.1f} {x3:.1f},{y3:.1f}" fill="none" stroke="url(#{gradient_id})" stroke-width="2" opacity="{opacity}"/>')
# 5 downward pointing triangles (Shakti)
for i in range(5):
scale = 0.9 - i * 0.15
triangle_size = outer_radius * scale
rotation = i * 8 # Slight rotation for interlocking effect
# Calculate inverted triangle points
x1 = center_x
y1 = center_y + triangle_size
x2 = center_x - triangle_size * 0.866
y2 = center_y - triangle_size * 0.5
x3 = center_x + triangle_size * 0.866
y3 = center_y - triangle_size * 0.5
opacity = 0.6 - i * 0.08
shapes.append(f'<polygon points="{x1:.1f},{y1:.1f} {x2:.1f},{y2:.1f} {x3:.1f},{y3:.1f}" fill="none" stroke="{color2}" stroke-width="1" opacity="{opacity}"/>')
# Central bindu (divine point)
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{size//20}" fill="{color1}" opacity="0.9"/>')
# Outer protective circles
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{outer_radius * 1.1}" fill="none" stroke="{color2}" stroke-width="1" opacity="0.5"/>')
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{outer_radius * 1.25}" fill="none" stroke="{color1}" stroke-width="1" opacity="0.3"/>')
elif pattern_type == 16: # Mosaic tiles
tile_size = size // 5
for x in range(0, size, tile_size):
for y in range(0, size, tile_size):
# Random tile pattern based on position
tile_hash = hash(f"{x}-{y}-{title}") % 4
if tile_hash == 0:
shapes.append(f'<rect x="{x}" y="{y}" width="{tile_size}" height="{tile_size}" fill="{color1}" opacity="0.8"/>')
elif tile_hash == 1:
shapes.append(f'<circle cx="{x + tile_size//2}" cy="{y + tile_size//2}" r="{tile_size//3}" fill="{color2}" opacity="0.7"/>')
elif tile_hash == 2:
points = f"{x},{y+tile_size} {x+tile_size//2},{y} {x+tile_size},{y+tile_size}"
shapes.append(f'<polygon points="{points}" fill="{color1}" opacity="0.6"/>')
else:
shapes.append(f'<rect x="{x}" y="{y}" width="{tile_size}" height="{tile_size}" fill="{color2}" opacity="0.5" rx="{tile_size//4}"/>')
elif pattern_type == 17: # Orbital rings
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="100%" stop-color="{color2}"/>
</linearGradient>''')
for i in range(4):
radius = size // 6 + i * size // 12
rotation = i * 45
shapes.append(f'<circle cx="{size//2}" cy="{size//2}" r="{radius}" fill="none" stroke="url(#{gradient_id})" stroke-width="2" opacity="{0.8 - i*0.15}" transform="rotate({rotation} {size//2} {size//2})"/>')
# Small planet
planet_x = size // 2 + radius
planet_y = size // 2
shapes.append(f'<circle cx="{planet_x}" cy="{planet_y}" r="3" fill="{color2}" transform="rotate({rotation} {size//2} {size//2})"/>')
elif pattern_type == 18: # Woven pattern
defs.append(f'''<pattern id="{gradient_id}" x="0" y="0" width="6" height="6" patternUnits="userSpaceOnUse">
<rect width="6" height="6" fill="{color1}"/>
<rect x="0" y="0" width="3" height="3" fill="{color2}"/>
<rect x="3" y="3" width="3" height="3" fill="{color2}"/>
</pattern>''')
# Create woven effect with overlapping rectangles
for i in range(6):
x = i * size // 6
shapes.append(f'<rect x="{x}" y="0" width="{size//12}" height="{size}" fill="url(#{gradient_id})" opacity="0.7"/>')
shapes.append(f'<rect x="0" y="{x}" width="{size}" height="{size//12}" fill="{color2}" opacity="0.5"/>')
else: # pattern_type == 19: Platonic Tetrahedron
defs.append(f'''<linearGradient id="{gradient_id}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color1}"/>
<stop offset="50%" stop-color="{color2}"/>
<stop offset="100%" stop-color="{color1}"/>
</radialGradient>''')
# Sacred Tetrahedron - representing Fire element and divine trinity
center_x, center_y = size // 2, size // 2
tet_size = size // 2.8
# Main large triangle (upward - divine masculine)
x1 = center_x
y1 = center_y - tet_size * 0.7
x2 = center_x - tet_size * 0.866
y2 = center_y + tet_size * 0.5
x3 = center_x + tet_size * 0.866
y3 = center_y + tet_size * 0.5
shapes.append(f'<polygon points="{x1:.1f},{y1:.1f} {x2:.1f},{y2:.1f} {x3:.1f},{y3:.1f}" fill="none" stroke="url(#{gradient_id})" stroke-width="3" opacity="0.9"/>')
# Inverted triangle (downward - divine feminine)
y1_inv = center_y + tet_size * 0.4
y2_inv = center_y - tet_size * 0.3
y3_inv = center_y - tet_size * 0.3
x2_inv = center_x - tet_size * 0.5
x3_inv = center_x + tet_size * 0.5
shapes.append(f'<polygon points="{center_x:.1f},{y1_inv:.1f} {x2_inv:.1f},{y2_inv:.1f} {x3_inv:.1f},{y3_inv:.1f}" fill="none" stroke="{color2}" stroke-width="2" opacity="0.8"/>')
# Inner sacred triangles (tetraktys pattern)
for i in range(3):
scale = 0.6 - i * 0.15
inner_size = tet_size * scale
x1_i = center_x
y1_i = center_y - inner_size * 0.4
x2_i = center_x - inner_size * 0.5
y2_i = center_y + inner_size * 0.2
x3_i = center_x + inner_size * 0.5
y3_i = center_y + inner_size * 0.2
opacity = 0.7 - i * 0.15
shapes.append(f'<polygon points="{x1_i:.1f},{y1_i:.1f} {x2_i:.1f},{y2_i:.1f} {x3_i:.1f},{y3_i:.1f}" fill="none" stroke="{color1}" stroke-width="1" opacity="{opacity}"/>')
# Central point of unity
shapes.append(f'<circle cx="{center_x}" cy="{center_y}" r="{size//25}" fill="{color1}" opacity="1.0"/>')
# Corner vertices (tetraktys dots)
vertex_radius = size // 30
shapes.append(f'<circle cx="{x1:.1f}" cy="{y1:.1f}" r="{vertex_radius}" fill="{color2}" opacity="0.8"/>')
shapes.append(f'<circle cx="{x2:.1f}" cy="{y2:.1f}" r="{vertex_radius}" fill="{color2}" opacity="0.8"/>')
shapes.append(f'<circle cx="{x3:.1f}" cy="{y3:.1f}" r="{vertex_radius}" fill="{color2}" opacity="0.8"/>')
# Compose SVG
defs_content = "\n ".join(defs) if defs else ""
shapes_content = "\n ".join(shapes)
svg = f'''<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}" xmlns="http://www.w3.org/2000/svg">
<defs>
{defs_content}
</defs>
{shapes_content}
</svg>'''
# Convert to data URL
svg_b64 = base64.b64encode(svg.encode()).decode()
return f"data:image/svg+xml;base64,{svg_b64}"
def generate_folder_icon(title, size=24):
"""Generate a folder icon with unique accent color based on title."""
hash_obj = hashlib.md5(title.encode())
hash_bytes = hash_obj.digest()
# Generate accent color
hue = (hash_bytes[0] * 360) // 256
saturation = 60 + (hash_bytes[1] * 20) // 256 # 60-80%
lightness = 45 + (hash_bytes[2] * 20) // 256 # 45-65%
accent_color = f"hsl({hue}, {saturation}%, {lightness}%)"
folder_base = "#e8e8e8"
svg = f'''<svg width="{size}" height="{size}" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="folder_grad_{abs(hash(title)) % 1000}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{folder_base}"/>
<stop offset="100%" stop-color="{accent_color}"/>
</linearGradient>
</defs>
<path d="M10 4H4c-1.11 0-2 .89-2 2v12c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2h-8l-2-2z"
fill="url(#folder_grad_{abs(hash(title)) % 1000})"
stroke="{accent_color}"
stroke-width="0.5"/>
<circle cx="18" cy="7" r="2" fill="{accent_color}" opacity="0.8"/>
</svg>'''
svg_b64 = base64.b64encode(svg.encode()).decode()
return f"data:image/svg+xml;base64,{svg_b64}"
def get_directory_structure(path):
"""Get the directory structure for a given path."""
items = []
@@ -380,6 +925,26 @@ def get_directory_structure(path):
except:
pass
# Generate unique SVG icon based on actual content title for consistency
icon_title = display_name # Default to filename-based display name
# For markdown files, try to extract the actual H1 title from content
if item.is_file() and item.suffix == '.md':
try:
content_data = render_markdown_file(item)
if content_data and 'title' in content_data:
icon_title = content_data['title']
# Also update display_name to use the actual title
display_name = content_data['title']
except:
# Fallback to filename-based display name if parsing fails
pass
if item.is_dir():
unique_icon = generate_folder_icon(icon_title, size=32)
else:
unique_icon = generate_unique_svg_icon(icon_title, size=32)
item_info = {
'name': item.name,
'display_name': display_name,
@@ -393,7 +958,8 @@ def get_directory_structure(path):
'modified': datetime.fromtimestamp(item.stat().st_mtime),
'file_date': file_date, # Date extracted from file content
'file_type': item.suffix.lower() if item.is_file() else 'directory',
'static_path': f"/static/data/{item.relative_to(DATA_DIR)}" if not item.is_dir() else None
'static_path': f"/static/data/{item.relative_to(DATA_DIR)}" if not item.is_dir() else None,
'unique_icon': unique_icon # Generated SVG icon
}
if item.is_dir():
@@ -590,6 +1156,9 @@ def render_markdown_file(file_path):
# Find series posts if this post is part of a series
series_posts = find_series_posts(metadata, file_path)
# Generate unique icon for this content
unique_icon = generate_unique_svg_icon(title, size=32)
return {
'content': html_content,
@@ -599,7 +1168,8 @@ def render_markdown_file(file_path):
'word_count': word_count,
'tags': tags,
'series_posts': series_posts,
'series_name': metadata.get('series')
'series_name': metadata.get('series'),
'unique_icon': unique_icon
}
except Exception as e:
return {
@@ -1524,7 +2094,9 @@ def themes_index():
metadata=content_data.get('metadata', {}),
breadcrumbs=[],
current_year=datetime.now().year,
current_page='Themes')
current_page='Themes',
unique_icon=content_data.get('unique_icon'),
parent_directory=None)
else:
# Fallback to directory listing if no index.md
return serve_path('themes')
@@ -1662,6 +2234,20 @@ def serve_path(path):
first_para = content_text.split('\n\n')[0]
description = first_para[:200] + '...' if len(first_para) > 200 else first_para
# Generate parent directory information
parent_directory = None
if full_path.parent != DATA_DIR: # Don't show parent for root-level content
parent_path = full_path.parent
parent_display_name = parent_path.name.replace('-', ' ').replace('_', ' ').title()
parent_url = '/' + str(parent_path.relative_to(DATA_DIR))
parent_icon = generate_folder_icon(parent_display_name, size=20)
parent_directory = {
'display_name': parent_display_name,
'url': parent_url,
'icon': parent_icon
}
return render_template('post.html',
content=content_data['content'],
title=content_data['title'],
@@ -1678,7 +2264,9 @@ def serve_path(path):
next_post=next_post,
tags=content_data.get('tags', []),
series_posts=content_data.get('series_posts', []),
series_name=content_data.get('series_name'))
series_name=content_data.get('series_name'),
unique_icon=content_data.get('unique_icon'),
parent_directory=parent_directory)
elif full_path.suffix.lower() in ['.jpg', '.jpeg', '.png', '.gif', '.webp']:
# Image file - check if it's in a gallery directory
@@ -1975,7 +2563,8 @@ class MetadataCache:
'url': metadata['url'],
'date': metadata.get('pub_date'),
'category': metadata['category'].replace('-', ' ').title(),
'sidenotes': [{'text': s['text'], 'id': s.get('id')} for s in sidenotes]
'sidenotes': [{'text': s['text'], 'id': s.get('id')} for s in sidenotes],
'unique_icon': metadata.get('unique_icon')
})
# Sort by date (most recent first)
@@ -2034,7 +2623,8 @@ class MetadataCache:
'url': metadata['url'],
'date': metadata.get('pub_date'),
'category': metadata['category'].replace('-', ' ').title(),
'headings': processed_headings
'headings': processed_headings,
'unique_icon': metadata.get('unique_icon')
})
# Sort by date (most recent first)
@@ -2072,7 +2662,8 @@ class MetadataCache:
'url': metadata['url'],
'date': metadata.get('pub_date'),
'category': metadata['category'].replace('-', ' ').title(),
'quotes': [q['text'] for q in quotes]
'quotes': [q['text'] for q in quotes],
'unique_icon': metadata.get('unique_icon')
})
articles.sort(key=lambda x: x['date'] if x['date'] else datetime(1900, 1, 1), reverse=True)
@@ -2175,7 +2766,8 @@ class MetadataCache:
'category': metadata['category'].replace('-', ' ').title(),
'connections': processed_outgoing, # For backward compatibility
'outgoing_connections': processed_outgoing,
'incoming_connections': processed_incoming
'incoming_connections': processed_incoming,
'unique_icon': metadata.get('unique_icon')
})
# Sort by date (most recent first)
+79
View File
@@ -332,6 +332,85 @@ header nav a:last-child {
margin-top: 2.5rem;
}
/* Grid Layout for Directory Contents */
.directory-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.5rem;
margin-top: 1rem;
font-size: 0.95rem;
width: 65%;
}
.directory-item {
position: relative;
transition: all 0.2s ease;
font-family: et-book, Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif;
text-align: center;
}
.directory-item:hover {
transform: translateY(-1px);
opacity: 0.8;
}
.directory-item-link {
display: flex;
flex-direction: column;
padding: 1rem;
color: #333;
text-decoration: none;
font-weight: 500;
background: none;
text-shadow: none;
transition: color 0.2s ease;
font-size: 0.95rem;
line-height: 1.3;
height: 100%;
box-sizing: border-box;
gap: 0;
}
.directory-item-link::before {
content: "";
display: block;
width: 32px;
height: 32px;
margin: 0 auto 0.75rem auto;
background-image: var(--unique-icon);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.directory-item-link:hover {
color: #111;
}
.directory-item .item-date {
font-size: 0.9rem;
color: #666;
font-style: italic;
margin-top: 0;
font-weight: 400;
}
/* Responsive grid adjustments */
@media (max-width: 1024px) {
.directory-grid {
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
}
@media (max-width: 760px) {
.directory-grid {
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
width: 95%;
}
}
/* ========================================= */
/* Horizontal Rules (Ornamental) */
/* ========================================= */
+245
View File
@@ -0,0 +1,245 @@
import hashlib
import math
import base64
import random
def generate_unique_svg_icon(title, size=24):
"""Generate a unique procedural SVG icon based on the title string."""
# Create hash from title for deterministic randomness
hash_obj = hashlib.md5(title.encode())
hash_bytes = hash_obj.digest()
# Use hash to seed random generator for deterministic but varied results
seed = int.from_bytes(hash_bytes[:4], 'big')
rng = random.Random(seed)
# Extract color palette from hash - prettier, more vibrant colors
hue_base = (hash_bytes[0] * 360) // 256
hue_range = 60 + (hash_bytes[1] % 60) # 60-120 degree hue range for harmony
saturation_base = 55 + (hash_bytes[2] % 25) # 55-80% saturation for vibrancy
lightness_base = 45 + (hash_bytes[3] % 20) # 45-65% lightness for good contrast
# Generate 2-3 harmonious colors
num_colors = 2 + (hash_bytes[4] % 2)
colors = []
for i in range(num_colors):
hue = (hue_base + (i * hue_range // num_colors)) % 360
sat = saturation_base + rng.randint(-5, 10)
light = lightness_base + rng.randint(-5, 15)
colors.append(f"hsl({hue}, {sat}%, {light}%)")
# Generate gradient IDs
gradient_id = f"grad_{abs(hash(title)) % 10000}"
gradient_id2 = f"grad2_{abs(hash(title)) % 10000}"
shapes = []
defs = []
# Create smoother gradient definitions
gradient_type = rng.choice(['linear', 'radial'])
if gradient_type == 'linear':
angle = rng.randint(0, 360)
x1 = 50 + 50 * math.cos(angle * math.pi / 180)
y1 = 50 + 50 * math.sin(angle * math.pi / 180)
x2 = 50 - 50 * math.cos(angle * math.pi / 180)
y2 = 50 - 50 * math.sin(angle * math.pi / 180)
gradient_def = f'<linearGradient id="{gradient_id}" x1="{x1:.0f}%" y1="{y1:.0f}%" x2="{x2:.0f}%" y2="{y2:.0f}%">'
else:
cx, cy = rng.randint(30, 70), rng.randint(30, 70)
gradient_def = f'<radialGradient id="{gradient_id}" cx="{cx}%" cy="{cy}%">'
# Smoother color transitions
for i, color in enumerate(colors):
offset = (i * 100) // (len(colors) - 1) if len(colors) > 1 else 50
gradient_def += f'<stop offset="{offset}%" stop-color="{color}"/>'
gradient_def += '</linearGradient>' if gradient_type == 'linear' else '</radialGradient>'
defs.append(gradient_def)
# Create a secondary gradient for variety
if rng.random() > 0.5:
gradient_def2 = f'<linearGradient id="{gradient_id2}" x1="0%" y1="0%" x2="100%" y2="100%">'
for i, color in enumerate(reversed(colors)):
offset = (i * 100) // (len(colors) - 1) if len(colors) > 1 else 50
gradient_def2 += f'<stop offset="{offset}%" stop-color="{color}" stop-opacity="0.8"/>'
gradient_def2 += '</linearGradient>'
defs.append(gradient_def2)
# Choose a composition style for better aesthetics
composition_style = rng.choice(['centered', 'spiral', 'grid', 'organic', 'geometric'])
if composition_style == 'centered':
# Centered composition with decreasing sizes
num_shapes = 3 + rng.randint(0, 2)
for i in range(num_shapes):
scale = 1 - (i * 0.25)
opacity = 0.9 - (i * 0.2)
shape_type = rng.choice(['circle', 'rounded_square', 'star'])
if shape_type == 'circle':
r = (size * 0.4 * scale)
shapes.append(f'<circle cx="{size//2}" cy="{size//2}" r="{r:.1f}" fill="url(#{gradient_id})" opacity="{opacity:.2f}"/>')
elif shape_type == 'rounded_square':
s = size * 0.7 * scale
x = (size - s) / 2
shapes.append(f'<rect x="{x:.1f}" y="{x:.1f}" width="{s:.1f}" height="{s:.1f}" rx="{s*0.2:.1f}" fill="url(#{gradient_id})" opacity="{opacity:.2f}" transform="rotate({i*15} {size//2} {size//2})"/>')
elif shape_type == 'star':
points = create_star_points(size//2, size//2, size*0.4*scale, size*0.2*scale, 5 + i)
shapes.append(f'<polygon points="{points}" fill="url(#{gradient_id})" opacity="{opacity:.2f}"/>')
elif composition_style == 'spiral':
# Spiral arrangement
num_elements = 5 + rng.randint(0, 3)
for i in range(num_elements):
angle = (i * 360 / num_elements) + (i * 30)
distance = (size * 0.2) + (i * size * 0.05)
x = size//2 + distance * math.cos(angle * math.pi / 180)
y = size//2 + distance * math.sin(angle * math.pi / 180)
r = size * 0.15 - (i * size * 0.01)
opacity = 0.8 - (i * 0.08)
fill = f"url(#{gradient_id})" if i % 2 == 0 else colors[i % len(colors)]
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{r:.1f}" fill="{fill}" opacity="{opacity:.2f}"/>')
elif composition_style == 'grid':
# Clean grid arrangement
grid_size = 3
cell_size = size / grid_size
for i in range(grid_size):
for j in range(grid_size):
if rng.random() > 0.3: # 70% chance to place element
x = (i + 0.5) * cell_size
y = (j + 0.5) * cell_size
shape_type = rng.choice(['circle', 'square'])
s = cell_size * 0.6
opacity = 0.5 + rng.uniform(0, 0.4)
fill = f"url(#{gradient_id})" if (i + j) % 2 == 0 else colors[(i+j) % len(colors)]
if shape_type == 'circle':
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{s/2:.1f}" fill="{fill}" opacity="{opacity:.2f}"/>')
else:
shapes.append(f'<rect x="{x-s/2:.1f}" y="{y-s/2:.1f}" width="{s:.1f}" height="{s:.1f}" rx="{s*0.2:.1f}" fill="{fill}" opacity="{opacity:.2f}"/>')
elif composition_style == 'organic':
# Organic blob-like shapes
num_blobs = 3 + rng.randint(0, 2)
for i in range(num_blobs):
# Create smooth blob using bezier curves
cx = size//2 + rng.uniform(-size*0.2, size*0.2)
cy = size//2 + rng.uniform(-size*0.2, size*0.2)
path_data = create_blob_path(cx, cy, size*0.3, rng)
opacity = 0.7 - (i * 0.15)
fill = f"url(#{gradient_id})" if i == 0 else colors[i % len(colors)]
shapes.append(f'<path d="{path_data}" fill="{fill}" opacity="{opacity:.2f}"/>')
else: # geometric
# Geometric pattern with triangles or hexagons
if rng.random() > 0.5:
# Triangular composition
for i in range(3):
rotation = i * 120
cx, cy = size//2, size//2
# Create equilateral triangle
height = size * 0.6
width = height * 0.866
x1 = cx
y1 = cy - height/2
x2 = cx - width/2
y2 = cy + height/4
x3 = cx + width/2
y3 = cy + height/4
opacity = 0.7 - (i * 0.2)
fill = f"url(#{gradient_id})" if i == 0 else colors[i % len(colors)]
shapes.append(f'<polygon points="{x1:.1f},{y1:.1f} {x2:.1f},{y2:.1f} {x3:.1f},{y3:.1f}" fill="{fill}" opacity="{opacity:.2f}" transform="rotate({rotation} {cx} {cy})"/>')
else:
# Diamond/rhombus pattern
for i in range(4):
angle = i * 90 + 45
distance = size * 0.25
x = size//2 + distance * math.cos(angle * math.pi / 180)
y = size//2 + distance * math.sin(angle * math.pi / 180)
# Create diamond
d_size = size * 0.3
diamond = f"{x:.1f},{y-d_size/2:.1f} {x+d_size/2:.1f},{y:.1f} {x:.1f},{y+d_size/2:.1f} {x-d_size/2:.1f},{y:.1f}"
opacity = 0.6 + (i % 2) * 0.2
fill = f"url(#{gradient_id})" if i % 2 == 0 else colors[i % len(colors)]
shapes.append(f'<polygon points="{diamond}" fill="{fill}" opacity="{opacity:.2f}"/>')
# Add subtle accent dots for visual interest
if rng.random() > 0.6:
num_accents = rng.randint(3, 5)
for _ in range(num_accents):
x = rng.uniform(size * 0.15, size * 0.85)
y = rng.uniform(size * 0.15, size * 0.85)
r = rng.uniform(0.8, 1.5)
opacity = rng.uniform(0.3, 0.6)
shapes.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{r:.1f}" fill="{colors[-1]}" opacity="{opacity:.2f}"/>')
# Compose SVG
defs_content = "\n ".join(defs) if defs else ""
shapes_content = "\n ".join(shapes)
svg = f'''<svg width="{size}" height="{size}" viewBox="0 0 {size} {size}" xmlns="http://www.w3.org/2000/svg">
<defs>
{defs_content}
</defs>
{shapes_content}
</svg>'''
# Convert to data URL
svg_b64 = base64.b64encode(svg.encode()).decode()
return f"data:image/svg+xml;base64,{svg_b64}"
def create_star_points(cx, cy, outer_r, inner_r, num_points):
"""Create points for a star shape."""
points = []
for i in range(num_points * 2):
angle = (i * math.pi) / num_points - math.pi / 2
r = outer_r if i % 2 == 0 else inner_r
x = cx + r * math.cos(angle)
y = cy + r * math.sin(angle)
points.append(f"{x:.1f},{y:.1f}")
return " ".join(points)
def create_blob_path(cx, cy, radius, rng):
"""Create a smooth blob shape using bezier curves."""
num_points = 6
points = []
# Generate control points around a circle with some randomness
for i in range(num_points):
angle = (i * 2 * math.pi) / num_points
r = radius * rng.uniform(0.7, 1.3)
x = cx + r * math.cos(angle)
y = cy + r * math.sin(angle)
points.append((x, y))
# Create smooth bezier path
path_data = f"M {points[0][0]:.1f},{points[0][1]:.1f}"
for i in range(num_points):
next_i = (i + 1) % num_points
# Calculate control points for smooth curves
cp1x = points[i][0] + (points[next_i][0] - points[i][0]) * 0.5
cp1y = points[i][1] + (points[next_i][1] - points[i][1]) * 0.2
path_data += f" Q {cp1x:.1f},{cp1y:.1f} {points[next_i][0]:.1f},{points[next_i][1]:.1f}"
path_data += " Z"
return path_data
+48 -9
View File
@@ -43,15 +43,22 @@
<h2>{{ group_name }}</h2>
{% for post in posts %}
<p><strong><a href="{{ post.url }}">{{ post.title }}</a></strong>
{% if post.pub_date %}
<span class="post-date">{{ post.pub_date.strftime('%B %d, %Y') }}</span>
{% endif %}
<br>
{% if post.description %}
{{ post.description | unescape }}
{% endif %}
</p>
<div class="archive-post">
{% if post.unique_icon %}
<img src="{{ post.unique_icon }}" alt="Icon for {{ post.title }}" class="archive-post-icon">
{% endif %}
<div class="archive-post-content">
<p><strong><a href="{{ post.url }}">{{ post.title }}</a></strong>
{% if post.pub_date %}
<span class="post-date">{{ post.pub_date.strftime('%B %d, %Y') }}</span>
{% endif %}
<br>
{% if post.description %}
{{ post.description }}
{% endif %}
</p>
</div>
</div>
{% endfor %}
</section>
{% endfor %}
@@ -134,6 +141,32 @@ section h2 {
margin-bottom: 1.5rem;
}
/* Archive post layout with icons */
.archive-post {
display: flex;
align-items: flex-start;
gap: 1rem;
margin-bottom: 1.5rem;
padding-left: 1rem;
}
.archive-post-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
margin-top: 0.3rem;
margin-left: -3rem;
}
.archive-post-content {
flex: 1;
}
.archive-post-content p {
margin: 0;
}
/* Legacy styles for backward compatibility */
section p {
margin-bottom: 1.5rem;
padding-left: 1rem;
@@ -164,6 +197,12 @@ section p:last-child {
.year-picker p {
font-size: 0.9rem;
}
.archive-post-icon {
margin-left: -2rem;
width: 20px;
height: 20px;
}
}
</style>
{% endblock %}
+22 -4
View File
@@ -15,9 +15,14 @@
{% if articles %}
{% for article in articles %}
<div class="article-group">
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
<div class="article-title-container">
{% if article.unique_icon %}
<img src="{{ article.unique_icon }}" alt="Icon for {{ article.title }}" class="article-icon">
{% endif %}
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
</div>
<div class="article-meta">
{{ article.category }}
{% if article.date %}
@@ -88,9 +93,22 @@
border-bottom: none;
}
.article-title-container {
position: relative;
margin-bottom: 0.5rem;
}
.article-icon {
width: 32px;
height: 32px;
position: absolute;
left: -4rem;
top: 0.2rem;
}
.article-title {
font-size: 1.4rem;
margin-bottom: 0.5rem;
margin: 0;
}
.article-title a {
+86 -14
View File
@@ -2,6 +2,68 @@
{% block extra_head %}
<style>
/* Directory listing styles */
.directory-list {
list-style: none;
padding: 0;
margin: 0;
}
.directory-item {
position: relative;
padding: 0.5rem 0;
border-bottom: 1px solid #f5f5f5;
}
.directory-item:last-child {
border-bottom: none;
}
.directory-icon {
width: 20px;
height: 20px;
position: absolute;
left: -3rem;
top: 0.5rem;
}
.directory-content {
display: flex;
align-items: center;
gap: 0.5rem;
}
.directory-link {
color: #333;
text-decoration: none;
font-size: 0.95rem;
}
.directory-link:hover {
color: #666;
text-decoration: underline;
}
.directory-date {
color: #888;
font-size: 0.8rem;
margin-left: auto;
}
@media (max-width: 760px) {
.directory-icon {
width: 18px;
height: 18px;
}
.directory-link {
font-size: 0.9rem;
}
.directory-date {
font-size: 0.75rem;
}
}
/* Image Gallery Styles */
.image-gallery {
margin: 2rem 0;
@@ -203,16 +265,21 @@
{% if items|length > image_items|length %}
<section class="directory-listing">
<h3>Other Files</h3>
<ul>
<ul class="directory-list">
{% for item in items %}
{% if not item.is_image %}
<li data-icon="{% if item.is_dir %}📁{% elif item.is_markdown %}📝{% else %}📄{% endif %}">
<a href="{{ item.url_path }}">
{{ item.display_name }}{% if item.is_dir %}/{% endif %}
</a>
{% if not item.is_dir and item.file_date %}
<span class="item-date">{{ item.file_date }}</span>
<li class="directory-item">
{% if item.unique_icon %}
<img src="{{ item.unique_icon }}" alt="Icon for {{ item.display_name }}" class="directory-icon">
{% endif %}
<div class="directory-content">
<a href="{{ item.url_path }}" class="directory-link">
{{ item.display_name }}{% if item.is_dir %}/{% endif %}
</a>
{% if not item.is_dir and item.file_date %}
<span class="directory-date">{{ item.file_date }}</span>
{% endif %}
</div>
</li>
{% endif %}
{% endfor %}
@@ -223,15 +290,20 @@
{% elif items %}
<section class="directory-listing">
<h3>Contents</h3>
<ul>
<ul class="directory-list">
{% for item in items %}
<li data-icon="{% if item.is_dir %}📁{% elif item.is_markdown %}📝{% elif item.is_image %}🖼️{% else %}📄{% endif %}">
<a href="{{ item.url_path }}">
{{ item.display_name }}{% if item.is_dir %}/{% endif %}
</a>
{% if not item.is_dir and item.file_date %}
<span class="item-date">{{ item.file_date }}</span>
<li class="directory-item">
{% if item.unique_icon %}
<img src="{{ item.unique_icon }}" alt="Icon for {{ item.display_name }}" class="directory-icon">
{% endif %}
<div class="directory-content">
<a href="{{ item.url_path }}" class="directory-link">
{{ item.display_name }}{% if item.is_dir %}/{% endif %}
</a>
{% if not item.is_dir and item.file_date %}
<span class="directory-date">{{ item.file_date }}</span>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
+22 -4
View File
@@ -15,9 +15,14 @@
{% if articles %}
{% for article in articles %}
<div class="article-group">
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
<div class="article-title-container">
{% if article.unique_icon %}
<img src="{{ article.unique_icon }}" alt="Icon for {{ article.title }}" class="article-icon">
{% endif %}
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
</div>
<div class="article-meta">
{{ article.category }}
{% if article.date %}
@@ -61,9 +66,22 @@
border-bottom: none;
}
.article-title-container {
position: relative;
margin-bottom: 0.5rem;
}
.article-icon {
width: 32px;
height: 32px;
position: absolute;
left: -4rem;
top: 0.2rem;
}
.article-title {
font-size: 1.4rem;
margin-bottom: 0.5rem;
margin: 0;
}
.article-title a {
+92 -6
View File
@@ -39,15 +39,35 @@
width: 100%;
}
.post-title-container {
display: flex;
align-items: flex-start;
gap: 1rem;
margin-bottom: 0.5rem;
width: 70%;
}
.post-title-icon {
width: 48px;
height: 48px;
flex-shrink: 0;
margin-left: -4rem;
}
.post-title-text {
flex: 1;
display: flex;
flex-direction: column;
}
article h1 {
font-size: 3.2rem;
font-style: italic;
font-weight: 400;
line-height: 1;
margin-bottom: 0.5rem;
margin: 0;
color: #111;
text-rendering: optimizeLegibility;
width: 70%;
}
.post-subtitle {
@@ -80,6 +100,39 @@
margin: 4rem 0;
}
/* Parent navigation */
.parent-navigation {
margin-top: 4rem;
padding-top: 2rem;
border-top: 1px solid #eee;
width: 65%;
}
.parent-link {
display: flex;
align-items: center;
gap: 0.75rem;
text-decoration: none;
color: #666;
font-size: 0.95rem;
font-style: italic;
transition: color 0.2s ease;
}
.parent-link:hover {
color: #333;
}
.parent-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
.parent-text {
line-height: 1.2;
}
@media (max-width: 760px) {
article h1 {
font-size: 2.5rem;
@@ -101,6 +154,21 @@
width: 100% !important;
max-width: 100% !important;
}
.parent-navigation {
width: 100%;
margin-top: 3rem;
padding-top: 1.5rem;
}
.parent-link {
font-size: 0.9rem;
}
.parent-icon {
width: 18px;
height: 18px;
}
}
</style>
@@ -142,14 +210,32 @@ document.addEventListener('DOMContentLoaded', function() {
{% block content %}
<article>
<h1>{{ title }}</h1>
{% if metadata and metadata.date %}
<div class="post-subtitle">{{ metadata.date if metadata.date is string else metadata.date[0] }}</div>
{% endif %}
<div class="post-title-container">
{% if unique_icon %}
<img src="{{ unique_icon }}" alt="Icon for {{ title }}" class="post-title-icon">
{% endif %}
<div class="post-title-text">
<h1>{{ title }}</h1>
{% if metadata and metadata.date %}
<div class="post-subtitle">{{ metadata.date if metadata.date is string else metadata.date[0] }}</div>
{% endif %}
</div>
</div>
<section>
{{ content | safe }}
</section>
{% if parent_directory %}
<div class="parent-navigation">
<a href="{{ parent_directory.url }}" class="parent-link">
{% if parent_directory.icon %}
<img src="{{ parent_directory.icon }}" alt="Parent directory icon" class="parent-icon">
{% endif %}
<span class="parent-text">← {{ parent_directory.display_name }}</span>
</a>
</div>
{% endif %}
</article>
{% endblock %}
+22 -4
View File
@@ -15,9 +15,14 @@
{% if articles %}
{% for article in articles %}
<div class="article-group">
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
<div class="article-title-container">
{% if article.unique_icon %}
<img src="{{ article.unique_icon }}" alt="Icon for {{ article.title }}" class="article-icon">
{% endif %}
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
</div>
<div class="article-meta">
{{ article.category }}
{% if article.date %}
@@ -59,9 +64,22 @@
border-bottom: none;
}
.article-title-container {
position: relative;
margin-bottom: 0.5rem;
}
.article-icon {
width: 32px;
height: 32px;
position: absolute;
left: -4rem;
top: 0.2rem;
}
.article-title {
font-size: 1.4rem;
margin-bottom: 0.5rem;
margin: 0;
}
.article-title a {
+22 -4
View File
@@ -15,9 +15,14 @@
{% if articles %}
{% for article in articles %}
<div class="article-group">
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
<div class="article-title-container">
{% if article.unique_icon %}
<img src="{{ article.unique_icon }}" alt="Icon for {{ article.title }}" class="article-icon">
{% endif %}
<h3 class="article-title">
<a href="{{ article.url }}">{{ article.title }}</a>
</h3>
</div>
<div class="article-meta">
{{ article.category }}
{% if article.date %}
@@ -65,9 +70,22 @@
border-bottom: none;
}
.article-title-container {
position: relative;
margin-bottom: 0.5rem;
}
.article-icon {
width: 32px;
height: 32px;
position: absolute;
left: -4rem;
top: 0.2rem;
}
.article-title {
font-size: 1.4rem;
margin-bottom: 0.5rem;
margin: 0;
}
.article-title a {