from flask import Flask, render_template, request, redirect, url_for from urllib.parse import urlencode import datetime app = Flask(__name__) def coerce_bool(val, default=False): if val is None: return default s = str(val).lower() return s in ("1","true","yes","y","on") @app.template_filter("default_if_none") def default_if_none(value, default): return default if value is None or value == "" else value @app.route("/") def index(): # Prefill from query if present, so users can tweak and regenerate q = request.args context = { "title": q.get("title", "Big Day"), "target": q.get("target", ""), "scheme": q.get("scheme", "dark"), "accent": q.get("accent", "#8b5cf6"), # violet-500 "radius": q.get("radius", "16"), "shadow": q.get("shadow", "lg"), "font": q.get("font", "system-ui"), "show_millis": coerce_bool(q.get("millis"), False), "rounded_unit": q.get("rounded_unit", "none"), # none, minutes, hours, days "bg": q.get("bg", "#0b0b10"), "fg": q.get("fg", "#e5e7eb"), } return render_template("index.html", **context) @app.route("/preview", methods=["POST"]) def preview(): data = request.form.to_dict(flat=True) # sanitize minimal # Build query string and redirect to /countdown return redirect(url_for("countdown") + "?" + urlencode(data)) @app.route("/countdown") def countdown(): q = request.args # Provide sane defaults if not supplied params = { "title": q.get("title", "Big Day"), "target": q.get("target", ""), "scheme": q.get("scheme", "dark"), "accent": q.get("accent", "#8b5cf6"), "radius": q.get("radius", "16"), "shadow": q.get("shadow", "lg"), "font": q.get("font", "system-ui"), "millis": q.get("millis", "0"), "rounded_unit": q.get("rounded_unit", "none"), "bg": q.get("bg", "#0b0b10"), "fg": q.get("fg", "#e5e7eb"), } # If no target specified, bounce user to setup screen if not params["target"]: return redirect(url_for("index")) return render_template("countdown.html", **params) if __name__ == "__main__": # Helpful for local dev app.run(debug=True, host="0.0.0.0", port=5000)