add(backend): "/d" -> "/dashboard"

add(frontend): "/d" -> "/dashboard"
add(frontend): sync with dorsal example
add(frontend): better accessibility styles
add(frontend): "modal" utility functions
add(frontend): auth picker instead of error screen
add(backend): pages::errors:error401
fix(frontend): markdown paste editor errors
chore: bump version (v0.13.1 -> v0.13.2)
This commit is contained in:
hkau 2024-04-29 13:03:24 -04:00
parent bd5f7cac36
commit e834f2449b
25 changed files with 428 additions and 305 deletions

View file

@ -3,7 +3,7 @@ name = "bundlrs"
authors = ["hkau"]
license = "MIT"
version = "0.13.1"
version = "0.13.2"
edition = "2021"
rust-version = "1.75"

View file

@ -61,19 +61,15 @@ struct DashboardTemplate {
body_embed: String,
}
#[get("/d/atomic")]
/// Available at "/d/atomic"
#[get("/dashboard/atomic")]
/// Available at "/dashboard/atomic"
pub async fn dashboard_request(req: HttpRequest, data: web::Data<db::AppData>) -> impl Responder {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
if token_user.is_none() {
// you must have an account to use atomic pastes
return HttpResponse::NotFound().body(
"You must have an account to use atomic pastes.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// fetch pastes
@ -103,19 +99,15 @@ You can create an account at: /d/auth/register",
);
}
#[get("/d/atomic/new")]
/// Available at "/d/atomic/new"
#[get("/dashboard/atomic/new")]
/// Available at "/dashboard/atomic/new"
pub async fn new_request(req: HttpRequest, data: web::Data<db::AppData>) -> impl Responder {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
if token_user.is_none() {
// you must have an account to use atomic pastes
return HttpResponse::NotFound().body(
"You must have an account to use atomic pastes.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// ...
@ -138,8 +130,8 @@ You can create an account at: /d/auth/register",
);
}
#[get("/d/atomic/{id:.*}")]
/// Available at "/d/atomic/{id}"
#[get("/dashboard/atomic/{id:.*}")]
/// Available at "/dashboard/atomic/{id}"
pub async fn edit_request(
req: HttpRequest,
data: web::Data<db::AppData>,
@ -150,12 +142,9 @@ pub async fn edit_request(
if token_user.is_none() {
// you must have an account to use atomic pastes
// we'll likely track bandwidth used by atomic pastes and limit it in the future
return HttpResponse::NotFound().body(
"You must have an account to use atomic pastes.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
// we'll likely track requests used by atomic pastes and limit it in the future, similar to Vibrant
// ...or just migrate to Vibrant
return super::errors::error401(req, data).await;
}
// get paste

View file

@ -3,6 +3,7 @@ use crate::db::AppData;
use super::base;
use actix_web::{web, HttpRequest, HttpResponse};
use askama::Template;
use awc::http::StatusCode;
#[derive(Template)]
#[template(path = "general/404.html")]
@ -37,3 +38,37 @@ pub async fn error404(req: HttpRequest, data: web::Data<AppData>) -> HttpRespons
.unwrap(),
);
}
#[derive(Template)]
#[template(path = "general/401.html")]
struct Error401Template {
// required fields (super::base)
info: String,
auth_state: bool,
guppy: String,
site_name: String,
body_embed: String,
}
pub async fn error401(req: HttpRequest, data: web::Data<AppData>) -> HttpResponse {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
// ...
let base = base::get_base_values(token_user.is_some());
return HttpResponse::build(StatusCode::from_u16(401).unwrap())
.append_header(("Set-Cookie", set_cookie))
.append_header(("Content-Type", "text/html"))
.body(
Error401Template {
// required fields
info: base.info,
auth_state: base.auth_state,
guppy: base.guppy,
site_name: base.site_name,
body_embed: base.body_embed,
}
.render()
.unwrap(),
);
}

View file

@ -126,7 +126,10 @@ pub async fn home_request(
.unwrap()
.paste
.content
.replace(r"`", "\\`")
.replace("\\", "\\\\")
.replace("`", "\\`")
.replace("$", "\\$")
.replace("/", "\\/")
} else {
String::new()
}
@ -190,19 +193,15 @@ pub async fn adstxt() -> impl Responder {
.body(std::env::var("ADS_TXT").unwrap_or(String::new()));
}
#[get("/d")]
/// Available at "/d"
#[get("/dashboard")]
/// Available at "/dashboard"
pub async fn dashboard_request(req: HttpRequest, data: web::Data<AppData>) -> impl Responder {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
if token_user.is_none() {
// you must have an account to use the user dashboard
return HttpResponse::NotFound().body(
"You must have an account to use the user dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// ...
@ -228,8 +227,8 @@ You can create an account at: /d/auth/register",
);
}
#[get("/d/inbox")]
/// Available at "/d/inbox"
#[get("/dashboard/inbox")]
/// Available at "/dashboard/inbox"
pub async fn inbox_request(
req: HttpRequest,
data: web::Data<AppData>,
@ -240,11 +239,7 @@ pub async fn inbox_request(
if token_user.is_none() {
// you must have an account to use the user dashboard
return HttpResponse::NotFound().body(
"You must have an account to use the user dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// get inboxes

View file

@ -236,7 +236,7 @@ pub async fn paste_view_request(
Edit
</a>", &paste.custom_url);
let config_button = format!("<a href=\"/d/settings/paste/{}\" class=\"button round\">
let config_button = format!("<a href=\"/dashboard/settings/paste/{}\" class=\"button round\">
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-settings\"><path d=\"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>
<span class=\"device:desktop\">Config</span>
</a>", &paste.custom_url);
@ -397,8 +397,8 @@ pub async fn atomic_paste_view_request(
}
}
#[get("/d/pastes")]
/// Available at "/d/pastes"
#[get("/dashboard/pastes")]
/// Available at "/dashboard/pastes"
pub async fn dashboard_request(
req: HttpRequest,
data: web::Data<db::AppData>,
@ -410,11 +410,7 @@ pub async fn dashboard_request(
if token_user.is_none() {
// you must have an account to use atomic pastes
// we'll likely track bandwidth used by atomic pastes and limit it in the future
return HttpResponse::NotFound().body(
"You must have an account to use the paste dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// fetch pastes

View file

@ -31,8 +31,8 @@ struct UserSettingsTemplate {
body_embed: String,
}
#[get("/d/settings")]
/// Available at "/d/settings"
#[get("/dashboard/settings")]
/// Available at "/dashboard/settings"
pub async fn user_settings_request(req: HttpRequest, data: web::Data<AppData>) -> impl Responder {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
@ -56,8 +56,8 @@ pub async fn user_settings_request(req: HttpRequest, data: web::Data<AppData>) -
);
}
#[get("/d/settings/paste/{url:.*}")]
/// Available at "/d/settings/paste/{custom_url}"
#[get("/dashboard/settings/paste/{url:.*}")]
/// Available at "/dashboard/settings/paste/{custom_url}"
pub async fn paste_settings_request(
req: HttpRequest,
data: web::Data<AppData>,

View file

@ -51,19 +51,15 @@ pub struct UsersQueryProps {
pub username: Option<String>,
}
#[get("/d/staff")]
/// Available at "/d/staff"
#[get("/dashboard/staff")]
/// Available at "/dashboard/staff"
pub async fn dashboard_request(req: HttpRequest, data: web::Data<db::AppData>) -> impl Responder {
// verify auth status
let (set_cookie, _, token_user) = base::check_auth_status(req.clone(), data.clone()).await;
if token_user.is_none() {
// you must have an account to use the staff dashboard
return HttpResponse::NotFound().body(
"You must have an account to use the staff dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// validate role
@ -96,8 +92,8 @@ You can create an account at: /d/auth/register",
);
}
#[get("/d/staff/boards")]
/// Available at "/d/staff/boards"
#[get("/dashboard/staff/boards")]
/// Available at "/dashboard/staff/boards"
pub async fn staff_boards_dashboard_request(
req: HttpRequest,
data: web::Data<db::AppData>,
@ -108,11 +104,7 @@ pub async fn staff_boards_dashboard_request(
if token_user.is_none() {
// you must have an account to use the staff dashboard
return HttpResponse::NotFound().body(
"You must have an account to use the staff dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// validate role
@ -156,8 +148,8 @@ You can create an account at: /d/auth/register",
);
}
#[get("/d/staff/users")]
/// Available at "/d/staff/users"
#[get("/dashboard/staff/users")]
/// Available at "/dashboard/staff/users"
pub async fn staff_users_dashboard_request(
req: HttpRequest,
data: web::Data<db::AppData>,
@ -168,11 +160,7 @@ pub async fn staff_users_dashboard_request(
if token_user.is_none() {
// you must have an account to use the staff dashboard
return HttpResponse::NotFound().body(
"You must have an account to use the staff dashboard.
You can login at: /d/auth/login
You can create an account at: /d/auth/register",
);
return super::errors::error401(req, data).await;
}
// validate role

View file

@ -1,6 +1,10 @@
/* https://codeberg.org/hkau/fusion */
@import url("./css/fusion.css");
:root {
--roundness: var(--u-02);
}
.tab-container {
background: var(--background-surface1);
transition: background 0.15s;
@ -15,7 +19,7 @@
.tabbar button:not(.full-normal),
.tabbar .button:not(.full-normal) {
border-radius: var(--u-02) var(--u-02) 0 0;
border-radius: var(--roundness) var(--roundness) 0 0;
}
@media screen and (max-width: 900px) {
@ -60,10 +64,11 @@
--primary: hsl(0, 100%, 80%);
--primary-low: hsl(0, 100%, 76%);
--secondary: hsl(41, 100%, 62%);
--secondary-low: hsl(41, 100%, 58%);
--secondary: hsl(180, 80%, 70%);
--secondary-low: hsl(180, 70%, 66%);
--blue3: hsl(208, 98%, 40%);
--blue3a: hsla(208, 98%, 40%, 50%);
}
.dark-theme {
@ -76,10 +81,11 @@
--diff: 0%;
--blue3: hsl(205, 59%, 64%);
--blue3a: hsla(205, 59%, 64%, 50%);
}
*.round {
border-radius: var(--u-02) !important;
border-radius: var(--roundness) !important;
}
.card.less-padding {
@ -111,28 +117,60 @@ button.border,
box-shadow: 0 0 0 1px var(--background-surface2a);
}
button.bundles-primary,
.button.bundles-primary {
button:not(.no-shadow):hover,
.button:not(.no-shadow):hover {
box-shadow: inset 0 0 2px 2px var(--background-surface2a);
}
button.theme\:primary,
.button.theme\:primary {
background: var(--primary);
color: black;
}
button.bundles-primary:hover,
.button.bundles-primary:hover {
button.theme\:primary:hover,
.button.theme\:primary:hover {
background: var(--primary-low);
}
button.bundles-secondary,
.button.bundles-secondary {
button.theme\:secondary,
.button.theme\:secondary {
background: var(--secondary);
color: white;
color: black;
}
button.bundles-secondary:hover,
.button.bundles-secondary:hover {
button.theme\:secondary:hover,
.button.theme\:secondary:hover {
background: var(--secondary-low);
}
/* focus */
a[href]:not(.button):focus,
a[href]:not(.button):active {
background: black !important;
color: white !important;
}
.button.active:not(:active):not(:focus) {
box-shadow: none !important;
}
button:focus,
.button:focus,
input:focus,
textarea:focus,
select:focus,
button:active,
.button:active,
input:active,
textarea:active,
select:active {
transition: background 0.1s !important;
outline: 2px solid var(--blue3) !important;
box-shadow: 0 0 0 5px var(--blue3a) !important;
z-index: 2 !important;
}
/* input modifications */
button + input,
.button + input {
@ -148,17 +186,17 @@ input + .button {
/* details */
details {
border-radius: var(--u-02);
border-radius: var(--roundness);
}
details[open] {
border-radius: var(--u-02) var(--u-02) 0 0 !important;
border-radius: var(--roundness) var(--roundness) 0 0 !important;
}
details summary {
background: transparent;
border: none;
border-radius: var(--u-02) !important;
border-radius: var(--roundness) !important;
transition: none !important;
}
@ -182,7 +220,7 @@ details[open] summary {
background: var(--background-surface1);
box-shadow: none;
margin-bottom: 0 !important;
border-radius: var(--u-02) var(--u-02) 0 0 !important;
border-radius: var(--roundness) var(--roundness) 0 0 !important;
}
details summary + .content {
@ -213,7 +251,7 @@ select {
input.round,
textarea.round,
select.round {
border-radius: var(--u-02) !important;
border-radius: var(--roundness) !important;
}
input:focus,
@ -224,7 +262,7 @@ select:focus {
/* notes */
.mdnote {
border-radius: var(--u-02) !important;
border-radius: var(--roundness) !important;
}
.mdnote-title {
@ -238,7 +276,7 @@ select:focus {
/* chips */
.chip.mention {
border-radius: var(--u-02);
border-radius: var(--roundness);
background: var(--background-surface2a);
border: solid 1px var(--background-surface2);
color: var(--text-color);
@ -282,7 +320,7 @@ select:focus {
--active-color: var(--background-surface2a);
padding: 0 var(--u-02);
background: var(--hidden-color);
border-radius: var(--u-02);
border-radius: var(--roundness);
color: transparent;
transition: all 0.15s;
box-shadow: none;
@ -413,18 +451,6 @@ select:focus {
margin: 0 0 2rem 0;
}
@media screen and (max-width: 900px) {
#link-header {
padding: 0 var(--u-10);
}
.link-list {
width: 100%;
border-left: none;
border-right: none;
}
}
#link-header .link-header-middle {
padding: calc(var(--u-10) * 2) 0;
}
@ -440,7 +466,27 @@ select:focus {
#link-header .link-header-bottom .button.active {
background: var(--background-surface);
box-shadow: none !important;
/* box-shadow: none !important; */
}
#link-header.tall .link-header-middle {
padding: calc(var(--u-10) * 4) 0;
}
@media screen and (max-width: 900px) {
#link-header {
padding: 0 var(--u-10);
}
#link-header.tall .link-header-middle {
padding: calc(var(--u-10) * 2) 0;
}
.link-list {
width: 100%;
border-left: none;
border-right: none;
}
}
/* messages */
@ -474,27 +520,6 @@ select:focus {
user-select: none;
}
/* footer */
.__footernav {
display: flex;
gap: 0.75rem;
}
.__footernav .item {
position: relative;
margin-left: 0.5rem;
}
.__footernav .item::before {
content: "·";
position: absolute;
left: -0.75rem;
}
.__footernav .item:first-child:before {
display: none;
}
/* more highlight.js styles */
.hljs-tag,
.hljs-number {
@ -512,7 +537,7 @@ select:focus {
/* code */
pre {
padding: var(--u-08);
border-radius: var(--u-04);
border-radius: var(--roundness);
}
pre,
@ -542,7 +567,7 @@ pre code {
}
.cm-search * {
border-radius: var(--u-02) !important;
border-radius: var(--roundness) !important;
color: var(--text-color) !important;
display: inline-block !important;
}
@ -572,7 +597,7 @@ pre code {
/* avatar */
.avatar {
--size: 50px;
border-radius: var(--u-02);
border-radius: var(--roundness);
width: var(--size);
height: var(--size);
}
@ -592,3 +617,28 @@ table.stripped thead tr th {
table.stripped tbody tr td {
padding: 6px 8px;
}
table td:focus-within {
box-shadow: none;
}
/* footer */
.footernav {
display: flex;
gap: 0.75rem;
}
.footernav .item {
position: relative;
margin-left: 0.5rem;
}
.footernav .item::before {
content: "·";
position: absolute;
left: -0.75rem;
}
.footernav .item:first-child:before {
display: none;
}

View file

@ -106,7 +106,7 @@ export function paste_settings(
};
// add button
option_render = `<button class="bundles-primary round" onclick="document.getElementById('permissions-modal').showModal();">Edit Permissions</button>`;
option_render = `<button class="theme:primary round" onclick="document.getElementById('permissions-modal').showModal();">Edit Permissions</button>`;
}
// ...

View file

@ -270,5 +270,18 @@ for (const element of Array.from(
)}`;
}
// modal
for (const element of Array.from(
document.querySelectorAll("[data-dialog]")
) as HTMLAnchorElement[]) {
const dialog_element: HTMLDialogElement = document.getElementById(
element.getAttribute("data-dialog")!
) as HTMLDialogElement;
element.addEventListener("click", () => {
dialog_element.showModal();
});
}
// default export
export default {};

View file

@ -10,7 +10,7 @@
<footer class="flex justify-center align-center flex-column full" {% block footer_stuff %}{% endblock %}>
<hr class="small" style="width: 425px; max-width: 100%; margin-top: 1rem;" />
<div class="__footernav" style="padding: 0; margin: 0;">
<div class="footernav" style="padding: 0; margin: 0;">
<div class="item">
<a href="{{ info }}" class="flex align-center g-4">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
@ -64,7 +64,7 @@
</div>
{% else %}
<div class="item">
<a href="/d" class="flex align-center g-4">
<a href="/dashboard" class="flex align-center g-4">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-layout-dashboard">
@ -101,7 +101,7 @@
<div style="position: relative; width: 100%;">
<div style="position: absolute; bottom: 8px; right: 0;">
<a id="theme_button" href="javascript:window.toggle_theme()" title="Toggle Theme"
style="color: var(--text-color-faded);">
style="color: var(--text-color-faded); display: block;">
<div id="theme-icon-sun">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"

View file

@ -0,0 +1,28 @@
{% extends "../toolbar_base.html" %}
{% block title %}Auth Options{% endblock %}
{% block content %}
<div class="flex flex-column g-4">
<main class="small flex flex-column g-4 align-center">
<h3 class="no-margin">Account Required</h3>
<div class="card secondary round border flex flex-column g-4" style="width: 25rem; max-width: 100dvw;"
id="options">
<a href="{{ guppy }}/d/auth/login" class="button full round theme:primary"
data-wants-redirect="true">Login</a>
<a href="{{ guppy }}/d/auth/register" class="button full round theme:secondary"
data-wants-redirect="true">Register</a>
</div>
<div style="width: 25rem; max-width: 100dvw;">
<hr />
<div class="flex justify-center footernav">
<span class="item"><a href="/">Homepage</a></span>
<span class="item"><a href="https://code.stellular.org/stellular/bundlrs">Source Code</a></span>
</div>
</div>
</main>
</div>
{% call super() %}
{% endblock %}

View file

@ -4,18 +4,44 @@
{% block main_stuff %}style="overflow: hidden; max-height: 100%;"{% endblock %}
{% block content %}
<div id="link-header" style="display: flex;" class="flex-column bg-1">
<div id="link-header" style="display: flex;" class="flex-column bg-1 tall">
<div class="link-header-top"></div>
<div class="link-header-middle flex align-center flex-column g-4">
<h1 class="no-margin">404: Not Found</h1>
<div class="flex g-4 flex-wrap">
{% if auth_state == false %}
<a href="{{ guppy }}/d/auth/login" class="button green-cta round" data-wants-redirect="true">Login</a>
<a href="{{ guppy }}/d/auth/register" class="button green secondary round"
data-wants-redirect="true">Register</a>
<a href="{{ guppy }}/d/auth/login" class="button theme:primary round" data-wants-redirect="true">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-log-in">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" />
<polyline points="10 17 15 12 10 7" />
<line x1="15" x2="3" y1="12" y2="12" />
</svg>
Login
</a>
<a href="{{ guppy }}/d/auth/register" class="button theme:secondary round" data-wants-redirect="true">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-user-plus">
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<line x1="19" x2="19" y1="8" y2="14" />
<line x1="22" x2="16" y1="11" y2="11" />
</svg>
Register
</a>
{% else %}
<a href="/d" class="button border round">My Dashboard</a>
<a href="/dashboard" class="button theme:primary round">
My Dashboard
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-arrow-right">
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
</div>

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button">Home</a>
<a href="/d/pastes" class="button">Pastes</a>
<a href="/d/atomic" class="button active">Atomic</a>
<a href="/dashboard" class="button">Home</a>
<a href="/dashboard/pastes" class="button">Pastes</a>
<a href="/dashboard/atomic" class="button active">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>
@ -23,7 +23,7 @@
<div class="flex justify-space-between align-center">
<b>Atomic Pastes</b>
<a class="button bundles-primary round" href="/d/atomic/new">
<a class="button theme:primary round" href="/dashboard/atomic/new">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-plus-square">
@ -37,7 +37,7 @@
<div class="card round secondary flex g-4 flex-column justify-center" id="pastes_list">
{% for p in pastes.iter() %}
<a class="button secondary round full justify-start" href="/d/atomic/{{ p.id }}">
<a class="button secondary round full justify-start no-shadow" href="/dashboard/atomic/{{ p.id }}">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-folder-archive">

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button">Home</a>
<a href="/d/pastes" class="button">Pastes</a>
<a href="/d/atomic" class="button active">Atomic</a>
<a href="/dashboard" class="button">Home</a>
<a href="/dashboard/pastes" class="button">Pastes</a>
<a href="/dashboard/atomic" class="button active">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>
@ -30,7 +30,7 @@
<hr />
<button class="bundles-primary full round">
<button class="theme:primary full round">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-plus">

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button">Home</a>
<a href="/d/pastes" class="button">Pastes</a>
<a href="/d/atomic" class="button active">Atomic</a>
<a href="/dashboard" class="button">Home</a>
<a href="/dashboard/pastes" class="button">Pastes</a>
<a href="/dashboard/atomic" class="button active">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>
@ -27,7 +27,7 @@
<form class="flex justify-center align-center g-4">
<input type="text" placeholder="/index.(html|css|js)" name="path" class="round full" minlength="4" />
<button class="round bundles-primary" style="min-width: max-content;">Open</button>
<button class="round theme:primary" style="min-width: max-content;">Open</button>
</form>
<table class="full stripped">
@ -44,7 +44,7 @@
<td><a href="?path={{ p.path }}">{{ p.path }}</a></td>
<td class="flex g-4 flex-wrap">
<a class="button secondary round" href="?path={{ p.path }}" title="Edit File">
<a class="button secondary round no-shadow" href="?path={{ p.path }}" title="Edit File">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-file-pen-line">
@ -54,7 +54,7 @@
</svg>
</a>
<button class="secondary round action:more-modal" data-suffix="{{ custom_url }}{{ p.path }}"
<button class="secondary round action:more-modal no-shadow" data-suffix="{{ custom_url }}{{ p.path }}"
title="More Options">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -85,7 +85,7 @@
<tr>
<td>View</td>
<td>
<a class="button round secondary" target="_blank" href="/{{ custom_url }}">
<a class="button round secondary no-shadow" target="_blank" href="/{{ custom_url }}">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-circle-play">
@ -100,7 +100,7 @@
<tr>
<td>Delete</td>
<td>
<button class="round secondary" id="delete">
<button class="round secondary no-shadow" id="delete">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-circle-play">

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button">Home</a>
<a href="/d/pastes" class="button active">Pastes</a>
<a href="/d/atomic" class="button">Atomic</a>
<a href="/dashboard" class="button">Home</a>
<a href="/dashboard/pastes" class="button active">Pastes</a>
<a href="/dashboard/atomic" class="button">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>
@ -23,7 +23,7 @@
<div class="flex justify-space-between align-center">
<b>Pastes</b>
<a class="button bundles-primary round" href="/">
<a class="button theme:primary round" href="/">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-plus-square">
@ -37,7 +37,7 @@
<div class="card round secondary flex g-4 flex-column justify-center" id="pastes_list">
{% for p in pastes.iter() %}
<a class="button secondary round full justify-start" href="/?editing={{ p.custom_url }}">
<a class="button secondary round full justify-start no-shadow" href="/?editing={{ p.custom_url }}">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-file-pen">

View file

@ -16,7 +16,7 @@
<hr />
<button class="bundles-primary full round">
<button class="theme:primary full round">
Continue
</button>
</form>

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d/staff" class="button active">Home</a>
<a href="/d/staff/users" class="button">Users</a>
<a href="/d/staff/boards" class="button">Boards</a>
<a href="/dashboard/staff" class="button active">Home</a>
<a href="/dashboard/staff/users" class="button">Users</a>
<a href="/dashboard/staff/boards" class="button">Boards</a>
</div>
</div>
{% endblock %}

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d/staff" class="button">Home</a>
<a href="/d/staff/users" class="button">Users</a>
<a href="/d/staff/boards" class="button active">Boards</a>
<a href="/dashboard/staff" class="button">Home</a>
<a href="/dashboard/staff/users" class="button">Users</a>
<a href="/dashboard/staff/boards" class="button active">Boards</a>
</div>
</div>
@ -24,7 +24,7 @@
<div class="card round secondary flex g-4 flex-column justify-center" id="boards_list">
{% for p in posts.iter() %}
{% let post = crate::db::derserialize_post(p.content) %}
<a class="button secondary round full justify-start" href="{{ puffer }}/{{ post.board }}/posts/{{ p.id }}">
<a class="button secondary round full justify-start no-shadow" href="{{ puffer }}/{{ post.board }}/posts/{{ p.id }}">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-message-square-text">

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d/staff" class="button">Home</a>
<a href="/d/staff/users" class="button active">Users</a>
<a href="/d/staff/boards" class="button">Boards</a>
<a href="/dashboard/staff" class="button">Home</a>
<a href="/dashboard/staff/users" class="button active">Users</a>
<a href="/dashboard/staff/boards" class="button">Boards</a>
</div>
</div>
@ -29,7 +29,7 @@
<input type="text" name="username" id="username" placeholder="Username" class="round" value="{{ username }}"
maxlength="250" style="width: calc(100% - 50px);" />
<button class="round bundles-primary" style="width: 50px;">Go</button>
<button class="round theme:primary" style="width: 50px;">Go</button>
</form>
</div>
@ -43,7 +43,7 @@
<div class="flex full g-4 flex-wrap justify-space-between align-center">
<h6 class="no-margin">Banhammer</h6>
<button class="round bundles-primary" id="hammer-time"
<button class="round theme:primary" id="hammer-time"
data-endpoint="/api/auth/users/{{ user.user.username }}/ban">Ban User</button>
</div>
</div>

View file

@ -83,7 +83,7 @@
login
</a>
{% else %}
<a href="/d" class="button full round border justify-start">
<a href="/dashboard" class="button full round border justify-start">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-layout-dashboard">

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button active">Home</a>
<a href="/d/pastes" class="button">Pastes</a>
<a href="/d/atomic" class="button">Atomic</a>
<a href="/dashboard" class="button active">Home</a>
<a href="/dashboard/pastes" class="button">Pastes</a>
<a href="/dashboard/atomic" class="button">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>
@ -32,7 +32,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>Pastes</b>
<a class="button bundles-primary round" href="/d/pastes">
<a class="button theme:primary round" href="/dashboard/pastes">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -46,7 +46,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>Atomic Pastes</b>
<a class="button bundles-primary round" href="/d/atomic">
<a class="button theme:primary round" href="/dashboard/atomic">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -62,7 +62,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>Site Settings</b>
<a class="button bundles-primary round" href="/d/settings">
<a class="button theme:primary round" href="/dashboard/settings">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -76,7 +76,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>My Boards</b>
<a class="button bundles-primary round" href="{{ puffer }}/d">
<a class="button theme:primary round" href="{{ puffer }}/d">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -90,7 +90,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>My Inboxes</b>
<a class="button bundles-primary round" href="/d/inbox">
<a class="button theme:primary round" href="/dashboard/inbox">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -104,7 +104,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>My Profile</b>
<a class="button bundles-primary round" href="{{ guppy }}/{{ user.username }}">
<a class="button theme:primary round" href="{{ guppy }}/{{ user.username }}">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
@ -118,7 +118,7 @@
<div class="card secondary round flex justify-space-between align-center g-4">
<b>Account Settings</b>
<a class="button bundles-primary round" href="{{ guppy }}/{{ user.username }}/settings">
<a class="button theme:primary round" href="{{ guppy }}/{{ user.username }}/settings">
Go
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"

View file

@ -1,131 +1,134 @@
{% extends "../footer_base.html" %}
{% block title %}{{ site_name }}{% endblock %}
{% block main_stuff %}style="overflow: hidden; max-height: 100%; margin-bottom: 0;"{% endblock %}
{% block main_stuff %}style="overflow: hidden; max-height: 100%; height: 100%; margin-bottom: 0; margin-top: 0;
padding-top: var(--u-04);" class="flex flex-column align-center"{% endblock %}
{% block content %}
<div class="tabbar justify-space-between full">
<div class="flex">
<button id="editor-open-tab-text">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-notebook-pen">
<path d="M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4" />
<path d="M2 6h4" />
<path d="M2 10h4" />
<path d="M2 14h4" />
<path d="M2 18h4" />
<path d="M18.4 2.6a2.17 2.17 0 0 1 3 3L16 11l-4 1 1-4Z" />
</svg>
Text
</button>
<button id="editor-open-tab-preview" class="secondary">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-paintbrush">
<path
d="M18.37 2.63 14 7l-1.59-1.59a2 2 0 0 0-2.82 0L8 7l9 9 1.59-1.59a2 2 0 0 0 0-2.82L17 10l4.37-4.37a2.12 2.12 0 1 0-3-3Z" />
<path d="M9 8c-2 3-4 3.5-7 4l8 10c2-1 6-5 6-7" />
<path d="M14.5 17.5 4.5 15" />
</svg>
Preview
</button>
</div>
</div>
<div id="-editor" class="tab-container card secondary round"
style="border-top-left-radius: 0px !important; padding: var(--u-10) !important;">
<div id="editor-tab-text" class="editor-tab -editor active" style="height: 100%;"></div>
<div id="editor-tab-preview" class="editor-tab -editor"></div>
</div>
<form class="flex flex-wrap mobile:justify-center justify-space-between g-4 align-center" id="save-changes">
{% if edit_mode == false %}
<div class="mobile:justify-center flex g-4 justify-start">
<button class="round">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-plus">
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
Publish
</button>
<a class="button round border" href="javascript:document.getElementById('more-modal').showModal();">
More
</a>
</div>
<div class="mobile:justify-center flex-wrap flex g-4 justify-start">
<input class="secondary round" type="text" placeholder="Custom URL" minlength="2" maxlength="500"
name="custom_url" id="custom_url" autocomplete="off" />
<input class="secondary round" type="text" placeholder="Edit Password" minlength="5" name="edit_password" />
</div>
<dialog id="more-modal">
<div style="width: 25rem; max-width: 100%;">
<h2 class="no-margin full text-center">More Options</h2>
<hr />
<details class="full round">
<summary>Group Settings</summary>
<div class="card secondary">
<input class="full secondary round" type="text" placeholder="Group Name" minlength="2"
maxlength="500" name="group_name" id="group_name" autocomplete="off" />
</div>
</details>
<hr />
<div class="full flex justify-right">
<a class="button round red" href="javascript:document.getElementById('more-modal').close();">
Close
</a>
</div>
</div>
</dialog>
{% else %}
<div class="mobile:justify-center flex g-4 justify-start full mobile:flex-column">
{% if password_not_needed == false %}
<input class="secondary round full" type="text" placeholder="Edit Password" minlength="5"
name="edit_password" />
{% else %}
<input class="secondary round full" type="text" placeholder="Passwordless Enabled" minlength="5"
name="edit_password" disabled />
{% endif %}
<input class="secondary round full" type="text" placeholder="New Edit Password (optional)" minlength="5"
name="new_edit_password" />
<input class="secondary round full" type="text" placeholder="New Custom URL (optional)" minlength="2"
maxlength="500" name="new_custom_url" id="new_custom_url" autocomplete="off" />
</div>
<div class="flex g-4 justify-space-between full">
<div class="flex g-4 justify-start">
<button class="green round">
Save
<div class="flex flex-column full">
<div class="tabbar justify-space-between full">
<div class="flex">
<button id="editor-open-tab-text">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-notebook-pen">
<path d="M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4" />
<path d="M2 6h4" />
<path d="M2 10h4" />
<path d="M2 14h4" />
<path d="M2 18h4" />
<path d="M18.4 2.6a2.17 2.17 0 0 1 3 3L16 11l-4 1 1-4Z" />
</svg>
Text
</button>
<a href="/{{ editing }}" class="button round">Cancel</a>
<button id="editor-open-tab-preview" class="secondary">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-paintbrush">
<path
d="M18.37 2.63 14 7l-1.59-1.59a2 2 0 0 0-2.82 0L8 7l9 9 1.59-1.59a2 2 0 0 0 0-2.82L17 10l4.37-4.37a2.12 2.12 0 1 0-3-3Z" />
<path d="M9 8c-2 3-4 3.5-7 4l8 10c2-1 6-5 6-7" />
<path d="M14.5 17.5 4.5 15" />
</svg>
Preview
</button>
</div>
</div>
<div id="-editor" class="tab-container card secondary round"
style="border-top-left-radius: 0px !important; padding: var(--u-10) !important;">
<div id="editor-tab-text" class="editor-tab -editor active" style="height: 100%;"></div>
<div id="editor-tab-preview" class="editor-tab -editor"></div>
</div>
<form class="flex flex-wrap mobile:justify-center justify-space-between g-4 align-center" id="save-changes">
{% if edit_mode == false %}
<div class="mobile:justify-center flex g-4 justify-start">
<button class="round">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-plus">
<path d="M5 12h14" />
<path d="M12 5v14" />
</svg>
Publish
</button>
<a class="button round border" href="javascript:document.getElementById('more-modal').showModal();">
More
</a>
</div>
<a href="javascript:" id="delete-btn" class="button round red">Delete</a>
</div>
{% endif %}
</form>
<div class="mobile:justify-center flex-wrap flex g-4 justify-start">
<input class="secondary round" type="text" placeholder="Custom URL" minlength="2" maxlength="500"
name="custom_url" id="custom_url" autocomplete="off" />
<script type="text/plain" style="display: none;" id="edit-mode">{{ edit_mode }}</script>
<script type="text/plain" style="display: none;" id="editing">{{ editing }}</script>
<input class="secondary round" type="text" placeholder="Edit Password" minlength="5" name="edit_password" />
</div>
<script type="module">
import CreateEditor from "/static/js/MarkdownEditor.js";
CreateEditor("editor-tab-text", `{{ starting_content|safe }}`);
</script>
<dialog id="more-modal">
<div style="width: 25rem; max-width: 100%;">
<h2 class="no-margin full text-center">More Options</h2>
<hr />
<details class="full round">
<summary>Group Settings</summary>
<div class="card secondary">
<input class="full secondary round" type="text" placeholder="Group Name" minlength="2"
maxlength="500" name="group_name" id="group_name" autocomplete="off" />
</div>
</details>
<hr />
<div class="full flex justify-right">
<a class="button round red" href="javascript:document.getElementById('more-modal').close();">
Close
</a>
</div>
</div>
</dialog>
{% else %}
<div class="mobile:justify-center flex g-4 justify-start full mobile:flex-column">
{% if password_not_needed == false %}
<input class="secondary round full" type="text" placeholder="Edit Password" minlength="5"
name="edit_password" />
{% else %}
<input class="secondary round full" type="text" placeholder="Passwordless Enabled" minlength="5"
name="edit_password" disabled />
{% endif %}
<input class="secondary round full" type="text" placeholder="New Edit Password (optional)" minlength="5"
name="new_edit_password" />
<input class="secondary round full" type="text" placeholder="New Custom URL (optional)" minlength="2"
maxlength="500" name="new_custom_url" id="new_custom_url" autocomplete="off" />
</div>
<div class="flex g-4 justify-space-between full">
<div class="flex g-4 justify-start">
<button class="green round">
Save
</button>
<a href="/{{ editing }}" class="button round">Cancel</a>
</div>
<a href="javascript:" id="delete-btn" class="button round red">Delete</a>
</div>
{% endif %}
</form>
<script type="text/plain" style="display: none;" id="edit-mode">{{ edit_mode }}</script>
<script type="text/plain" style="display: none;" id="editing">{{ editing }}</script>
<script type="module">
import CreateEditor from "/static/js/MarkdownEditor.js";
CreateEditor("editor-tab-text", `{{ starting_content|safe }}`);
</script>
</div>
{% call super() %}
{% endblock %}

View file

@ -12,9 +12,9 @@
</div>
<div class="link-header-bottom">
<a href="/d" class="button">Home</a>
<a href="/d/pastes" class="button">Pastes</a>
<a href="/d/atomic" class="button">Atomic</a>
<a href="/dashboard" class="button">Home</a>
<a href="/dashboard/pastes" class="button">Pastes</a>
<a href="/dashboard/atomic" class="button">Atomic</a>
<a href="{{ puffer }}/d" class="button">Boards</a>
</div>
</div>