Merge pull request 'Make it not broken without javascript lmao' (#1) from woju/home:woju/nojs into main

Reviewed-on: #1
This commit is contained in:
radex 2024-12-18 19:35:02 +00:00
commit 1c92355c0d
14 changed files with 162 additions and 97 deletions

View file

@ -1,2 +1,39 @@
FROM nginxinc/nginx-unprivileged:1.25.2
COPY src /usr/share/nginx/html
FROM nginxinc/nginx-unprivileged:1.25.2-bookworm
ARG UID=101
ENV DEBIAN_FRONTEND=noninteractive
USER 0
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
build-essential \
python3-dev \
python3-venv \
python3-wheel \
rsync \
&& rm -rf /var/lib/apt/lists/*
# Installing jsonnet using pip takes about 5 min, because for some reason it
# needs to be compiled. To help docker cache it properly, we preinstall it into
# venv, because otherwise it would be installed as part of `lektor build`.
RUN python3 -m venv /tmp/venv \
&& /tmp/venv/bin/pip3 install wheel \
&& /tmp/venv/bin/pip3 install lektor jsonnet \
;
RUN mkdir -p /tmp/src
ADD assets/ /tmp/src/assets/
ADD content/ /tmp/src/content/
#ADD models/ /tmp/src/models/
ADD packages/ /tmp/src/packages/
ADD templates/ /tmp/src/templates/
ADD *.lektorproject /tmp/src/
WORKDIR /tmp/src
RUN /tmp/venv/bin/lektor build \
&& /tmp/venv/bin/lektor deploy docker \
;
USER $UID

View file

@ -4,10 +4,15 @@ List of all web services / app launcher
## Quick Start
There's no bundler, it's all vanilla html/css/js. You may run into CORS trouble just opening it
in the browser though. Use `npx serve src` or `python3 -m http.server -d src` to quickly spawn
a local web server.
It's a static site generator called [Lektor](https://getlektor.com). Use:
```sh
sudo apt-get install build-essential python3-dev python3-venv
python3 -m venv venv
./venv/bin/pip3 install lektor
./venv/bin/lektor serve
```
to quickly spawn a local web server.
### How to add services?
Change `src/services.js`.
Change `contents/services.jsonnet`.

View file

@ -1,69 +1,10 @@
// look ma, no bundler!
import services from './services.js'
const searchEl = document.querySelector('#search')
const searchboxEl = document.querySelector('#searchbox')
const contentEl = document.querySelector('#content')
for (const category of services.categories) {
const { title, description, items } = category
searchboxEl.classList.remove('hidden')
searchEl.focus()
const categoryEl = document.createElement('div')
categoryEl.setAttribute('class', 'category')
const titleEl = document.createElement('h2')
titleEl.textContent = title
categoryEl.appendChild(titleEl)
if (description) {
const descriptionEl = document.createElement('p')
descriptionEl.setAttribute('class', 'categoryDescription')
descriptionEl.textContent = description
categoryEl.appendChild(descriptionEl)
}
const itemsEl = document.createElement('div')
itemsEl.setAttribute('class', 'items')
for (const item of items) {
const { url, name, alias, description } = item
const fullUrl = url.startsWith('http') ? url : `https://${url}`
const itemEl = document.createElement('a')
itemEl.setAttribute('href', fullUrl)
itemEl.setAttribute('class', 'item')
itemEl.dataset.name = name.toLowerCase()
itemEl.dataset.alias = alias?.toLowerCase() || ''
const linkEl = document.createElement('p')
linkEl.setAttribute('class', 'itemName')
linkEl.textContent = name
if (alias) {
const aliasEl = document.createElement('span')
aliasEl.setAttribute('class', 'itemAlias')
aliasEl.textContent = ` (${alias})`
linkEl.appendChild(aliasEl)
}
itemEl.appendChild(linkEl)
if (description) {
const descriptionEl = document.createElement('p')
descriptionEl.setAttribute('class', 'itemDescription')
descriptionEl.textContent = description
itemEl.appendChild(descriptionEl)
}
itemsEl.appendChild(itemEl)
}
categoryEl.appendChild(itemsEl)
contentEl.appendChild(categoryEl)
}
const loadingEl = document.querySelector('#loading')
loadingEl.remove()
const searchEl = document.querySelector('#search')
searchEl.addEventListener('input', (event) => {
const query = event.currentTarget.value.toLowerCase().trim()

4
content/contents.lr Normal file
View file

@ -0,0 +1,4 @@
_model: none
---
_template: index.html
---

View file

@ -1,4 +1,4 @@
export default {
{
categories: [
{
title: 'General',

View file

@ -0,0 +1,2 @@
_hidden: yes
---

6
home.lektorproject Normal file
View file

@ -0,0 +1,6 @@
[project]
name = home
[servers.docker]
target = rsync:///usr/share/nginx/html
enabled = yes

5
packages/load-jsonnet/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
dist
build
*.pyc
*.pyo
*.egg-info

View file

@ -0,0 +1,13 @@
import json
import _jsonnet
import lektor.pluginsystem
def load_jsonnet(filename):
return json.loads(_jsonnet.evaluate_file(filename))
class LoadJsonnetPlugin(lektor.pluginsystem.Plugin):
name = 'Load Jsonnet'
def on_setup_env(self, **extra):
self.env.jinja_env.globals.update(load_jsonnet=load_jsonnet)

View file

@ -0,0 +1,2 @@
[bdist_wheel]
universal=1

View file

@ -0,0 +1,24 @@
import ast
import io
import re
from setuptools import setup
setup(
author='Wojtek Porczyk',
author_email='woju@hackerspace.pl',
keywords='Lektor plugin',
name='lektor-load-jsonnet',
license='AGPL-3',
py_modules=['lektor_load_jsonnet'],
install_requires = [
'jsonnet',
],
classifiers=[
'Framework :: Lektor',
'Environment :: Plugins',
],
entry_points={
'lektor.plugins': 'load-jsonnet = lektor_load_jsonnet:LoadJsonnetPlugin'
}
)

View file

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="pl">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Warsaw Hackerspace Services</title>
<link rel="stylesheet" href="style.css">
<script type="module" src="main.js"></script>
<main>
<h1>Warsaw Hackerspace services</h1>
<noscript>This website requires JavaScript lmao</noscript>
<div id="searchbox">
<input id="search" type="search" placeholder="Search for services…" autofocus>
<p class="searchHelp">↑↓ to navigate, ⮐ to open</p>
</div>
<div id="loading">Loading...</div>
<div id="content"></div>
<div id="noresults"></div>
</main>
<footer>
<a href="https://code.hackerspace.pl/hswaw/home">source</a>
</footer>

53
templates/index.html Normal file
View file

@ -0,0 +1,53 @@
{% set data = load_jsonnet(this.attachments.get('services.jsonnet').contents.filename) -%}
<!DOCTYPE html>
<html lang="pl">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Warsaw Hackerspace Services</title>
<link rel="stylesheet" href="style.css">
<script type="module" src="main.js"></script>
<main>
<h1>Warsaw Hackerspace services</h1>
<div id="searchbox" class="hidden">
<input id="search" type="search" placeholder="Search for services…" autofocus>
<p class="searchHelp">↑↓ to navigate, ⮐ to open</p>
</div>
<div id="content">
{%- for category in data.categories %}
<div class="category">
<h2>{{ category.title }}</h2>
{%- if category.description %}
<p class="categoryDescription">{{ category.description }}</p>
{%- endif %}
<div class="items">
{%- for item in category['items'] %}
{%- set fullurl = 'https://' + item.url if not item.url.startswith('http://') else item.url %}
<a class="item" href="{{ fullurl }}" data-name="{{ item.name.lower() }}" data-alias="{{ item.alias.lower() if alias is defined else '' }}">
<p class="itemName">{{ item.name }}
{%- if item.alias -%}
<span class="itemAlias"> ({{ item.alias }})</span>
{%- endif -%}
</p>
{%- if item.description %}
<p class="itemDescription">{{ item.description }}</p>
{%- endif %}
</a>
{%- endfor %}
</div>
</div>
{%- endfor %}
</div>
<div id="noresults"></div>
</main>
<footer>
<a href="https://code.hackerspace.pl/hswaw/home">source</a>
</footer>
{#- vim: set ft=jinja ts=2 sts=2 sw=2 et : #}