Compare commits

...

2 Commits

Author SHA1 Message Date
Jonathan Carter
d4cfa4815c Add button menu placeholders 2025-08-16 14:22:34 +02:00
Jonathan Carter
9f8f89fde2 More UI work 2025-08-16 14:22:13 +02:00
11 changed files with 216 additions and 17 deletions

View File

@@ -2,14 +2,48 @@
{% block body %} {% block body %}
<center> <center>
<p style="position: absolute: left: 45px; top: 10px;"> <big><big> Installing...</big></big></p> <br /> <p style="position: absolute: left: 45px; top: 10px;">
<big><big>
Installing...
</big></big>
</p>
<br />
<img style="width: 400px; padding: 20px;" src="/static/slide1.png" /> <br /> <img style="width: 400px; padding: 20px;" src="/static/slide1.png" /> <br />
<p> Installing system, the rest of the process is automated. </p> <p>
<p style="margin: 20px;"> <input style="background-color: #777777;" type="checkbox" id="reboot" name="reboot" value="reboot" checked> Installing system, the rest of the process is automated.
</p>
<p style="margin: 20px;">
<input style="background-color: #777777;" type="checkbox"
id="reboot" name="reboot" value="reboot" checked>
<label for="reboot"> Reboot automatically when ready </label></p><br> <label for="reboot"> Reboot automatically when ready </label></p><br>
<div id="output"></div>
<!-- Hidden input to track last line -->
<div id="last-line-container">
<input type="hidden" id="last-line" name="last_line" value="0">
</div>
<!-- Poller -->
<div hx-get="/install/status"
hx-trigger="load, every 1s"
hx-include="#last-line"
hx-swap-oob="true"
hx-target="this"
hx-ext="json-enc"
hx-vars="{}">
</div>
</center> </center>
<div style="position: absolute; padding: 10px; background-color: #26495e; left: 40px; right: 40px; bottom: 40px; height: text-align: middle;"> <center> Initializing... </center> </div> <div style="position: absolute; padding: 10px; background-color: #26495e; left: 40px;
right: 40px; bottom: 40px; height: text-align: middle;">
<center> Initializing... </center>
</div>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,20 @@
from applets.install import bp from applets.install import bp
from flask import Flask, request, session, redirect, \ from flask import Flask, request, session, redirect, \
url_for, render_template, flash, Blueprint url_for, render_template, flash, Blueprint, jsonify
from flask import current_app from flask import current_app
import subprocess
import threading
status = {
"status": "idle", # idle | running | finished
"output": "",
"exit_code": None,
"lines": []
}
@bp.route('/install',methods=['GET', 'POST', 'PUT']) @bp.route('/install',methods=['GET', 'POST', 'PUT'])
def install_index(): def install_index():
""" """
@@ -19,14 +31,74 @@ def install_start():
Trigger the installation process Trigger the installation process
""" """
print("The installation process is starting!... in theory at least") print("The installation process is starting!... in theory at least")
if status["status"] == "running":
return jsonify({"status": "already running"}), 409 # Conflict
threading.Thread(target=run_script).start()
return jsonify({"status": "started"})
print("did it finish?")
@bp.route('/install-status',methods=['GET', 'POST', 'PUT']) def run_script():
global status
status["status"] = "running"
process = subprocess.Popen(
["bash", "/data/jonathan/devel/highvoltage/system-installer/daemon/src/fake-install-shell.sh"],
stdout = subprocess.PIPE,
stderr = subprocess.STDOUT,
text = True)
lines = []
for line in iter(process.stdout.readline,''):
#lines.append(line.strip())
#status["output"] = "\n".join(lines)
status["lines"].append(line.strip())
process.wait()
status["status"] = 'finished'
status["exit_code"] = process.returncode
print(lines)
print("status:", status["status"], "code:", status["exit_code"])
@bp.route('/install/status', methods=['GET', 'POST'])
def install_status(): def install_status():
""" """
Update on the status of the installation process. Update on the status of the installation process.
""" """
print("installation en coers")
# Keep track of the last line posted, so that we can include
# any new lines not provided to the UI yet
last_line = int(request.args.get("last_line", 0))
new_lines = status["lines"][last_line:]
if not new_lines and status["status"] == "finished":
return "" # Empty response = HTMX will stop polling
lines_html = ""
for i, line in enumerate(new_lines, start=last_line + 1):
lines_html += f'<div>{line}</div>'
return {
"output": status['output'],
"status": status['status'],
"lines": lines_html,
"last_line": f'<input type="hidden" id="last-line" name="last_line" value="{last_line + len(new_lines)}">'
}
@bp.route('/install/cancel', methods=["POST"])
def install_cancel():
"""
Cancel the installation process.
"""
if status["status"] != "running" or not status["process"]:
return Response("No running script", status=400)
process["process"].terminate()
process["status"] = "cancelled"
process["exit_code"] = -1
return Response("Cancelled", status=200)
@bp.route('/settings',methods=['GET', 'POST', 'PUT']) @bp.route('/settings',methods=['GET', 'POST', 'PUT'])

View File

@@ -0,0 +1,21 @@
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-content">
<div style="background-color: #424242; padding: 15px; border-radius: 15px; boders: none; color: white;">
<h1 class="is-size-4"> Help </h1>
<p class="title is-6"><span class="tag is-black">The help system is not yet implemented.</span>
<div class="columns">
<a href="/welcome">
<div class="column"><button class="button is-light">Cancel</button></div>
</a>
<a href="/welcome"><div class="column"><button class="button is-link">Continue</button></div></a>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,17 @@
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-content">
<div style="background-color: #424242; padding: 15px; border-radius: 15px; borders: none; color: white;">
<h1 class="is-size-4"> Main menu </h1>
<br />
<p> There's nothing yet to do here.</p>
<br />
<div class="columns">
<a href="/welcome"><div class="column"><button class="button is-link">Continue</button></div></a>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,18 @@
<div class="modal is-active">
<div class="modal-background"></div>
<div class="modal-content">
<div style="background-color: #424242; padding: 15px; border-radius: 15px; boders: none; color: white;">
<h1 class="is-size-4"> Exit system installer </h1>
<p class="title is-6"><span class="tag is-black">Quitting is not yet implemented.</span>
<div class="columns">
<a href="/welcome">
<div class="column"><button class="button is-light">Cancel</button></div>
</a>
<a href="/welcome"><div class="column"><button class="button is-link">Continue</button></div></a>
</div>
</div>
</div>
</div>

View File

@@ -1,5 +1,10 @@
from applets.main import bp from applets.welcome import bp
from flask import Flask, request, session, redirect, \
url_for, render_template, flash, Blueprint
# we use this neat little trick to get config data from the main app
from flask import current_app
import gettext
import dmm.lsblk as lsblk
@bp.route('/') @bp.route('/')
def index(): def index():
@@ -19,3 +24,26 @@ def apihome():
""" """
return ("0") return ("0")
@bp.route('/main/help',methods=['GET', 'POST', 'PUT'])
def main_welcome():
"""
Manages the main help system.
"""
return render_template('help.html')
@bp.route('/main/quit',methods=['GET', 'POST', 'PUT'])
def main_quit():
"""
Manages the quit dialog.
"""
return render_template('quit.html')
@bp.route('/main/menu',methods=['GET', 'POST', 'PUT'])
def main_menu():
"""
Manages the main menu.
"""
return render_template('menu.html')

View File

@@ -11,7 +11,8 @@
<div style="padding-left: 15px;"> <div style="padding-left: 15px;">
<form action="/welcome" method="POST"> <form action="/welcome" method="POST">
<i class="fa fa-language" aria-hidden="true"></i> {{ string_dict['language_text'] }} <i class="fa fa-language-o" aria-hidden="true"></i>
{{ string_dict['language_text'] }}
<div class="control is-link" width="180px"> <div class="control is-link" width="180px">
<div class="select"> <div class="select">
<select hx-post="/welcome" hx-target="body" name="lang" style="width: 220px"> <select hx-post="/welcome" hx-target="body" name="lang" style="width: 220px">

View File

@@ -12,7 +12,8 @@ import importlib
import yaml import yaml
# configuration # configuration
CONFIGFILE="templates/dmm-installer-template.yaml" #CONFIGFILE="templates/dmm-installer-template.yaml"
CONFIGFILE="templates/dmm-installer-vm-template.yaml"
# import config # import config
global config global config

View File

@@ -2,7 +2,9 @@
<div style="position: absolute; bottom: 15px; left: 15px;"> <div style="position: absolute; bottom: 15px; left: 15px;">
{% if previous_step %} {% if previous_step %}
<a href="/{{ previous_step }}"><button class="button is-light">Back</button></a> <!-- Why two of these? So that we can use two different keyboard shortcuts for back. -->
<a href="/{{ previous_step }}"><button hx-get="/{{ previous_step }}" hx-target="body" hx-trigger="click, keyup[ctrlKey&&code=='PageUp'] from:body" class="button is-light">Back</button></a>
<a href="/{{ previous_step }}"><button hx-get="/{{ previous_step }}" hx-target="body" hx-trigger="click, keyup[altKey&&key=='b'] from:body" class=""></button></a>
{% endif %} {% endif %}
{% if bottom_menu %} {% if bottom_menu %}
@@ -16,9 +18,13 @@
<div style="position: absolute; bottom: 15px; right: 15px;"> <div style="position: absolute; bottom: 15px; right: 15px;">
{% if next_step %} {% if next_step %}
{% if next_step == "install" %} {% if next_step == "install" %}
<a href="/{{ next_step }}"><button hx-trigger="click, keyup[shiftKey&&key=='I'] from:body" class="button is-link">Install</button></a> <a href="/{{ next_step }}"><button hx-get="/{{ next_step }}" hx-target="body" hx-trigger="click, keyup[altKey&&key=='i'] from:body" class="button is-link">Install</button></a>
{% else %} {% else %}
<a href="/{{ next_step }}"><button hx-trigger="click, keyup[shiftKey&&key=='N'] from:body" hx-target="/{{ next_step }}" class="button is-link">Next</button></a>
<!-- Why two of these? So that we can use two different keyboard shortcuts for next. -->
<a href="/{{ next_step }}"<button hx-get="/{{ next_step }}" hx-target="body" hx-trigger="click, keyup[ctrlKey&&code=='PageDown'] from:body" class=""></button></a>
<a href="/{{ next_step }}"<button hx-get="/{{ next_step }}" hx-target="body" hx-trigger="click, keyup[altKey&&key=='n'] from:body" class="button is-link">Next</button></a>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@@ -4,7 +4,7 @@
<!-- drag region class: --> <!-- drag region class: -->
<!-- <div id="titlebar" class="pywebview-drag-region" style="padding: 15px; margin: -15px; padding-bottom: 30px;"> --> <!-- <div id="titlebar" class="pywebview-drag-region" style="padding: 15px; margin: -15px; padding-bottom: 30px;"> -->
<button class="button is-link"></button> <a hx-get="/main/menu" hx-swap="outerHTML"/> <button class="button is-link"></button </a>
{% for item in menu_order %} {% for item in menu_order %}
<a href="{{ menu[item][1]}}"> <button class="button is-{% if request.path == menu[item][1] %}info{% else %}dark{% endif %}"> {{ menu[item][0]}} </button></a> <a href="{{ menu[item][1]}}"> <button class="button is-{% if request.path == menu[item][1] %}info{% else %}dark{% endif %}"> {{ menu[item][0]}} </button></a>
@@ -12,8 +12,9 @@
<div style="position: absolute; top: 15px; right: 15px;"> <div style="position: absolute; top: 15px; right: 15px;">
<a href="/help"> <button class="button is-light"> <b> ? </b> </button></a> <a hx-get="/main/help" hx-swap="outerHTML"/> <button class="button is-light"> <b> ? </b> </button></a>
<button onclick="closeApp()" class="button is-danger" > 🗙 </button>
<a hx-get="/main/quit" hx-swap="outerHTML"/><button onclick="closeApp()" class="button is-danger" > 🗙 </button></a>
</div> </div>
</div> <!-- end pywebview-drag-region --> </div> <!-- end pywebview-drag-region -->