New theme for web UI (#957)

* Docker environment fixes

* New theme for web UI

* Apply breaking wrap to filenames only

* Reduce font sizes, whitespace and padding

* Right align action fields/buttons

* Improve mobile header, hide superfluous UI elements when logged out, drop placeholders from login labels, various other adjustments

* Force footer to bottom of screen

* Show manual link to logged out users

* Reduce header text size on desktop

* Fix incorrect selector ID

* Fix selector referencing old class name

* Fix right-aligned message when images table empty

* Add CSS linter/auto-formatter

* Run Stylelint + Prettier against modern theme CSS

* Select default theme based on browser’s user agent

* Style inputs on mobile/tablet devices

* Fixes for Safari 14 on iOS + iPad OS

* Explicitly define mobile browser support, switch to bare ua-parser without user-agent wrapper

* Add LICENSE file for modern theme icons

* Improve theme selection query string/field naming.

* Remove patch workaround from Docker build

* Update log level for UAs to info

* Move Bootstrap Reboot CSS to CDN

* Account for LUN column in attached devices table

* Prevent wrapping of config forms on small viewports

* Fix Stylelint issues

* Auto-format CSS with Prettier
This commit is contained in:
nucleogenic 2022-11-14 17:32:15 +00:00 committed by GitHub
parent 5920315730
commit 3627b39af4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 4610 additions and 139 deletions

View File

@ -7,11 +7,10 @@
!/docker/rascsi-web/start.sh
!/doc
!/python
!/src
!/cpp
!/test
!/easyinstall.sh
!/LICENCE
!/lido-driver.img
!/README.md
# From .gitignore

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ rascsi_interface_pb2.py
messages.pot
messages.mo
report.xml
node_modules
# Intermediate files from astyle
*.orig

View File

@ -6,19 +6,27 @@ FROM "${OS_ARCH}/${OS_DISTRO}:${OS_VERSION}"
EXPOSE 80 443
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog procps man-db man2html
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
sudo \
systemd \
rsyslog \
procps \
man-db \
wget \
git
RUN groupadd pi
RUN useradd --create-home --shell /bin/bash -g pi pi
RUN echo "pi ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN echo "pi:rascsi" | chpasswd
RUN mkdir /home/pi/afpshare
RUN mkdir /home/pi/shared_files
RUN touch /etc/dhcpcd.conf
RUN mkdir -p /etc/network/interfaces.d/
WORKDIR /home/pi/RASCSI
USER pi
WORKDIR /home/pi/RASCSI
COPY --chown=pi:pi . .
# Install standalone RaSCSI web UI

View File

@ -6,20 +6,16 @@ FROM "${OS_ARCH}/${OS_DISTRO}:${OS_VERSION}"
EXPOSE 6868
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog patch
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog patch wget
RUN groupadd pi
RUN useradd --create-home --shell /bin/bash -g pi pi
RUN echo "pi ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
WORKDIR /home/pi/RASCSI
USER pi
WORKDIR /home/pi/RASCSI
COPY --chown=pi:pi . .
# Workaround for Bullseye amd64 compilation error
# https://github.com/akuker/RASCSI/issues/821
RUN patch -p0 < docker/rascsi/cfilesystem.patch
# Install RaSCSI standalone
RUN ./easyinstall.sh --run_choice=10 --cores=`nproc`

View File

@ -1,24 +0,0 @@
--- src/raspberrypi/devices/cfilesystem.cpp 2022-09-08 12:07:14.000000000 +0100
+++ src/raspberrypi/devices/cfilesystem.cpp.patched 2022-09-08 12:12:55.000000000 +0100
@@ -1075,12 +1075,15 @@
m_dirHuman.name[i] = ' ';
}
- for (i = 0; i < 10; i++) {
- if (p < m_pszHumanExt)
- m_dirHuman.add[i] = *p++;
- else
- m_dirHuman.add[i] = '\0';
- }
+ // This code causes a compilation error on Debian "bullseye" (amd64)
+ // https://github.com/akuker/RASCSI/issues/821
+ //
+ // for (i = 0; i < 10; i++) {
+ // if (p < m_pszHumanExt)
+ // m_dirHuman.add[i] = *p++;
+ // else
+ // m_dirHuman.add[i] = '\0';
+ // }
if (*p == '.')
p++;

View File

@ -0,0 +1,3 @@
{
"printWidth": 100
}

View File

@ -0,0 +1,6 @@
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": {
"no-descending-specificity": null
}
}

3343
python/web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

8
python/web/package.json Normal file
View File

@ -0,0 +1,8 @@
{
"devDependencies": {
"prettier": "2.7.1",
"stylelint": "^14.14.1",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard": "^29.0.0"
}
}

View File

@ -5,3 +5,4 @@ protobuf==3.20.2
requests==2.28.1
simplepam==0.1.5
flask_babel==2.0.0
ua-parser==0.16.1

View File

@ -22,3 +22,12 @@ AUTH_GROUP = "rascsi"
# The language locales supported by RaSCSI
LANGUAGES = ["en", "de", "sv", "fr", "es"]
# Available themes
TEMPLATE_THEMES = ["classic", "modern"]
# Default theme for modern browsers
TEMPLATE_THEME_DEFAULT = "modern"
# Fallback theme for older browsers
TEMPLATE_THEME_LEGACY = "classic"

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -47,11 +47,15 @@ div.footer {
font-family: monospace;
}
div.logged_in {
div.footer div.theme-change-hint {
margin-bottom: 15px;
}
div.logged-in {
background-color: green;
}
div.logged_out {
div.logged-out {
background-color: red;
}
@ -66,9 +70,12 @@ div.flash {
div.flash div {
color: white;
font-size: 18px;
white-space: pre-line;
padding: 2px 5px;
font-size: 18px;
}
div.flash div div {
white-space: pre-line;
}
div.flash div.success {

View File

@ -0,0 +1,26 @@
Feather Icons
https://github.com/feathericons/feather
---
The MIT License (MIT)
Copyright (c) 2013-2017 Cole Bemis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-hard-drive"><line x1="22" y1="12" x2="2" y2="12"></line><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path><line x1="6" y1="16" x2="6.01" y2="16"></line><line x1="10" y1="16" x2="10.01" y2="16"></line></svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-disc"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="3"></circle></svg>

After

Width:  |  Height:  |  Size: 295 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-server"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>

After

Width:  |  Height:  |  Size: 431 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-printer"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>

After

Width:  |  Height:  |  Size: 407 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-save"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>

After

Width:  |  Height:  |  Size: 392 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-slash"><circle cx="12" cy="12" r="10"></circle><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line></svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-octagon"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>

After

Width:  |  Height:  |  Size: 409 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-copy"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>

After

Width:  |  Height:  |  Size: 356 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>

After

Width:  |  Height:  |  Size: 371 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-zap"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>

After

Width:  |  Height:  |  Size: 282 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>

After

Width:  |  Height:  |  Size: 308 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>

After

Width:  |  Height:  |  Size: 365 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-info"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>

After

Width:  |  Height:  |  Size: 347 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>

After

Width:  |  Height:  |  Size: 360 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-book"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-book"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>

After

Width:  |  Height:  |  Size: 255 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="red" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x-circle"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>

After

Width:  |  Height:  |  Size: 337 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-up-circle"><circle cx="12" cy="12" r="10"></circle><polyline points="16 12 12 8 8 12"></polyline><line x1="12" y1="16" x2="12" y2="8"></line></svg>

After

Width:  |  Height:  |  Size: 357 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-pause-circle"><circle cx="12" cy="12" r="10"></circle><line x1="10" y1="15" x2="10" y2="9"></line><line x1="14" y1="15" x2="14" y2="9"></line></svg>

After

Width:  |  Height:  |  Size: 352 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check-circle"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>

After

Width:  |  Height:  |  Size: 321 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-triangle"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>

After

Width:  |  Height:  |  Size: 424 B

View File

@ -0,0 +1,890 @@
@import url("//cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.2/css/bootstrap-reboot.min.css");
:root {
--success: var(--bs-success);
--danger: var(--bs-danger);
--info: #80eaff;
--warning: var(--bs-warning);
--dark: var(--bs-dark);
--light: var(--bs-light);
--primary: var(--bs-primary);
--secondary: var(--bs-secondary);
--text-color: var(--bs-body-color);
--border-radius: 0.2rem;
--input-padding: 0.25rem 0.5rem;
--font-size: 0.85rem;
--icon-size: 1.2rem;
}
/*
------------------------------------------------------------------------------
General layout
------------------------------------------------------------------------------
*/
html,
body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
font-size: var(--font-size);
}
div.content {
flex-grow: 1;
padding: 1rem;
margin: auto;
width: 100%;
}
hr {
display: none;
}
a:hover {
text-decoration: none;
}
/*
------------------------------------------------------------------------------
Tables
------------------------------------------------------------------------------
*/
table {
width: 100%;
border: 1px solid var(--dark);
border-collapse: collapse;
}
table th,
table td {
padding: 0.5rem;
text-align: left;
height: 2.5rem;
}
table th {
background: var(--dark);
border: 1px solid var(--dark);
color: #fff;
}
table td {
border: 1px solid #ccc;
padding: 0.25rem 0.5rem;
}
/*
------------------------------------------------------------------------------
Forms
------------------------------------------------------------------------------
*/
form {
display: inline-block;
}
input,
select,
button,
label {
margin: 0.15rem 0;
}
input,
select,
button {
border-radius: var(--border-radius);
border: 1px solid #ccc;
font-size: var(--font-size);
font-weight: 400;
line-height: 1.25;
color: var(--text-color);
}
input[type="submit"],
button {
padding: var(--input-padding);
background-color: #efefef;
}
input[type="text"],
input[type="number"],
input[type="url"],
input[type="password"] {
display: inline-block;
padding: var(--input-padding);
background-color: #fff;
background-clip: padding-box;
appearance: none;
}
select {
display: inline-block;
padding: 0.275rem 2.25rem 0.275em 0.75rem;
-moz-padding-start: calc(0.75rem - 3px);
background-color: #fff;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 16px 12px;
appearance: none;
}
/*
------------------------------------------------------------------------------
Dropzone
------------------------------------------------------------------------------
*/
.dropzone {
display: flex;
flex-wrap: wrap;
}
.dropzone p,
.dropzone .dz-default {
flex: 0 0 100%;
}
.dropzone .dz-button {
width: 100%;
padding: 2rem 4rem;
border: 2px dashed darkcyan;
background: lightcyan;
}
.dropzone .dz-preview .dz-image,
.dropzone .dz-preview .dz-success-mark,
.dropzone .dz-preview .dz-error-mark {
display: none;
}
.dropzone .dz-preview {
display: inline-block;
background: var(--light) url("icons/upload-queued.svg") no-repeat 1rem center;
padding: 1rem 1rem 1rem 3.5rem;
margin: 1rem 1rem 0 0;
border-radius: var(--border-radius);
}
.dropzone .dz-preview.dz-processing {
background: #ededbe url("icons/upload-in-progress.svg") no-repeat 1rem center;
}
.dropzone .dz-preview.dz-success {
background: #e0f5df url("icons/upload-success.svg") no-repeat 1rem center;
}
.dropzone .dz-preview.dz-error {
background: #fae2e2 url("icons/upload-error.svg") no-repeat 1rem center;
}
.dropzone .dz-preview .dz-error-message {
color: var(--danger);
}
.dropzone .dz-preview.dz-processing .dz-progress {
display: block;
}
.dropzone .dz-preview.dz-error .dz-progress,
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
display: none;
}
.dropzone .dz-preview .dz-progress .dz-upload {
width: 1px;
background: var(--dark);
display: block;
height: 0.5rem;
margin-top: 0.25rem;
}
/*
------------------------------------------------------------------------------
Header
------------------------------------------------------------------------------
*/
div.header {
display: flex;
}
div.header div.title {
order: 1;
text-align: left;
flex-grow: 1;
}
div.header div.title h1 {
margin: 0;
color: #f9f9f9;
font-size: 1.25rem;
}
div.header div.title a {
text-decoration: none;
}
div.header div.hostname {
display: none;
}
div.header div.login-status {
order: 10;
}
div.header div.login-status form {
display: flex;
}
div.header span.logged-in-as-text em {
font-weight: bold;
font-style: normal;
}
div.header div.login-form-title {
display: none;
}
div.header div.authentication-disabled {
background: var(--danger);
border-radius: var(--border-radius);
padding: 0 0.5rem;
}
@media (max-width: 820px) {
div.header {
min-height: 3.5rem; /* Safari 14 iOS and iPad OS */
}
body:not(.logged-in) div.header {
flex-wrap: wrap;
min-height: 8.875rem; /* Safari 14 iOS and iPad OS */
}
div.header div.title {
background: var(--dark);
}
div.header div.title a {
display: block;
background: url("/static/logo.png") no-repeat;
background-size: auto 2rem;
background-position: 1rem center;
padding: 1rem 1rem 1rem 3.5rem;
}
div.header div.login-status.logged-out {
flex: 0 0 100%;
}
div.header div.login-status.logged-out form {
align-items: end;
padding: 1rem;
background: var(--light);
border-bottom: 1px solid #ccc;
}
div.header div.login-status.logged-out form span {
display: block;
padding: 0 0.1rem;
flex-grow: 1;
}
div.header div.login-status.logged-out form label {
display: block;
text-align: left;
margin: 0;
padding: 0;
}
div.header div.login-status.logged-out form input[type="submit"] {
flex-grow: 0.5;
margin-top: auto; /* Safari 14 iOS and iPad OS */
}
div.header div.login-status.logged-out form input:not([type="submit"]) {
width: 100%;
}
div.header div.login-status.logged-in {
background: var(--dark);
display: flex;
align-items: center;
}
div.header div.login-status.logged-in span.logged-in-as-text,
div.header div.login-status.logged-in span.separator {
display: none;
}
div.header div.login-status.logged-in a {
margin-right: 1rem;
color: var(--secondary);
text-decoration: none;
}
}
@media (min-width: 821px) {
div.header {
background: var(--dark);
align-items: center;
padding: 0.5rem 1.25rem;
color: #fff;
}
div.header div.title a {
display: inline-block;
background: url("/static/logo.png") no-repeat;
background-size: auto 40px;
background-position: left center;
padding-left: 3rem;
}
div.header div.title a h1 {
font-size: 1.5rem;
padding: 0.25rem;
}
@supports (-webkit-background-clip: text) {
div.header div.title a:hover h1 {
background: linear-gradient(
to right,
rgb(101 204 51 / 100%) 0%,
rgb(255 204 51 / 100%) 10%,
rgb(255 153 51 / 100%) 20%,
rgb(205 51 50 / 100%) 55%,
rgb(152 50 153 / 100%) 100%
);
-webkit-background-clip: text; /* stylelint-disable-line */
-webkit-text-fill-color: transparent; /* stylelint-disable-line */
}
}
div.header div.login-status.logged-out form label,
div.header div.login-status.logged-out form input {
margin-left: 0.5rem;
}
div.header div.login-status.logged-out form input:not([type="submit"]) {
width: 8rem;
background: var(--dark);
border-color: var(--secondary);
color: #fff;
}
div.header div.login-status.logged-out form input::-webkit-credentials-auto-fill-button {
background-color: #ccc;
}
div.header div.login-status.logged-out form input[type="submit"] {
background: var(--secondary);
border-color: var(--secondary);
color: #fff;
}
div.header div.login-status.logged-in a {
background: var(--danger) url("icons/log-out.svg") no-repeat right 0.5rem center;
background-size: var(--icon-size);
border-radius: var(--border-radius);
padding: 0.25rem 2.25rem 0.25rem 0.75rem;
display: inline-block;
text-decoration: none;
color: #fff;
}
div.header div.login-status.logged-in span.logged-in-as-text {
margin-right: 1rem;
}
div.header div.login-status.logged-in span.separator {
display: none;
}
}
/*
------------------------------------------------------------------------------
Footer
------------------------------------------------------------------------------
*/
div.footer {
flex-shrink: 0;
background: var(--dark);
padding: 1rem;
color: #fff;
}
div.footer a {
color: #ccc;
}
div.footer div.theme-change-hint {
margin-bottom: 1rem;
}
div.footer div.theme-change-hint a {
color: yellow;
}
/*
------------------------------------------------------------------------------
Flash messages
------------------------------------------------------------------------------
*/
div.flash > div {
margin: 1rem 1rem 0;
padding: 0.5rem 0.75rem 0.5rem 3rem;
border-radius: var(--border-radius);
background-color: #efefef;
background-repeat: no-repeat;
background-position: 1rem center;
display: flex;
align-items: center;
}
div.flash > div a {
display: inline-block !important;
padding: 0.25rem 0.75rem;
margin-left: auto;
color: #fff;
text-decoration: none;
font-size: 1.25rem;
font-weight: bold;
}
div.flash > div a::before {
content: "×";
}
div.flash > div.info {
background-color: var(--info);
background-image: url("icons/info.svg");
}
div.flash > div.error {
background-color: var(--danger);
background-image: url("icons/error.svg");
color: #fff;
}
div.flash > div.success {
background-color: var(--success);
background-image: url("icons/success.svg");
color: #fff;
}
div.flash > div.warning {
background-color: var(--warning);
background-image: url("icons/warning.svg");
}
/*
------------------------------------------------------------------------------
Section headings
------------------------------------------------------------------------------
*/
section > details {
margin: 1rem auto;
}
div.content > section:first-child > details {
margin-top: 0;
}
section > details summary {
background: var(--secondary);
border-radius: var(--border-radius);
padding: 0.5rem 1rem;
color: #fff;
font-size: 1rem;
}
section > details ul {
background-color: lightcyan;
border: 2px solid var(--secondary);
padding: 1rem 1rem 1rem 2rem;
margin-top: 1rem;
border-radius: 0.5rem;
}
@media (max-width: 820px) {
section > details summary {
font-size: 0.9rem;
}
}
/*
------------------------------------------------------------------------------
Index > Section: Current RaSCSI configuration
------------------------------------------------------------------------------
*/
body:not(.logged-in) section:not(#current-config, #manual) {
display: none;
}
body:not(.logged-in) section#current-config form#config-actions,
body:not(.logged-in) section#current-config form#config-save {
display: none;
}
body:not(.logged-in) section#current-config table#attached-devices th.actions,
body:not(.logged-in) section#current-config table#attached-devices td.actions,
body:not(.logged-in) section#current-config table#attached-devices form {
display: none;
}
body:not(.logged-in) section#current-config form#detach-all-devices {
display: none;
}
section#current-config form#config-actions select,
section#current-config form#config-save input[type="text"] {
max-width: 10rem;
}
table#attached-devices th.id,
table#attached-devices td.id,
table#attached-devices th.unit,
table#attached-devices td.unit {
text-align: center;
}
table#attached-devices th.actions,
table#attached-devices td.actions {
text-align: center;
}
table#attached-devices td.parameters form {
display: flex;
}
table#attached-devices td.parameters form label {
display: none;
}
table#attached-devices td.parameters form select {
width: 100%;
flex-grow: 1;
margin-right: 0.5rem;
}
table#attached-devices span.filename {
word-break: break-all;
}
table#attached-devices tr.reserved td {
background-color: #ffe9e9;
}
@media (max-width: 820px) {
table#attached-devices th.product,
table#attached-devices td.product {
display: none;
}
}
@media (max-width: 625px) {
table#attached-devices td.parameters form {
display: block;
max-width: none;
text-align: left;
}
table#attached-devices td.parameters form select {
margin-right: 0;
}
}
@media (min-width: 821px) {
section#current-config form#config-actions {
float: left;
height: 2.75rem;
}
section#current-config form#config-save {
float: right;
height: 2.75rem;
}
section#current-config form#config-save input[type="text"] {
width: 10rem;
}
table#attached-devices tr.device-assigned td.name,
table#attached-devices tr.reserved td.name {
background-image: url("icons/device-other.svg");
background-repeat: no-repeat;
background-position: 1rem center;
background-size: var(--icon-size);
padding-left: 3rem;
}
table#attached-devices tr.reserved td.name {
background-image: url("icons/device-reserved.svg");
}
table#attached-devices tr.device-sccd td.name,
table#attached-devices tr.device-scmo td.name {
background-image: url("icons/device-optical.svg");
}
table#attached-devices tr.device-scdp td.name {
background-image: url("icons/device-network.svg");
}
table#attached-devices tr.device-schd td.name {
background-image: url("icons/device-hard-drive.svg");
}
table#attached-devices tr.device-scrm td.name {
background-image: url("icons/device-removable.svg");
}
table#attached-devices tr.device-sclp td.name {
background-image: url("icons/device-printer.svg");
}
}
/*
------------------------------------------------------------------------------
Index > Section:Image/file management
------------------------------------------------------------------------------
*/
section#files table#images td:first-child {
word-break: break-all;
width: 25%;
}
section#files table#images th:last-child,
section#files table#images td:last-child {
text-align: right;
}
section#files table#images tr.directory-empty td {
text-align: center;
}
section#files p {
margin-top: 1rem;
}
@media (max-width: 820px) {
section#files table#images tr th:nth-child(2),
section#files table#images tr td:nth-child(2) {
display: none;
}
section#files table#images form.file-attach {
width: 100%;
margin-bottom: 0.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px dotted #ccc;
}
}
@media (min-width: 821px) {
section#files table#images form.file-copy input[type="submit"],
section#files table#images form.file-rename input[type="submit"],
section#files table#images form.file-delete input[type="submit"],
section#files table#images form.file-info input[type="submit"] {
background-repeat: no-repeat;
background-position: center;
background-size: 1rem;
text-indent: -1000px;
width: 2.5rem;
}
section#files table#images form.file-attach input[type="submit"],
section#attach-devices form.device-attach input[type="submit"] {
background: #efefef url("icons/file-device-attach.svg") no-repeat 0.5rem center;
background-size: 1rem;
padding-left: 2rem;
}
section#files table#images form.file-copy input[type="submit"] {
background-image: url("icons/file-copy.svg");
}
section#files table#images form.file-rename input[type="submit"] {
background-image: url("icons/file-rename.svg");
}
section#files table#images form.file-delete input[type="submit"] {
background-image: url("icons/file-delete.svg");
}
section#files table#images form.file-info input[type="submit"] {
background-image: url("icons/file-info.svg");
}
section#files table#images form.file-extract input[type="submit"] {
background: #efefef url("icons/file-extract.svg") no-repeat 0.5rem center;
background-size: 1rem;
padding-left: 2rem;
}
}
/*
------------------------------------------------------------------------------
Index > Section: Attach peripheral devices
------------------------------------------------------------------------------
*/
section#attach-devices table th:last-child,
section#attach-devices table td:last-child {
text-align: right;
}
section#attach-devices form {
display: block;
}
@media (max-width: 820px) {
section#attach-devices table tr th:nth-child(2),
section#attach-devices table tr td:nth-child(2) {
display: none;
}
section#attach-devices form label {
display: none;
}
section#attach-devices form select {
max-width: 200px;
}
}
/*
------------------------------------------------------------------------------
Index > Section: Create image
------------------------------------------------------------------------------
*/
section#create-image > p a {
display: block;
margin-top: 1rem;
}
/*
------------------------------------------------------------------------------
Index > Section: Logging
------------------------------------------------------------------------------
*/
section#logging div:first-of-type {
margin-bottom: 0.5rem;
}
/*
------------------------------------------------------------------------------
Index > Section: System
------------------------------------------------------------------------------
*/
@media (min-width: 821px) {
section#system input[type="submit"] {
background: var(--danger);
border-color: var(--danger);
color: #fff;
}
}
/*
------------------------------------------------------------------------------
Index > Section: Manual
------------------------------------------------------------------------------
*/
section#manual {
margin: 2rem 0 1rem;
}
section#manual a {
margin: auto;
display: block;
padding: 0 0 0 2rem;
background: url("icons/manual.svg") no-repeat left center;
font-weight: bold;
}
section#manual a p {
margin: 0;
}
/*
------------------------------------------------------------------------------
Drives page
------------------------------------------------------------------------------
*/
body.page-drives div.content h2:first-child {
margin-top: 0;
}
body.page-drives div.content h2 {
margin: 2rem 0 1rem;
}
body.page-drives div.content p:first-of-type {
background: lightcyan;
border: 2px solid darkcyan;
padding: 1rem;
border-radius: 0.5rem;
}
body.page-drives div.content p:nth-of-type(3) {
margin-top: 1rem;
}
body.page-drives div.content p.home {
font-weight: bold;
}
/*
------------------------------------------------------------------------------
Disk info page
------------------------------------------------------------------------------
*/
body.page-diskinfo div.content p.home {
font-weight: bold;
}
/*
------------------------------------------------------------------------------
Device info page
------------------------------------------------------------------------------
*/
body.page-deviceinfo div.content table th {
background: #efefef;
color: var(--text-color);
border-color: #ccc;
width: 25%;
}
body.page-deviceinfo div.content p.home {
font-weight: bold;
}
/*
------------------------------------------------------------------------------
Logs page
------------------------------------------------------------------------------
*/
body.page-logs div.content p.home {
font-weight: bold;
}
/*
------------------------------------------------------------------------------
Manual page
------------------------------------------------------------------------------
*/
body.page-manpage div#manpage-content {
font-family: monospace;
font-size: 0.9rem;
}
body.page-manpage div#manpage-content h2 {
margin: 2rem 0 0.5rem;
}
body.page-manpage div.content p.home {
margin-top: 2rem;
font-weight: bold;
}

View File

@ -22,16 +22,16 @@
<meta name="msapplication-TileImage" content="/pwa/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename=current_theme_stylesheet) }}">
<script type="application/javascript">
var processNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
document.getElementById("flash").innerHTML = "<div class=\"info\"><div>" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div></div>";
window.scrollTo(0,0);
}
var shutdownNotify = function(Notification) {
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
document.getElementById("flash").innerHTML = "<div class=\"warning\"><div>" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div></div>";
window.scrollTo(0,0);
}
</script>
@ -39,52 +39,77 @@
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
</head>
<body>
<body class="{{ body_class }}{% if env["logged_in"] %} logged-in{% endif %}">
<div class="header">
{% if env["auth_active"] %}
{% if env["username"] %}
<div align="center" class="logged_in">
{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} - <a href="/logout">{{ _("Log Out") }}</a>
</div>
{% if env["logged_in"] %}
<div align="center" class="login-status logged-in">
<span class="logged-in-as-text">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }}</span>
<span class="separator">-</span>
<a href="/logout">{{ _("Log Out") }}</a>
</div>
{% else %}
<div align="center" class="login-status logged-out">
<form method="POST" action="/login">
<div class="login-form-title">{{ _("Log In to Use Web Interface") }}</div>
<span>
<label for="username">{{ _("Username") }}</label>
<input type="text" name="username" id="username">
</span>
<span>
<label for="password">{{ _("Password") }}</label>
<input type="password" name="password" id="password">
</span>
<input type="submit" value="Login">
</form>
</div>
{% endif %}
{% else %}
<div align="center" class="logged_out">
<form method="POST" action="/login">
<div>{{ _("Log In to Use Web Interface") }}</div>
<label for="username">{{ _("Username") }}</label>
<input type="text" name="username" id="username">