Compare commits

..

3 Commits

20 changed files with 158 additions and 190 deletions

1
.gitignore vendored
View File

@@ -364,3 +364,4 @@ Temporary Items
.apdisk .apdisk
.kateproject* .kateproject*
.*.kate-swp

Binary file not shown.

View File

@@ -141,9 +141,19 @@ def render_image(request, path_text, full_path, special=None):
except Exception: except Exception:
dir_text = "" dir_text = ""
# For special galleries hide the Back control but still present Home # Back (directory) and Home (root) links and thumbnails
# For special galleries we still want to show a Back link that points
# to the special gallery root — populate `back_thumb` with the
# thumbnail of the first image in that special gallery so the UI can
# display a representative image. For non-special views keep the
# existing behavior (thumbnail for parent directory).
if special is not None: if special is not None:
back_url = None back_url = None
# images_sorted is already the special list in the requested order
try:
first_image = images_sorted[0] if len(images_sorted) > 0 else None
back_thumb = thumb_for(first_image)
except Exception:
back_thumb = None back_thumb = None
else: else:
back_url = gallery_url( back_url = gallery_url(

View File

@@ -1,128 +0,0 @@
/****************************************************************************
* Specific elements. *
****************************************************************************/
html {
height:100%;
}
body {
height: 100%;
margin: 0px;
}
#id_username {
width: 100%;
}
#id_password {
width: 100%;
}
a:link {
color: #ffff00;
}
a:visited {
color: #009CD9;
}
/****************************************************************************
* Containers. *
****************************************************************************/
.centered-container {
width: 100%;
text-align: center;
margin-bottom: 0.5em;
}
.background {
background-color: darkslategray;
color: lightgray;
}
.image-container {
max-width: 900px;
max-height: 600px;
}
.fc {
width: fit-content;
}
.h90 {
height: 90%;
}
.full-width {
width: 100%;
}
.fixed-width {
width: 300px;
}
/****************************************************************************
* Content. *
****************************************************************************/
.image {
max-width: 100%;
max-height: 600px;
width: auto;
}
.mauto {
margin: auto;
}
.mauto-top {
margin: 200px auto;
}
.navigation-icon {
width: 100px;
}
.small-nav-icon {
width: 3em;
}
.mb-2 {
margin-bottom: 2em;
}
.mr-2 {
margin-right: 2em;
}
.ml-4 {
margin-left: 4em;
}
.ml-2 {
margin-left: 2em;
}
.clear-btn {
border: none;
background-color: #00000000;
}
.search-box {
height: 2.5em;
}
.float-right {
float: right;
}
/****************************************************************************
* Grid. *
****************************************************************************/
.column {
text-align: center;
border: black 1px solid;
}

View File

@@ -378,7 +378,16 @@ body.theme-dark .small.text-muted {
background: transparent; background: transparent;
} }
@media (max-width: 991.98px) { .login-form {
background: var(--panel-strong);
border-radius: 0 1rem 1rem 0;
}
.card {
background: var(--bg);
}
media (max-width: 991.98px) {
.app-shell { .app-shell {
padding: 0px; padding: 0px;
gap: 12px; gap: 12px;
@@ -405,6 +414,12 @@ body.theme-dark .small.text-muted {
padding-right: 16px; padding-right: 16px;
} }
} }
@media (max-width: 767px) {
.login-form {
background: var(--panel-strong);
border-radius: 1rem 1rem 1rem 1rem;
}
}
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
/* On narrow viewports keep the fixed 128px thumbnails but allow the /* On narrow viewports keep the fixed 128px thumbnails but allow the

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

BIN
viewer/static/imgs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -206,6 +206,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% if not is_special %}
<div class="dropdown ms-auto"> <div class="dropdown ms-auto">
<button class="btn btn-sm btn-plain" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="{{ sort_label }}" aria-label="Sort options"> <button class="btn btn-sm btn-plain" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="{{ sort_label }}" aria-label="Sort options">
<i class="fa-solid fa-arrow-down-short-wide"></i> <i class="fa-solid fa-arrow-down-short-wide"></i>
@@ -218,8 +219,10 @@
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
{% endif %}
</div> </div>
{% if not is_special %}
<noscript> <noscript>
<div class="pt-2"> <div class="pt-2">
{% for option in sort_options %} {% for option in sort_options %}
@@ -227,6 +230,7 @@
{% endfor %} {% endfor %}
</div> </div>
</noscript> </noscript>
{% endif %}
<section class="gallery-scroll flex-grow-1"> <section class="gallery-scroll flex-grow-1">
<div class="gallery-grid"> <div class="gallery-grid">

View File

@@ -205,13 +205,14 @@
</div> </div>
</div> </div>
<div class="offcanvas-body pt-0 d-flex flex-column"> <div class="offcanvas-body pt-0 d-flex flex-column">
<a href="#" class="sidebar-link">Favorites</a> <a href="/gallery/favorites/" class="sidebar-link {% if is_special and breadcrumbs.0.label == 'Favorites' %}active-sort{% endif %}">Favorites</a>
<a href="#" class="sidebar-link">Most visited</a> <a href="/gallery/most-visited/" class="sidebar-link {% if is_special and breadcrumbs.0.label == 'Most Visited' %}active-sort{% endif %}">Most visited</a>
<a href="#" class="sidebar-link">Recently visited</a> <a href="/gallery/recent/" class="sidebar-link {% if is_special and breadcrumbs.0.label == 'Recent' %}active-sort{% endif %}">Recently visited</a>
<hr> <hr>
<div class="sidebar-scroll flex-grow-1"> <div class="sidebar-scroll flex-grow-1">
{% if not is_special or is_special and 'favorites' in breadcrumbs.0.path %}
{% if prev_url %} {% if prev_url %}
<a href="{{ prev_url }}" class="subdir-item"> <a href="{{ prev_url }}" class="subdir-item">
{% if prev_thumb %} {% if prev_thumb %}
@@ -233,6 +234,7 @@
<span>Next</span> <span>Next</span>
</a> </a>
{% endif %} {% endif %}
{% endif %}
{% if is_special %} {% if is_special %}
<a href="{{ breadcrumbs.0.path }}" class="subdir-item"> <a href="{{ breadcrumbs.0.path }}" class="subdir-item">

View File

@@ -1,43 +1,74 @@
{% load static %} {% load static form_tags %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta name="viewport" content="width=device-width, height=device-height" /> <meta charset="utf-8">
<title> <meta name="viewport" content="width=device-width, initial-scale=1">
NibasaViewer login <title>NibasaViewer — Login</title>
</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link href="{% static 'css/old_styles.css' %}" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
<link href="{% static 'css/styles.css' %}" rel="stylesheet">
</head> </head>
<body class="background"> <body class="theme-{{ theme|default:'dark' }}">
<div class="fixed-width mauto-top">
<form method="post" action="{% url 'login' %}" class="full-width"> <section class="vh-100">
{% csrf_token %} <div class="container py-5 h-100">
<table class="full-width"> <div class="row d-flex justify-content-center align-items-center h-100">
<tr> <div class="col col-xl-10">
<td> <div class="card shadow" style="border-radius: 1rem;">
{{ form.username.label_tag }} <div class="row g-0">
</td> <div class="col-md-6 col-lg-5 d-none d-md-block">
<td> <img src="{% static 'imgs/login.jpg' %}"
{{ form.username }} alt="login image" class="img-fluid" style="height:100%; object-fit:cover; border-radius: 1rem 0 0 1rem;" />
</td>
</tr>
<tr>
<td>
{{ form.password.label_tag }}
</td>
<td>
{{ form.password }}
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="login" class="float-right">
<input type="hidden" name="next" value="{{ next }}">
</td>
</tr>
</table>
</form>
</div> </div>
<div class="col-md-6 col-lg-7 d-flex align-items-center">
<div class="card-body p-4 p-lg-5 text-black h-100 login-form">
<form method="post" action="{% url 'login' %}" class="w-100">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">{{ form.non_field_errors }}</div>
{% endif %}
<div class="d-flex align-items-center mb-3 pb-1">
<img src="{% static 'imgs/logo.png' %}" alt="Logo" style="width:44px; height:44px; object-fit:contain;" class="me-3">
<span class="h1 fw-bold mb-0 text-light">Nibasa Viewer</span>
</div>
<h5 class="fw-normal mb-1 mt-4 pb-3" style="letter-spacing: 1px; color:var(--muted);">Sign in</h5>
<div class="mb-4">
{% render_field form.username class="form-control form-control-lg" placeholder="User name" autocomplete="username" %}
{% if form.username.errors %}
<div class="text-danger small mt-1">{{ form.username.errors|striptags }}</div>
{% endif %}
</div>
<div class="mb-4">
{% render_field form.password class="form-control form-control-lg" placeholder="Password" autocomplete="current-password" %}
{% if form.password.errors %}
<div class="text-danger small mt-1">{{ form.password.errors|striptags }}</div>
{% endif %}
</div>
<div class="pt-1 mb-4">
<button class="btn btn-dark btn-lg btn-block w-100" type="submit">LOGIN</button>
</div>
{% if next %}
<input type="hidden" name="next" value="{{ next }}">
{% endif %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body> </body>
</html> </html>

View File

View File

@@ -0,0 +1,33 @@
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def render_field(field, **attrs):
"""Render a bound field with temporary widget attributes.
Usage in template:
{% render_field form.username class="form-control" placeholder="Email" %}
This updates the widget attrs just for the duration of rendering and
restores the original attrs afterwards to avoid side effects.
"""
try:
widget = field.field.widget
except Exception:
return ""
# preserve original attrs and update temporarily
original_attrs = widget.attrs.copy()
# Convert all attr values to strings (template passes them as strings)
for k, v in attrs.items():
widget.attrs[str(k)] = str(v)
rendered = field.as_widget()
# restore original attrs
widget.attrs = original_attrs
return mark_safe(rendered)