list debian deps
This commit is contained in:
@@ -17,6 +17,12 @@ Recommended:
|
||||
- sass-stylesheets-bulma (needed if the web front-end is to be used)
|
||||
- libxapp-gtk3-module
|
||||
|
||||
On Debian-based systems, you should get everything with the following:
|
||||
|
||||
```
|
||||
apt install python3-flask python3-command-runner python3-pytest python3-dmm gettext libjs-htmx sass-stylesheets-bulma fonts-fork-awesome
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
**Please Note:** This project is in early development, and if you have stumbled across this page, please note that it is nowhere near ready for production use yet. Hopefully that changes over the next few months!
|
||||
|
||||
@@ -15,13 +15,14 @@ window = webview.create_window('System Installer', LOCATION,
|
||||
transparent=True, easy_drag=False, frameless=True,
|
||||
width=1050, focus=True, zoomable=True,
|
||||
confirm_close=True, resizable=True, shadow=True,
|
||||
text_select=False
|
||||
text_select=False, draggable=False
|
||||
)
|
||||
|
||||
webview.settings = {
|
||||
'ALLOW_DOWNLOADS': False,
|
||||
'ALLOW_FILE_URLS': False,
|
||||
'DRAG_REGION_SELECTOR': 'pywebview-drag-region',
|
||||
'DRAG_REGION_SELECTOR': '.pywebview-drag-region',
|
||||
'DRAG_REGION_DIRECT_TARGET_ONLY': True,
|
||||
'OPEN_EXTERNAL_LINKS_IN_BROWSER': True,
|
||||
'OPEN_DEVTOOLS_IN_DEBUG': True,
|
||||
'IGNORE_SSL_ERRORS': False,
|
||||
@@ -30,4 +31,5 @@ webview.settings = {
|
||||
}
|
||||
|
||||
window.events.closed += on_closed
|
||||
webview.start(icon='/usr/share/icons/yasi.png')
|
||||
#webview.start(icon='/usr/share/icons/yasi.png')
|
||||
webview.start()
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
<input type="radio" name="rsvp" disabled />
|
||||
Edit existing partitions
|
||||
</label>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div class="columns" width="100%">
|
||||
<div class="column"><p> <br>Current layout: </p></div>
|
||||
@@ -66,7 +66,7 @@
|
||||
-->
|
||||
|
||||
|
||||
{% for disk in blockdevs["blockdevices"] if disk["name"] != "zram0" and disk["name"] != "loop0" and disk["name"] != "sr0" %}
|
||||
{% for disk in blockdevs["blockdevices"] if disk["name"] != "zram0" and "loop" not in disk["name"] and disk["name"] != "sr0" %}
|
||||
|
||||
<!--
|
||||
<h1> Raw data </h1>
|
||||
@@ -148,14 +148,28 @@ fssize: {{ child['fssize'] }}
|
||||
<div class="column"><p> <br>Proposed partitioning: </p></div>
|
||||
</div>
|
||||
|
||||
<div style=""> <!-- partition bars -->
|
||||
<div style="background-color: gray; padding: 4px 6px 7px 6px; border-radius: 10px;">
|
||||
<p> <span style="width: 100%; border-radius: 8px 8px 0 0;" class="tag is-black">
|
||||
<small><small> New GPT Partition Table</small></small>
|
||||
<div class="columns">
|
||||
{% for partition in partitions %}
|
||||
<ul>
|
||||
<li> {{ partition }} </li>
|
||||
<ul>
|
||||
|
||||
<!-- {% set commands = partition.split(' ') %} -->
|
||||
|
||||
{% if "mkpart" in partition['command'] %}
|
||||
<a hx-get="/disks/partition/{{ partition['device'] }}" hx-swap="outerHTML"/ style="{% if "100%" in partition['command'] %}width: 100%; {% endif %}">
|
||||
<div class="column is-narrow is-primary">
|
||||
<div class="box" style="min-width: 220px; border-radius: 0 0 0px 5px; height: 60px; padding-top: 5px;">
|
||||
<p class="is-tiny"> {{ partition['device'] }} </p>
|
||||
<p class="title is-5"><span class="tag is-black"> {{ partition['mountpoint'] }} </span>
|
||||
<span class="tag is-link"> {{ partition['fstype'] }} </span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ def disks():
|
||||
menu_order=current_app.config['CONFIG']['settings']['menu_order'].split(),
|
||||
previous_step = previous_step_url,
|
||||
next_step=next_step_url,
|
||||
partitions=current_app.config['CONFIG']['recipe']['create_partitions']['commands'],
|
||||
partitions=current_app.config['CONFIG']['recipe']['create_partitions']['command_set'],
|
||||
format_partitions=current_app.config['CONFIG']['recipe']['format_partitions'],
|
||||
bottom_menu=bottom_menu)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<!-- drag region class: -->
|
||||
<div id="titlebar" class="pywebview-drag-region" style="padding: 15px; margin: -15px; padding-bottom: 30px;">
|
||||
|
||||
|
||||
<!--
|
||||
You might be tempted to replace the unicode character by a fa-code as in the
|
||||
alternate commend below. Don't do it, it causes all kinds of artifacts and
|
||||
|
||||
@@ -5,6 +5,18 @@ from flask import Flask, request, session, redirect, \
|
||||
from flask import current_app
|
||||
import gettext
|
||||
|
||||
# Set up Gettext
|
||||
def set_language(LANG):
|
||||
"""
|
||||
Sets language for this applet
|
||||
"""
|
||||
# TODO: unhardcode this path
|
||||
translations = gettext.translation("welcome", '/usr/share/yasi-daemon/yasi_applets/welcome/locales',
|
||||
fallback=True, languages=[LANG])
|
||||
translations.install()
|
||||
_ = translations.gettext
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
def index():
|
||||
"""
|
||||
@@ -37,3 +49,14 @@ def main_menu():
|
||||
Manages the main menu.
|
||||
"""
|
||||
return render_template('menu.html')
|
||||
|
||||
|
||||
def build_stringlist():
|
||||
"""
|
||||
Return all the strings that is used in this applet.
|
||||
"""
|
||||
string_dict = {}
|
||||
string_dict['next_text'] = _("Next")
|
||||
string_dict['previous_text'] = _("Previous")
|
||||
return string_dict
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
sodipodi:docname="keyboard.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="52.375"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="1236"
|
||||
inkscape:window-height="1041"
|
||||
inkscape:window-x="662"
|
||||
inkscape:window-y="199"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<g
|
||||
font-weight="400"
|
||||
fill="#474747"
|
||||
id="g2"
|
||||
style="fill:#c0bfbc">
|
||||
<path
|
||||
d="M3.702 1C2.17 1 .984 2.32.984 3.844v8.344c0 1.524 1.185 2.843 2.718 2.843h8.58c1.532 0 2.75-1.32 2.75-2.844V3.845c0-1.525-1.218-2.844-2.75-2.844zm.782 1.031c3.526.256 5.317.134 7.047 0 .754-.058 1.532.616 1.532 1.438v7.375c0 .665-.532 1.095-1.188 1.187-2.836.397-4.753.44-7.673 0-.655-.099-1.187-.522-1.187-1.187V3.5c0-.822.714-1.524 1.469-1.469z"
|
||||
style="line-height:normal;-inkscape-font-specification:'Bitstream Vera Sans';text-indent:0;text-align:start;text-decoration-line:none;text-transform:none;marker:none;fill:#c0bfbc"
|
||||
color="#bebebe"
|
||||
font-family="Bitstream Vera Sans"
|
||||
overflow="visible"
|
||||
id="path1" />
|
||||
<path
|
||||
d="M10.564 5.977l-2.939.044.016 1 2.879-.043c.2.018.33.076.396.135.067.06.105.121.105.293l-.008.604H8.517l.037.002a1.457 1.457 0 00-1.164.43 1.558 1.558 0 00-.416 1.023c-.013.367.092.75.352 1.053.26.303.687.496 1.162.482h3.482l.051-3.59v-.004c0-.402-.16-.786-.435-1.035-.276-.249-.63-.364-.998-.393zM8.48 9.01h2.519l-.014.99H8.457c-.22.007-.302-.051-.371-.133a.553.553 0 01-.112-.367.587.587 0 01.141-.371c.079-.083.163-.134.363-.12zm2.862-5.985l-3 1 .316.95 3-1z"
|
||||
style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;shape-padding:0;isolation:auto;mix-blend-mode:normal;marker:none;fill:#c0bfbc"
|
||||
color="#000"
|
||||
font-family="sans-serif"
|
||||
overflow="visible"
|
||||
id="path2" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M192 64C209.7 64 224 78.3 224 96L224 128L352 128C369.7 128 384 142.3 384 160C384 177.7 369.7 192 352 192L342.4 192L334 215.1C317.6 260.3 292.9 301.6 261.8 337.1C276 345.9 290.8 353.7 306.2 360.6L356.6 383L418.8 243C423.9 231.4 435.4 224 448 224C460.6 224 472.1 231.4 477.2 243L605.2 531C612.4 547.2 605.1 566.1 589 573.2C572.9 580.3 553.9 573.1 546.8 557L526.8 512L369.3 512L349.3 557C342.1 573.2 323.2 580.4 307.1 573.2C291 566 283.7 547.1 290.9 531L330.7 441.5L280.3 419.1C257.3 408.9 235.3 396.7 214.5 382.7C193.2 399.9 169.9 414.9 145 427.4L110.3 444.6C94.5 452.5 75.3 446.1 67.4 430.3C59.5 414.5 65.9 395.3 81.7 387.4L116.2 370.1C132.5 361.9 148 352.4 162.6 341.8C148.8 329.1 135.8 315.4 123.7 300.9L113.6 288.7C102.3 275.1 104.1 254.9 117.7 243.6C131.3 232.3 151.5 234.1 162.8 247.7L173 259.9C184.5 273.8 197.1 286.7 210.4 298.6C237.9 268.2 259.6 232.5 273.9 193.2L274.4 192L64.1 192C46.3 192 32 177.7 32 160C32 142.3 46.3 128 64 128L160 128L160 96C160 78.3 174.3 64 192 64zM448 334.8L397.7 448L498.3 448L448 334.8z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -10,6 +10,7 @@ body {
|
||||
overflow: hidden;
|
||||
min-width: 1050px;
|
||||
min-height: 600px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html, body {margin: 0; height: 100%; overflow: hidden;}
|
||||
@@ -23,6 +24,7 @@ html, body {margin: 0; height: 100%; overflow: hidden;}
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.applet a:link a:hover a:active {
|
||||
@@ -36,7 +38,7 @@ html, body {margin: 0; height: 100%; overflow: hidden;}
|
||||
|
||||
.applet {
|
||||
width: 100%;
|
||||
height: 442px;
|
||||
height: 512px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
overflow: -moz-scrollbars-none;
|
||||
@@ -45,3 +47,8 @@ html, body {margin: 0; height: 100%; overflow: hidden;}
|
||||
.radio:hover {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
.pywebview-drag-region {
|
||||
width: 100%;
|
||||
height: 85px;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ def set_language(LANG):
|
||||
"""
|
||||
Sets language for this applet
|
||||
"""
|
||||
translations = gettext.translation("users", './applets/users/locales',
|
||||
translations = gettext.translation("users", '/usr/share/yasi-daemon/yasi_applets/users/locales',
|
||||
fallback=True, languages=[LANG])
|
||||
translations.install()
|
||||
_ = translations.gettext
|
||||
@@ -101,6 +101,8 @@ def build_stringlist():
|
||||
string_dict['password_confirm'] = _("Password (confirm)")
|
||||
string_dict['password_nomatch'] = _("These passwords do now match")
|
||||
string_dict['password_tooshort'] = _("This password is too short")
|
||||
string_dict['next_text'] = _("Next")
|
||||
string_dict['back_text'] = _("Back")
|
||||
return string_dict
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -12,6 +12,33 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: UTF-8\n"
|
||||
|
||||
msgid "Welcome"
|
||||
msgstr "Welkom"
|
||||
|
||||
msgid "Users"
|
||||
msgstr "Gebruikers"
|
||||
|
||||
msgid "Disks"
|
||||
msgstr "Skywe"
|
||||
|
||||
msgid "Software"
|
||||
msgstr "Sagteware"
|
||||
|
||||
msgid "Summary"
|
||||
msgstr "Opsomming"
|
||||
|
||||
msgid "Power"
|
||||
msgstr "Krag"
|
||||
|
||||
msgid "Internet"
|
||||
msgstr "Internet"
|
||||
|
||||
msgid "Back"
|
||||
msgstr "Terug"
|
||||
|
||||
msgid "Next"
|
||||
msgstr "Volgende"
|
||||
|
||||
#: routes.py:27
|
||||
msgid "Welcome! This setup program will install Debian on to your system."
|
||||
msgstr "Welkom! Hierdie program sal jou deur die proses lei om Debian te installeer na jou sisteem."
|
||||
|
||||
@@ -14,7 +14,8 @@ def set_language(LANG):
|
||||
"""
|
||||
Sets language for this applet
|
||||
"""
|
||||
translations = gettext.translation("welcome", './applets/welcome/locales',
|
||||
# TODO: unhardcode this path
|
||||
translations = gettext.translation("welcome", '/usr/share/yasi-daemon/yasi_applets/welcome/locales',
|
||||
fallback=True, languages=[LANG])
|
||||
translations.install()
|
||||
_ = translations.gettext
|
||||
@@ -40,6 +41,7 @@ def welcome_index():
|
||||
global lang
|
||||
lang = current_app.config['CONFIG']['settings']['language']
|
||||
set_language(lang)
|
||||
global string_dict
|
||||
string_dict = build_stringlist()
|
||||
blkid = lsblk.list_scsi_devices()
|
||||
build_menu()
|
||||
@@ -54,8 +56,8 @@ def welcome_index():
|
||||
else:
|
||||
previous_step_url = menu[menu.index("users")-1]
|
||||
|
||||
power_button = '<button id="powerbutton" hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> Power </button>'
|
||||
internet_button = ' <button id="internetbutton" class="button is-dark" hx-get="/welcome/internet/button" hx-trigger="load, every 1s" hx-target="#internetbutton"><span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> Internet </button>'
|
||||
power_button = '<button id="powerbutton" hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> </button>'
|
||||
internet_button = ' <button id="internetbutton" class="button is-dark" hx-get="/welcome/internet/button" hx-trigger="load, every 1s" hx-target="#internetbutton"><span style="color: #ffffff;"> <i class="fa fa-question" aria-hidden="true"></i></span> </button>'
|
||||
bottom_menu = power_button + internet_button
|
||||
|
||||
|
||||
@@ -99,7 +101,7 @@ def welcome_battery_button():
|
||||
if on_battery:
|
||||
power_button = '<button id="powerbutton hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #ffa45c;"> <i class="fa fa-warning" aria-hidden="true"></i></span> Power: Battery: ' + percentage + '%</button>'
|
||||
else:
|
||||
power_button = '<button id="powerbutton hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #a0ff98;"> <i class="fa fa-check" aria-hidden="true"></i></span> Power </button>'
|
||||
power_button = """<button id="powerbutton hx-get="/welcome/battery/button" hx-trigger="load, every 1s" hx-target="#powerbutton" class="button is-dark"> <span style="color: #a0ff98;"> <i class="fa fa-check" aria-hidden="true"></i></span> """ + string_dict["power_text"] + "</button>"""
|
||||
return str(power_button)
|
||||
|
||||
|
||||
@@ -135,12 +137,21 @@ def build_stringlist():
|
||||
"""
|
||||
string_dict = {}
|
||||
string_dict['menu_item'] = _("Welcome")
|
||||
# In the future, these will be loaded from their own modules
|
||||
string_dict['menu_item_users'] = _("Users")
|
||||
string_dict['menu_item_disks'] = _("Disks")
|
||||
string_dict['menu_item_software'] = _("Software")
|
||||
string_dict['menu_item_summary'] = _("Summary")
|
||||
string_dict['welcome_text'] = _("Welcome! This setup program will install Debian on to your system.")
|
||||
string_dict['confirm_text'] = _("Please confirm the following details:")
|
||||
string_dict['language_text'] = _("Language:")
|
||||
string_dict['keylayout_text'] = _("Keyboard Layout:")
|
||||
string_dict['timezone_text'] = _("Time Zone:")
|
||||
string_dict['basicsettings_text'] = _("Basic Settings:")
|
||||
string_dict['power_text'] = _("Power")
|
||||
string_dict['internet_text'] = _("Internet")
|
||||
string_dict['next_text'] = _("Next")
|
||||
string_dict['back_text'] = _("Back")
|
||||
lang_dict = {}
|
||||
lang_dict['af'] = _("Afrikaans")
|
||||
lang_dict['en'] = _("English (International)")
|
||||
@@ -157,6 +168,12 @@ def build_menu():
|
||||
"""
|
||||
current_app.config['CONFIG']['settings']['menu']['welcome'] = (build_stringlist()['menu_item'], "/welcome", 10)
|
||||
|
||||
#TODO: temporary hack: We need an automated way to translate all menu items
|
||||
current_app.config['CONFIG']['settings']['menu']['users'] = (build_stringlist()['menu_item_users'], "/users", 20)
|
||||
current_app.config['CONFIG']['settings']['menu']['disks'] = (build_stringlist()['menu_item_disks'], "/disks", 30)
|
||||
current_app.config['CONFIG']['settings']['menu']['software'] = (build_stringlist()['menu_item_software'], "/software", 40)
|
||||
current_app.config['CONFIG']['settings']['menu']['summary'] = (build_stringlist()['menu_item_summary'], "/summary", 50)
|
||||
|
||||
|
||||
def build_summary():
|
||||
"""
|
||||
|
||||
@@ -5,34 +5,52 @@
|
||||
src="/welcome/static/img/banner.png"
|
||||
alt="Debian Image banner" />
|
||||
|
||||
<!-- This odd, yet very efficient mechanism ensures that translations,
|
||||
settings and summaries for each page gets loaded -->
|
||||
<div hx-get="/users" hx-target="#none" hx-trigger="load"></div>
|
||||
<div hx-get="/disks" hx-target="#none" hx-trigger="load"></div>
|
||||
<div hx-get="/software" hx-target="#none" hx-trigger="load"></div>
|
||||
<div hx-get="/summary" hx-target="#none" hx-trigger="load"></div>
|
||||
<!-- TODO: Add hook to fix next tab again after loading summary -->
|
||||
<div style="display: none;" id="none"></div>
|
||||
|
||||
|
||||
<p><b> {{ string_dict['welcome_text'] }} </b></p>
|
||||
<p>{{ string_dict['confirm_text'] }} </p> <br />
|
||||
|
||||
<form action="/welcome" method="POST">
|
||||
<i class="fa fa-language" aria-hidden="true"></i> {{ string_dict['language_text'] }}
|
||||
<div class="control is-link" width="180px">
|
||||
<div style="left: 25px; display: flex; align-items: center; gap: 1rem;">
|
||||
<img style="width: 80px;" src="/welcome/static/img/lang_w.svg" alt="Language icon" />
|
||||
<form action="/welcome" method="POST" style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<label for="lang">{{ string_dict['language_text'] }}</label>
|
||||
<div class="control is-link">
|
||||
<div class="select">
|
||||
<select hx-post="/welcome" hx-target="body" name="lang" style="width: 220px">
|
||||
<select id="lang" name="lang" hx-post="/welcome" hx-target="body" style="width: 220px;">
|
||||
{% for lang in string_dict['lang_list'] %}
|
||||
<option value="{{lang}}" {% if lang == selected_lang %} selected=selected {% endif %}>{{ string_dict['lang_list'][lang] }}</option>
|
||||
<option value="{{ lang }}" {% if lang == selected_lang %} selected {% endif %}>
|
||||
{{ string_dict['lang_list'][lang] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<i class="fa fa-keyboard-o" aria-hidden="true"></i> {{ string_dict['keylayout_text'] }}
|
||||
<div class="control is-link">
|
||||
<br />
|
||||
|
||||
<div style="left: 25px; display: flex; align-items: center; gap: 1rem;">
|
||||
<img style="width: 80px;" src="/welcome/static/img/keyboard.svg" alt="Language icon" />
|
||||
<form action="/welcome" method="POST" style="display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
<label for="keyboard">{{ string_dict['keylayout_text'] }}</label>
|
||||
<div class="control is-link">
|
||||
<div class="select">
|
||||
<select hx-post="/welcome" hx-target="body" name="keyboard" style="width: 220px">
|
||||
<select name="keyboard" hx-post="/welcome" hx-target="body" style="width: 220px;">
|
||||
<option>en-us</option>
|
||||
<option>en-uk</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user