Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions .github/workflows/bundle.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
name: Test Bundles
on:
pull_request:
jobs:
bundle:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- name: build ci environment
run: |
docker build --pull --rm -t qrapps:ci .
- name: generate QR
run: |
docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles
name: Test Bundles

on:
pull_request:

jobs:
bundle:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: build ci environment
run: |
docker compose build
- name: generate QR
run: |
docker compose run --rm qrs
16 changes: 0 additions & 16 deletions .github/workflows/prospector.yml

This file was deleted.

63 changes: 31 additions & 32 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
name: Release Bundles

on:
push:
branches: [ main ]

jobs:
bundle:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- name: build ci environment
run: |
docker build --pull --rm -t qrapps:ci .
- name: generate QR
run: |
docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles
- name: generate README.md
run: |
rm -rf docs
mv bundles docs
sudo cp -at docs .google/*
sudo git checkout -- docs/_config.yml
docker run --rm -v .:/src -w /src qrapps:ci release.py
- name: release
run: |
git config --global user.email "woldemar@mimas.ru"
git config --global user.name "Vladimir Looze"
git add docs
git commit -m "release new bundles"
git push --force origin main

name: Release Bundles

on:
push:
branches: [ main ]

jobs:
bundle:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: build ci environment
run: |
docker compose build
- name: generate QR
run: |
docker compose run --rm qrs
- name: generate README.md
run: |
rm -rf docs
mv bundles docs
sudo cp -at docs .google/*
sudo git checkout -- docs/_config.yml
docker compose run --rm release
- name: release
run: |
git config --global user.email "woldemar@mimas.ru"
git config --global user.name "Vladimir Looze"
git add docs
git commit -m "release new bundles"
git push --force origin main
16 changes: 16 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: ruff

on:
pull_request:

jobs:
ruff:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: build ci environment
run: |
docker compose build
- name: lint with ruff
run: |
docker compose run --rm lint
39 changes: 11 additions & 28 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,40 +1,23 @@
# Self-contained environment, useful commands:
#
# Build environment image:
# docker build --pull --rm -t qrapps:ci .
#
# After image has been built, you can run:
#
# Interactive shell, useful for debugging:
# docker run --rm -it -v .:/src -w /src --entrypoint=bash qrapps:ci
#
# Code quality check:
# docker run --rm -v .:/src -w /src --entrypoint=prospector qrapps:ci
#
# Convert everything to QR codes:
# docker run --rm -v .:/src -w /src qrapps:ci qrs.py --htmldirs=apps --builddir=bundles
#
# Convert single app to QR code:
# docker run --rm -v .:/src -w /src qrapps:ci qr.py --htmldir=apps/demo-clock --builddir=bundles
# Self-contained environment for QR Apps project

from debian:12
FROM debian:12

RUN apt-get -y update && apt-get install -y --no-install-recommends \
python3-pip=23.0.1+dfsg-1 \
python3-venv=3.11.2-1+b1 \
npm=9.2.0~ds1-1 \
&& \
rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/*

RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \
--mount=type=bind,source=requirements-contrib.txt,target=requirements-contrib.txt \
python3 -m venv /venv && \
COPY requirements.txt /tmp/requirements.txt
COPY requirements-contrib.txt /tmp/requirements-contrib.txt

RUN python3 -m venv /venv && \
/venv/bin/python3 -m pip install --upgrade pip && \
/venv/bin/python3 -m pip install -r requirements.txt && \
/venv/bin/python3 -m pip install -r requirements-contrib.txt
/venv/bin/python3 -m pip install -r /tmp/requirements.txt && \
/venv/bin/python3 -m pip install -r /tmp/requirements-contrib.txt

RUN npm install -g uglify-js html-minifier inline-scripts

ENV PATH="/venv/bin:${PATH}"

ENV PATH=/venv/bin:$PATH
WORKDIR /src
ENTRYPOINT ["/venv/bin/python3"]
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![CodeQL](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml)
[![CodeQL](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/codeql.yml)
[![GutHub Pages](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/pages/pages-build-deployment)
[![prospector](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/prospector.yml)
[![ruff](https://github.com/worldemar/qrapps/actions/workflows/ruff.yml/badge.svg)](https://github.com/worldemar/qrapps/actions/workflows/ruff.yml)

# QR Code Apps

Expand All @@ -24,6 +24,7 @@ Since QR code content is [Data URL](https://en.wikipedia.org/wiki/Data_URI_schem

(see `demo-*` apps for file content examples)
- Use build instructions from [Dockerfile](Dockerfile)
- See [docker.md](docker.md) for Docker Compose commands

## Why would you do that?
For fun, challenge and as a demonstration of modern browser capabilities.
Expand Down
3 changes: 3 additions & 0 deletions apps/demo-segmented-clock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Segmented Clock

A digital clock display using 7-segment display style. Shows hours, minutes, and seconds with blinking colons between them.
75 changes: 75 additions & 0 deletions apps/demo-segmented-clock/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<html>
<body style="background:#000;margin:0;display:flex;justify-content:center;align-items:center;height:100vh;overflow:hidden">
<canvas id="clock" width="640" height="160"></canvas>
<script>
const canvas = document.getElementById('clock');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0f0';

const segments = {
0: [1,1,1,1,1,1,0],
1: [0,1,1,0,0,0,0],
2: [1,1,0,1,1,0,1],
3: [1,1,1,1,0,0,1],
4: [0,1,1,0,0,1,1],
5: [1,0,1,1,0,1,1],
6: [1,0,1,1,1,1,1],
7: [1,1,1,0,0,0,0],
8: [1,1,1,1,1,1,1],
9: [1,1,1,1,0,1,1]
};

function drawSegment(x, y, width, height, active) {
ctx.globalAlpha = active ? 1 : 0.1;
ctx.fillRect(x, y, width, height);
}

function drawDigit(x, y, number) {
const segs = segments[number] || [0,0,0,0,0,0,0];
const w = 8, h = 48, s = 4;

// a (top)
drawSegment(x + s, y, h, w, segs[0]);
// b (top right)
drawSegment(x + h + s, y + s, w, h, segs[1]);
// c (bottom right)
drawSegment(x + h + s, y + h + s, w, h, segs[2]);
// d (bottom)
drawSegment(x + s, y + h*2 + s, h, w, segs[3]);
// e (bottom left)
drawSegment(x, y + h + s, w, h, segs[4]);
// f (top left)
drawSegment(x, y + s, w, h, segs[5]);
// g (middle)
drawSegment(x + s, y + h + s, h, w, segs[6]);
}

function drawColon(x, y) {
const dotSize = 8;
ctx.globalAlpha = 1;
ctx.fillRect(x, y, dotSize, dotSize);
ctx.fillRect(x, y + 32, dotSize, dotSize);
}

function updateClock() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');

drawDigit(40, 40, hours[0]);
drawDigit(120, 40, hours[1]);
drawColon(200, 56);
drawDigit(240, 40, minutes[0]);
drawDigit(320, 40, minutes[1]);
drawColon(400, 56);
drawDigit(440, 40, seconds[0]);
drawDigit(520, 40, seconds[1]);
}

updateClock();
setInterval(updateClock, 1000);
</script>
</body>
</html>
72 changes: 72 additions & 0 deletions apps/demo-segmented-clock/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const segments = {
0: ['a','b','c','d','e','f'],
1: ['b','c'],
2: ['a','b','g','e','d'],
3: ['a','b','g','c','d'],
4: ['f','g','b','c'],
5: ['a','f','g','c','d'],
6: ['a','f','g','c','d','e'],
7: ['a','b','c'],
8: ['a','b','c','d','e','f','g'],
9: ['a','b','c','d','f','g']
};

function createDigit() {
const digit = document.createElement('div');
digit.className = 'digit';
['a','b','c','d','e','f','g'].forEach(seg => {
const segment = document.createElement('div');
segment.className = `segment ${seg}`;
digit.appendChild(segment);
});
return digit;
}

function createColon() {
const colon = document.createElement('div');
colon.className = 'colon';
const dot1 = document.createElement('div');
dot1.className = 'dot top';
const dot2 = document.createElement('div');
dot2.className = 'dot bottom';
colon.appendChild(dot1);
colon.appendChild(dot2);
return colon;
}

function updateDigit(digit, number) {
const activeSegments = segments[number] || [];
digit.querySelectorAll('.segment').forEach(seg => {
seg.style.opacity = activeSegments.includes(seg.className.split(' ')[1]) ? '1' : '0.1';
});
}

function updateClock() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');

const digits = document.querySelectorAll('.digit');
updateDigit(digits[1], hours[0]);
updateDigit(digits[2], hours[1]);
updateDigit(digits[4], minutes[0]);
updateDigit(digits[5], minutes[1]);
updateDigit(digits[7], seconds[0]);
updateDigit(digits[8], seconds[1]);
}

function init() {
const clock = document.getElementById('clock');
// Create 6 digits (2 for hours, 2 for minutes, 2 for seconds)
for (let i = 0; i < 6; i++) {
clock.appendChild(createDigit());
if (i === 1 || i === 3) {
clock.appendChild(createColon());
}
}
updateClock();
setInterval(updateClock, 1000);
}

init();
6 changes: 6 additions & 0 deletions apps/demo-segmented-clock/minify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"collapseWhitespace": true,
"minifyJS": true,
"minifyCSS": true,
"removeOptionalTags": true
}
15 changes: 15 additions & 0 deletions apps/demo-segmented-clock/uglify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"mangle": true,
"toplevel": true,
"compress": {
"sequences": true,
"dead_code": true,
"conditionals": true,
"loops": true,
"booleans": true,
"unused": true,
"if_return": true,
"join_vars": true,
"drop_console": true
}
}
Loading
Loading