feat: redesign UI with themes, animations, procedural ASCII engine
Build and Deploy / Build Image (push) Successful in 31s
Build and Deploy / Deploy to Portainer (push) Successful in 6s

- Split app.py into routes + ascii_engine.py (stdlib-only procedural
  generation: figlet banners, creatures, patterns, scenes, classics)
- API: /api/random?category=..., /api/categories, richer JSON payload
- New frontend: templates/index.html + static/ (vanilla JS/CSS)
- 4 themes (graphite/paper/phosphor/amber), localStorage persist,
  no-flash boot, prefers-color-scheme default
- Scramble-resolve art transition, reduced-motion support
- Responsive: rail collapses to chips <760px, art auto-fits width
- Fix grid blowout on mobile (min-width: 0 on stage children)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 22:18:14 -07:00
co-authored by Claude Fable 5
parent fdd7f9d751
commit 5e33a38dbb
8 changed files with 1230 additions and 198 deletions
+34 -197
View File
@@ -1,205 +1,42 @@
import random
from flask import Flask, render_template_string, jsonify
from flask import Flask, jsonify, render_template, request
import ascii_engine
app = Flask(__name__)
# Collection of ASCII art templates
ASCII_ART = [
r"""
╔══════════════════════════╗
║ RANDOM ASCII ART ║
║ generated ║
╚══════════════════════════╝
""",
r"""
_
_( )
(_, /|_ _
| / \ (_)
\| \|/
_
___| |_
|_ _| \
|_|
""",
r"""
/ \
/ _ \
/ /_\ \
/_____\
\_ _/
| |
| |
""",
r"""
┌─────────────┐
│ (◕‿◕) │
│ HAPPY! │
└─────────────┘
""",
r"""
.---.
/ \
| (.) |
\ /
'---'
""",
r"""
.-.
(o o)
| O |
\ /
`='`
""",
r"""
/\_/\
( o.o )
> ^ <
""",
r"""
___
/ _ \
| | | |
| | | |
|_| |_|
""",
r"""
┌───┐
│(◉)│
└───┘
^__^
(__)
""",
r"""
*
/|\
/ | \
/ | \
/___|___\
""",
r"""
,---.
/ @_@ \
| ^ |
\ ___ /
'---'
""",
r"""
╭──────────╮
│ SUCCESS │
╰──────────╯
\ /
\ /
\/
""",
]
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random ASCII Art Generator</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', monospace;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
h1 {
color: #00ff88;
margin-bottom: 30px;
text-shadow: 0 0 10px #00ff88;
font-size: 1.8rem;
}
.art-container {
background: #0f0f23;
border: 2px solid #00ff88;
border-radius: 10px;
padding: 30px;
max-width: 90%;
overflow-x: auto;
box-shadow: 0 0 30px rgba(0, 255, 136, 0.3);
}
pre {
color: #00ff88;
font-size: 16px;
line-height: 1.2;
white-space: pre;
}
.btn {
margin-top: 30px;
padding: 15px 40px;
font-size: 18px;
font-family: 'Courier New', monospace;
background: transparent;
color: #00ff88;
border: 2px solid #00ff88;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn:hover {
background: #00ff88;
color: #0f0f23;
box-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
}
.btn:active {
transform: scale(0.95);
}
.btn:focus {
outline: none;
box-shadow: 0 0 10px #00ff88;
}
</style>
</head>
<body>
<h1>🎲 Random ASCII Art Generator</h1>
<div class="art-container">
<pre id="art">{{ art }}</pre>
</div>
<button class="btn" onclick="newArt()">Generate New Art</button>
<script>
function newArt() {
fetch('/api/random')
.then(response => response.json())
.then(data => {
document.getElementById('art').textContent = data.art;
});
}
// Allow spacebar to generate new art
document.addEventListener('keydown', function(e) {
if (e.code === 'Space') {
e.preventDefault();
newArt();
}
});
</script>
</body>
</html>
"""
@app.route('/')
@app.route("/")
def index():
art = random.choice(ASCII_ART)
return render_template_string(HTML_TEMPLATE, art=art)
piece = ascii_engine.generate()
return render_template(
"index.html",
piece=piece,
categories=ascii_engine.category_list(),
)
@app.route('/api/random')
@app.route("/api/random")
def api_random():
return jsonify({'art': random.choice(ASCII_ART)})
category = request.args.get("category")
try:
piece = ascii_engine.generate(category)
except KeyError:
return (
jsonify(
{
"error": f"unknown category '{category}'",
"categories": ascii_engine.category_list(),
}
),
400,
)
return jsonify(piece)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
@app.route("/api/categories")
def api_categories():
return jsonify({"categories": ascii_engine.category_list()})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)