This commit is contained in:
retoor 2025-12-05 19:17:09 +01:00
parent f8d650567a
commit 0ad8f6cb31
28 changed files with 978 additions and 470 deletions

View File

@ -4,4 +4,4 @@ from snek.system.model import BaseModel, ModelField
class UserPropertyModel(BaseModel):
user_uid = ModelField(name="user_uid", required=True, kind=str)
name = ModelField(name="name", required=True, kind=str)
value = ModelField(name="path", required=True, kind=str)
value = ModelField(name="value", required=True, kind=str)

View File

@ -15,6 +15,7 @@ class UserPropertyService(BaseService):
},
["user_uid", "name"],
)
self.mapper.db.commit()
async def get(self, user_uid, name):
try:

View File

@ -66,11 +66,13 @@ header .logo {
}
header nav a {
color: #aaa;
color: #888;
text-decoration: none;
margin-left: 15px;
font-size: 1em;
transition: color 0.3s;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s ease;
}
.no-select {
@ -82,6 +84,7 @@ header nav a {
header nav a:hover {
color: #fff;
background-color: rgba(255, 255, 255, 0.05);
}
a {
@ -436,19 +439,26 @@ a {
}
.sidebar ul li a {
color: #ccc;
color: #888;
text-decoration: none;
font-family: 'Courier New', monospace;
font-size: 0.9em;
transition: color 0.3s;
display: block;
padding: 4px 8px;
padding: 6px 10px;
border-radius: 4px;
transition: background-color 0.2s, color 0.2s;
border-left: 2px solid transparent;
transition: all 0.2s ease;
}
.sidebar ul li a:hover {
color: #fff;
background-color: rgba(255, 255, 255, 0.05);
color: #e6e6e6;
background-color: #1a1a1a;
border-left-color: #444;
}
.sidebar ul li a.active {
color: #f05a28;
border-left-color: #f05a28;
}
@keyframes glow {
@ -563,12 +573,22 @@ dialog .dialog-actions {
}
dialog .dialog-button {
padding: 8px 16px;
font-size: 0.95rem;
border-radius: 8px;
border: none;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
border-radius: 4px;
border: 1px solid #333;
background: #1a1a1a;
color: #e6e6e6;
cursor: pointer;
transition: background 0.2s ease;
transition: all 0.2s ease;
}
dialog .dialog-button:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
}
@ -594,38 +614,40 @@ dialog .dialog-button {
dialog .dialog-button.primary {
background-color: #f05a28;
color: white;
border-color: #f05a28;
color: #fff;
}
dialog .dialog-button.primary:hover {
background-color: #f05a28;
background-color: #e04924;
border-color: #e04924;
}
dialog .dialog-button.secondary {
background-color: #f0a328;
color: #eee;
background-color: #2a2a2a;
border-color: #444;
color: #e6e6e6;
}
dialog .dialog-button.secondary:hover {
background-color: #f0b84c;
background-color: #3a3a3a;
border-color: #555;
}
dialog .dialog-button.primary:disabled,
dialog .dialog-button.primary[aria-disabled="true"] {
/* slightly darker + lower saturation of the live colour */
background-color: #70321e; /* muted burnt orange */
color: #bfbfbf; /* light grey text */
opacity: .55; /* unified fade */
background-color: #0a0a0a;
border-color: #222;
color: #555;
opacity: .55;
cursor: not-allowed;
pointer-events: none;
}
/* ---------- SECONDARY (yellow) ---------- */
dialog .dialog-button.secondary:disabled,
dialog .dialog-button.secondary[aria-disabled="true"] {
background-color: #6c5619; /* muted mustard */
color: #bfbfbf;
background-color: #0a0a0a;
border-color: #222;
color: #555;
opacity: .55;
cursor: not-allowed;
pointer-events: none;

View File

@ -0,0 +1,93 @@
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
text-decoration: none;
border: 1px solid #333;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
background: #1a1a1a;
color: #e6e6e6;
}
.btn:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
}
.btn:active {
background: #111;
transform: translateY(1px);
}
.btn:disabled {
background: #0a0a0a;
color: #555;
cursor: not-allowed;
border-color: #222;
}
.btn-primary {
background: #f05a28;
border-color: #f05a28;
color: #fff;
}
.btn-primary:hover {
background: #e04924;
border-color: #e04924;
}
.btn-secondary {
background: #2a2a2a;
border-color: #444;
}
.btn-secondary:hover {
background: #3a3a3a;
border-color: #555;
}
.btn-danger {
background: #1a1a1a;
border-color: #8b0000;
color: #ff6b6b;
}
.btn-danger:hover {
background: #2a1515;
border-color: #b00;
}
.btn-success {
background: #1a1a1a;
border-color: #006400;
color: #6bff6b;
}
.btn-success:hover {
background: #152a15;
border-color: #0b0;
}
.btn-sm {
padding: 6px 12px;
font-size: 12px;
}
.btn-lg {
padding: 14px 28px;
font-size: 16px;
}
.btn-block {
display: flex;
width: 100%;
}

View File

@ -28,21 +28,30 @@ class FancyButton extends HTMLElement {
button {
width: var(--width);
min-width: ${size};
padding: 10px;
background-color: #f05a28;
border: none;
border-radius: 5px;
color: white;
font-size: 1em;
font-weight: bold;
padding: 10px 20px;
background-color: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
border: 1px solid #f05a28;
transition: all 0.2s ease;
}
button:hover {
color: #EFEFEF;
background-color: #2a2a2a;
border-color: #444;
color: #fff;
}
button.primary {
background-color: #f05a28;
border-color: #f05a28;
color: #fff;
}
button.primary:hover {
background-color: #e04924;
border: 1px solid #efefef;
border-color: #e04924;
}
`;

View File

@ -2,11 +2,11 @@
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
body {
font-family: Arial, sans-serif;
background-color: #1a1a1a;
body {
font-family: 'Courier New', monospace;
background-color: #0a0a0a;
color: #e6e6e6;
line-height: 1.5;
display: flex;
@ -15,61 +15,72 @@
align-items: center;
height: 100vh;
width: 100vw;
}
}
generic-form {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: #000000;
}
.generic-form-container {
background-color: #0f0f0f;
border-radius: 10px;
padding: 30px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
text-align: center;
}
.generic-form-container h1 {
font-size: 2em;
color: #f05a28;
margin-bottom: 20px;
}
input {
border: 10px solid #000000;
}
.generic-form-container generic-field {
width: 100%;
padding: 10px;
padding: 10px 12px;
margin: 10px 0;
border: 1px solid #333;
border-radius: 5px;
background-color: #1a1a1a;
border-radius: 4px;
background-color: #0f0f0f;
color: #e6e6e6;
font-size: 1em;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.generic-form-container button {
width: 100%;
padding: 10px;
background-color: #f05a28;
border: none;
border-radius: 5px;
color: white;
font-size: 1em;
font-weight: bold;
padding: 10px 20px;
background-color: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
transition: all 0.2s ease;
}
.generic-form-container button:hover {
background-color: #2a2a2a;
border-color: #444;
color: #fff;
}
.generic-form-container button[type="submit"],
.generic-form-container button.primary {
background-color: #f05a28;
border-color: #f05a28;
color: #fff;
}
.generic-form-container button[type="submit"]:hover,
.generic-form-container button.primary:hover {
background-color: #e04924;
border-color: #e04924;
}
.generic-form-container a {
@ -85,15 +96,13 @@ input {
color: #e04924;
}
.error {
color: #d8000c;
font-size: 0.9em;
margin-top: 5px;
border-color: #8b0000;
box-shadow: 0 0 0 2px rgba(139, 0, 0, 0.2);
}
@media (max-width: 600px) {
.generic-form-container {
width: 90%;
}
}

View File

@ -76,19 +76,24 @@ class GenericField extends HTMLElement {
input {
width: 90%;
padding: 10px;
padding: 10px 12px;
margin: 10px 0;
border: 1px solid #333;
border-radius: 5px;
background-color: #1a1a1a;
border-radius: 4px;
background-color: #0f0f0f;
color: #e6e6e6;
font-size: 1em;
font-family: 'Courier New', monospace;
font-size: 14px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
&:focus {
outline: 2px solid #f05a28 !important;
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
&::placeholder {
color: #555;
transition: opacity 0.3s;
}
@ -99,24 +104,38 @@ class GenericField extends HTMLElement {
button {
width: 50%;
padding: 10px;
background-color: #f05a28;
border: none;
padding: 10px 20px;
background-color: #1a1a1a;
border: 1px solid #333;
float: right;
margin-top: 10px;
margin-left: 10px;
margin-right: 10px;
border-radius: 5px;
color: white;
font-size: 1em;
font-weight: bold;
border-radius: 4px;
color: #e6e6e6;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
transition: all 0.2s ease;
clear: both;
}
button:hover {
background-color: #2a2a2a;
border-color: #444;
color: #fff;
}
button[value="submit"], button.primary {
background-color: #f05a28;
border-color: #f05a28;
color: #fff;
}
button[value="submit"]:hover, button.primary:hover {
background-color: #e04924;
border-color: #e04924;
}
a {
@ -133,17 +152,13 @@ class GenericField extends HTMLElement {
}
.valid {
border: 1px solid green;
color: green;
font-size: 0.9em;
margin-top: 5px;
border-color: #006400;
box-shadow: 0 0 0 2px rgba(0, 100, 0, 0.2);
}
.error {
border: 3px solid red;
color: #d8000c;
font-size: 0.9em;
margin-top: 5px;
border-color: #8b0000;
box-shadow: 0 0 0 2px rgba(139, 0, 0, 0.2);
}
@media (max-width: 500px) {

102
src/snek/static/inputs.css Normal file
View File

@ -0,0 +1,102 @@
.input {
width: 100%;
padding: 10px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
background: #0f0f0f;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input:focus {
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
.input:disabled {
background: #0a0a0a;
color: #555;
cursor: not-allowed;
}
.input::placeholder {
color: #555;
}
.input-error {
border-color: #8b0000;
box-shadow: 0 0 0 2px rgba(139, 0, 0, 0.2);
}
.input-success {
border-color: #006400;
box-shadow: 0 0 0 2px rgba(0, 100, 0, 0.2);
}
.textarea {
width: 100%;
min-height: 100px;
padding: 10px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
background: #0f0f0f;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
resize: vertical;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.textarea:focus {
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
.select {
width: 100%;
padding: 10px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
background: #0f0f0f;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
cursor: pointer;
}
.select:focus {
outline: none;
border-color: #f05a28;
}
.checkbox-wrapper {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.checkbox {
width: 18px;
height: 18px;
accent-color: #f05a28;
cursor: pointer;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
margin-bottom: 6px;
font-family: 'Courier New', monospace;
font-size: 13px;
color: #aaa;
text-transform: uppercase;
letter-spacing: 0.5px;
}

100
src/snek/static/lists.css Normal file
View File

@ -0,0 +1,100 @@
.settings-list {
display: flex;
flex-direction: column;
gap: 0;
}
.settings-list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 0;
flex-wrap: wrap;
gap: 15px;
}
.settings-list-info {
display: flex;
flex-direction: column;
gap: 5px;
flex: 1;
min-width: 200px;
}
.settings-list-title {
font-size: 1.1rem;
font-weight: 600;
color: #e6e6e6;
display: flex;
align-items: center;
gap: 10px;
}
.settings-list-title i {
color: #888;
}
.settings-list-meta {
color: #888;
font-size: 0.9em;
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.settings-list-meta code {
background: #0f0f0f;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
.settings-list-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 0.85em;
color: #888;
}
.settings-list-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.settings-topbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.settings-topbar h2 {
margin: 0;
font-size: 1.3rem;
color: #e6e6e6;
}
.settings-empty {
text-align: center;
padding: 60px 20px;
}
.settings-empty p {
color: #888;
font-size: 1.1em;
margin-bottom: 20px;
}
@media (max-width: 600px) {
.settings-list-item {
flex-direction: column;
align-items: stretch;
}
.settings-list-actions {
justify-content: flex-start;
}
}

View File

@ -1,13 +1,10 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
.registration-container {
.registration-container {
background-color: #0f0f0f;
border-radius: 10px;
padding: 30px;
@ -15,63 +12,90 @@
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
text-align: center;
left: calc(50%-200);
}
}
.registration-container h1 {
.registration-container h1 {
font-size: 2em;
color: #f05a28;
margin-bottom: 20px;
}
}
.registration-container input {
.registration-container input {
width: 100%;
padding: 10px;
padding: 10px 12px;
margin: 10px 0;
border: 1px solid #333;
border-radius: 5px;
background-color: #1a1a1a;
border-radius: 4px;
background-color: #0f0f0f;
color: #e6e6e6;
font-size: 1em;
}
font-family: 'Courier New', monospace;
font-size: 14px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.registration-container button {
.registration-container input:focus {
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
.registration-container input::placeholder {
color: #555;
}
.registration-container button {
width: 100%;
padding: 10px;
background-color: #f05a28;
border: none;
border-radius: 5px;
color: white;
font-size: 1em;
font-weight: bold;
padding: 10px 20px;
background-color: #1a1a1a;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
}
transition: all 0.2s ease;
}
.registration-container button:hover {
.registration-container button:hover {
background-color: #2a2a2a;
border-color: #444;
color: #fff;
}
.registration-container button[type="submit"],
.registration-container button.primary {
background-color: #f05a28;
border-color: #f05a28;
color: #fff;
}
.registration-container button[type="submit"]:hover,
.registration-container button.primary:hover {
background-color: #e04924;
}
border-color: #e04924;
}
.registration-container a {
.registration-container a {
color: #f05a28;
text-decoration: none;
display: block;
margin-top: 15px;
font-size: 0.9em;
transition: color 0.3s;
}
}
.registration-container a:hover {
.registration-container a:hover {
color: #e04924;
}
}
.error {
color: #d8000c;
font-size: 0.9em;
margin-top: 5px;
}
@media (max-width: 500px) {
.error {
border-color: #8b0000;
box-shadow: 0 0 0 2px rgba(139, 0, 0, 0.2);
}
@media (max-width: 500px) {
.registration-container {
width: 90%;
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -146,7 +146,10 @@ class Validator:
return True
def __repr__(self):
return str(self.to_json())
return str(self.value)
def __str__(self):
return str(self.value)
@property
async def is_valid(self):

View File

@ -30,6 +30,9 @@
<link rel="stylesheet" href="/user-list.css">
<link rel="stylesheet" href="/fa640.min.css">
<link rel="stylesheet" href="/base.css">
<link rel="stylesheet" href="/buttons.css">
<link rel="stylesheet" href="/inputs.css">
<link rel="stylesheet" href="/lists.css">
<link rel="icon" type="image/png" href="/image/snek_logo_32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/image/snek_logo_64x64.png" sizes="64x64">
<script nonce="{{nonce}}" defer src="https://umami.molodetz.nl/script.js" data-website-id="d127c3e4-dc70-4041-a1c8-bcc32c2492ea" defer></script>

View File

@ -11,6 +11,8 @@
<meta name="color-scheme" content="dark">
<link rel="stylesheet" href="/sandbox.css" />
<link rel="stylesheet" href="/buttons.css" />
<link rel="stylesheet" href="/inputs.css" />
<title>{% block title %}Snek chat by Molodetz{% endblock %}</title>
<script src="/polyfills/Promise.withResolvers.js" type="module"></script>

View File

@ -63,16 +63,34 @@
}
.btn {
display: inline-block;
padding: .75rem 1.5rem;
padding: 10px 20px;
margin: .5rem;
background: #0fa;
color: #111;
font-weight: bold;
background: #1a1a1a;
border: 1px solid #333;
color: #e6e6e6;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
border-radius: 4px;
transition: background .2s;
transition: all .2s ease;
cursor: pointer;
text-decoration: none;
}
.btn:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
text-decoration: none;
}
.btn-primary {
background: #f05a28;
border-color: #f05a28;
color: #fff;
}
.btn-primary:hover {
background: #e04924;
border-color: #e04924;
}
.btn:hover { background: #7ef; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
@ -149,7 +167,7 @@
<h1>Snek</h1>
<p>Professional Platform for Developers, Testers &amp; AI Professionals</p>
<a href="/login.html" class="btn">Login</a>
<a href="/register.html" class="btn">Register</a>
<a href="/register.html" class="btn btn-primary">Register</a>
<a href="/about.html" class="about-link">About</a>
</header>
<main class="container">
@ -212,7 +230,7 @@
<section id="signup">
<h2>Start Now</h2>
<p>No email. No activity logs. Register a username and access the platform immediately.</p>
<a href="/register.html" class="btn">Sign Up</a>
<a href="/register.html" class="btn btn-primary">Sign Up</a>
<br>
<a href="/about.html" class="about-link">Learn more about Snek</a>
</section>

View File

@ -1,5 +1,5 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
<style>
form {
padding: 2rem;
border-radius: 10px;
@ -8,21 +8,81 @@
padding-bottom: 15px
}
}
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
button {
background: #0d6efd; color: #fff;
border: none; border-radius: 5px; padding: 0.6rem 1rem;
label {
font-family: 'Courier New', monospace;
font-weight: bold;
display: flex;
align-items: center;
gap: 0.5rem;
color: #aaa;
text-transform: uppercase;
font-size: 13px;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
input[type="text"], input[type="password"], textarea {
width: 100%;
padding: 10px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
background: #0f0f0f;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input[type="text"]:focus, input[type="password"]:focus, textarea:focus {
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #f05a28;
cursor: pointer;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
button, a.cancel {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
text-decoration: none;
border: 1px solid #333;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
background: #1a1a1a;
color: #e6e6e6;
}
button:hover, a.cancel:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
}
button[type="submit"] {
background: #f05a28;
border-color: #f05a28;
color: #fff;
}
button[type="submit"]:hover {
background: #e04924;
border-color: #e04924;
}
.cancel {
background: #6c757d;
background: #2a2a2a;
border-color: #444;
}
.cancel:hover {
background: #3a3a3a;
border-color: #555;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
form { padding: 1rem; }
}
</style>
</style>

View File

@ -49,16 +49,71 @@
margin-bottom: 1rem;
}
button, a.button {
background: #198754; color: #fff; border: none; border-radius: 5px;
padding: 0.4rem 0.8rem; text-decoration: none; cursor: pointer;
transition: background 0.2s;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
text-decoration: none;
border: 1px solid #333;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
background: #1a1a1a;
color: #e6e6e6;
}
button:hover, a.button:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
}
.button.delete {
background: #1a1a1a;
border-color: #8b0000;
color: #ff6b6b;
}
.button.delete:hover {
background: #2a1515;
border-color: #b00;
}
.button.edit {
background: #2a2a2a;
border-color: #444;
}
.button.edit:hover {
background: #3a3a3a;
border-color: #555;
}
.button.clone {
background: #2a2a2a;
border-color: #444;
}
.button.clone:hover {
background: #3a3a3a;
border-color: #555;
}
.button.browse {
background: #2a2a2a;
border-color: #444;
color: #e6e6e6;
}
.button.browse:hover {
background: #3a3a3a;
border-color: #555;
}
.button.create {
background: #f05a28;
border-color: #f05a28;
color: #fff;
margin-left: 0;
}
.button.create:hover {
background: #e04924;
border-color: #e04924;
}
.button.delete { background: #dc3545; }
.button.edit { background: #0d6efd; }
.button.clone { background: #6c757d; }
.button.browse { background: #ffc107; color: #212529; }
.button.create { background: #20c997; margin-left: 0.5rem; }
</style>
</head>
<body>

View File

@ -8,9 +8,7 @@
{% block head %}
<link href="https://cdn.bootcdn.net/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/monaco-editor/0.20.0/min/vs/loader.min.js"></script>
{% endblock %}
{% block logo %}

View File

@ -3,35 +3,55 @@
{% block header_text %}<h2 style="color:#fff">Profile</h2>{% endblock %}
{% block main %}
<section>
<section style="padding: 20px;">
<form method="post">
<h2>Nickname</h2>
<input type="text" name="nick" placeholder="Your nickname" value="{{ user.nick.value }}" />
<h2>Description</h2>
<textarea name="profile" id="profile">{{profile}}</textarea>
<input type="submit" name="action" value="Save" />
</form>
<div class="form-group">
<label class="form-label">Nickname</label>
<input type="text" name="nick" placeholder="Your nickname" value="{{ user.nick.value }}" class="input" />
</div>
<div class="form-group">
<label class="form-label">Description</label>
<textarea name="profile" id="profile" class="textarea">{{profile}}</textarea>
</div>
<button type="submit" name="action" value="Save" class="btn btn-primary">Save</button>
</form>
</section>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css">
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
<script>
const easyMDE = new EasyMDE({element:document.getElementById("profile")});
</script>
document.querySelector('form').addEventListener('submit', function(e) {
easyMDE.codemirror.save();
});
</script>
<style>
.EasyMDEContainer {
filter: invert(1) !important;
}
</style>
background: #0f0f0f;
color: #e6e6e6;
}
.EasyMDEContainer .CodeMirror {
background: #0f0f0f;
color: #e6e6e6;
border: 1px solid #333;
}
.EasyMDEContainer .editor-toolbar {
background: #1a1a1a;
border: 1px solid #333;
border-bottom: none;
}
.EasyMDEContainer .editor-toolbar button {
color: #e6e6e6 !important;
}
.EasyMDEContainer .editor-toolbar button:hover {
background: #2a2a2a;
}
.CodeMirror-cursor {
border-left-color: #e6e6e6 !important;
}
.editor-statusbar {
color: #888 !important;
}
</style>
{% endblock %}

View File

@ -3,23 +3,23 @@
{% block header_text %}<h2 style="color:#fff">Create Profile Page</h2>{% endblock %}
{% block main %}
<section style="padding: 10px; height: 100%; overflow: auto; display: flex; flex-direction: column;">
<div style="margin-bottom: 10px;">
<a href="/settings/profile_pages/index.html" style="color: #f05a28; text-decoration: none;">
<section style="padding: 20px; height: 100%; overflow: auto; display: flex; flex-direction: column;">
<div style="margin-bottom: 16px;">
<a href="/settings/profile_pages/index.html" class="btn btn-sm">
Back to Pages
</a>
</div>
{% if error %}
<div style="background: #c42; color: white; padding: 8px; margin-bottom: 10px;">
<div style="background: #2a1515; border: 1px solid #8b0000; color: #ff6b6b; padding: 10px; margin-bottom: 16px; border-radius: 4px;">
{{ error }}
</div>
{% endif %}
<form method="post" style="flex: 1; display: flex; flex-direction: column;">
<div style="margin-bottom: 10px;">
<label for="title" style="display: block; margin-bottom: 5px; font-weight: bold;">
Page Title <span style="color: #c42;">*</span>
<div class="form-group">
<label for="title" class="form-label">
Page Title <span style="color: #ff6b6b;">*</span>
</label>
<input
type="text"
@ -28,35 +28,35 @@
value="{{ title or '' }}"
required
placeholder="e.g., About Me, Projects, Blog"
style="width: 100%; padding: 8px; background: #2d2d2d; border: 1px solid #444; color: #f0f0f0;"
class="input"
/>
</div>
<div style="margin-bottom: 10px;">
<label for="is_published" style="display: flex; align-items: center; cursor: pointer;">
<div class="form-group">
<label for="is_published" class="checkbox-wrapper">
<input
type="checkbox"
id="is_published"
name="is_published"
checked
style="margin-right: 8px;"
class="checkbox"
/>
<span>Publish immediately</span>
<span style="color: #e6e6e6;">Publish immediately</span>
</label>
</div>
<div style="flex: 1; display: flex; flex-direction: column; min-height: 0;">
<label for="content" style="display: block; margin-bottom: 5px; font-weight: bold;">
<div style="flex: 1; display: flex; flex-direction: column; min-height: 0;" class="form-group">
<label for="content" class="form-label">
Content (Markdown)
</label>
<textarea id="content" name="content" style="flex: 1;">{{ content or '' }}</textarea>
<textarea id="content" name="content" class="textarea" style="flex: 1;">{{ content or '' }}</textarea>
</div>
<div style="margin-top: 10px; display: flex; gap: 10px;">
<button type="submit" style="padding: 8px 16px; background: #f05a28; color: white; border: none; cursor: pointer;">
<div style="margin-top: 16px; display: flex; gap: 10px;">
<button type="submit" class="btn btn-primary">
Create Page
</button>
<a href="/settings/profile_pages/index.html" style="padding: 8px 16px; background: #444; color: white; text-decoration: none; display: inline-block;">
<a href="/settings/profile_pages/index.html" class="btn btn-secondary">
Cancel
</a>
</div>
@ -84,37 +84,40 @@
status: ["lines", "words"],
placeholder: "Write your content here using Markdown...",
});
document.querySelector('form').addEventListener('submit', function(e) {
easyMDE.codemirror.save();
});
</script>
<style>
.EasyMDEContainer {
background: #2d2d2d;
color: #f0f0f0;
background: #0f0f0f;
color: #e6e6e6;
height: 100%;
}
.EasyMDEContainer .CodeMirror {
background: #2d2d2d;
color: #f0f0f0;
border: 1px solid #444;
background: #0f0f0f;
color: #e6e6e6;
border: 1px solid #333;
}
.EasyMDEContainer .editor-toolbar {
background: #252525;
border: 1px solid #444;
background: #1a1a1a;
border: 1px solid #333;
border-bottom: none;
}
.EasyMDEContainer .editor-toolbar button {
color: #f0f0f0 !important;
color: #e6e6e6 !important;
}
.EasyMDEContainer .editor-toolbar button:hover {
background: #3d3d3d;
background: #2a2a2a;
}
.CodeMirror-cursor {
border-left-color: #f0f0f0 !important;
border-left-color: #e6e6e6 !important;
}
.editor-statusbar {

View File

@ -3,23 +3,23 @@
{% block header_text %}<h2 style="color:#fff">Edit Profile Page</h2>{% endblock %}
{% block main %}
<section style="padding: 10px; height: 100%; overflow: auto; display: flex; flex-direction: column;">
<div style="margin-bottom: 10px;">
<a href="/settings/profile_pages/index.html" style="color: #f05a28; text-decoration: none;">
<section style="padding: 20px; height: 100%; overflow: auto; display: flex; flex-direction: column;">
<div style="margin-bottom: 16px;">
<a href="/settings/profile_pages/index.html" class="btn btn-sm">
Back to Pages
</a>
</div>
{% if error %}
<div style="background: #c42; color: white; padding: 8px; margin-bottom: 10px;">
<div style="background: #2a1515; border: 1px solid #8b0000; color: #ff6b6b; padding: 10px; margin-bottom: 16px; border-radius: 4px;">
{{ error }}
</div>
{% endif %}
<form method="post" style="flex: 1; display: flex; flex-direction: column;">
<div style="margin-bottom: 10px;">
<label for="title" style="display: block; margin-bottom: 5px; font-weight: bold;">
Page Title <span style="color: #c42;">*</span>
<div class="form-group">
<label for="title" class="form-label">
Page Title <span style="color: #ff6b6b;">*</span>
</label>
<input
type="text"
@ -28,41 +28,41 @@
value="{{ page.title }}"
required
placeholder="e.g., About Me, Projects, Blog"
style="width: 100%; padding: 8px; background: #2d2d2d; border: 1px solid #444; color: #f0f0f0;"
class="input"
/>
<p style="color: #888; font-size: 0.85em; margin-top: 3px;">
<p style="color: #888; font-size: 0.85em; margin-top: 6px;">
Slug: {{ page.slug }}
</p>
</div>
<div style="margin-bottom: 10px;">
<label for="is_published" style="display: flex; align-items: center; cursor: pointer;">
<div class="form-group">
<label for="is_published" class="checkbox-wrapper">
<input
type="checkbox"
id="is_published"
name="is_published"
{% if page.is_published %}checked{% endif %}
style="margin-right: 8px;"
class="checkbox"
/>
<span>Publish this page</span>
<span style="color: #e6e6e6;">Publish this page</span>
</label>
</div>
<div style="flex: 1; display: flex; flex-direction: column; min-height: 0;">
<label for="content" style="display: block; margin-bottom: 5px; font-weight: bold;">
<div style="flex: 1; display: flex; flex-direction: column; min-height: 0;" class="form-group">
<label for="content" class="form-label">
Content (Markdown)
</label>
<textarea id="content" name="content" style="flex: 1;">{{ page.content }}</textarea>
<textarea id="content" name="content" class="textarea" style="flex: 1;">{{ page.content }}</textarea>
</div>
<div style="margin-top: 10px; display: flex; gap: 10px;">
<button type="submit" style="padding: 8px 16px; background: #f05a28; color: white; border: none; cursor: pointer;">
<div style="margin-top: 16px; display: flex; gap: 10px;">
<button type="submit" class="btn btn-primary">
Save Changes
</button>
<a href="/user/{{ page.user_uid }}/{{ page.slug }}.html" style="padding: 8px 16px; background: #444; color: white; text-decoration: none; display: inline-block;">
<a href="/user/{{ page.user_uid }}/{{ page.slug }}.html" class="btn btn-secondary">
Preview
</a>
<a href="/settings/profile_pages/index.html" style="padding: 8px 16px; background: #444; color: white; text-decoration: none; display: inline-block;">
<a href="/settings/profile_pages/index.html" class="btn">
Cancel
</a>
</div>
@ -90,37 +90,40 @@
status: ["lines", "words"],
placeholder: "Write your content here using Markdown...",
});
document.querySelector('form').addEventListener('submit', function(e) {
easyMDE.codemirror.save();
});
</script>
<style>
.EasyMDEContainer {
background: #2d2d2d;
color: #f0f0f0;
background: #0f0f0f;
color: #e6e6e6;
height: 100%;
}
.EasyMDEContainer .CodeMirror {
background: #2d2d2d;
color: #f0f0f0;
border: 1px solid #444;
background: #0f0f0f;
color: #e6e6e6;
border: 1px solid #333;
}
.EasyMDEContainer .editor-toolbar {
background: #252525;
border: 1px solid #444;
background: #1a1a1a;
border: 1px solid #333;
border-bottom: none;
}
.EasyMDEContainer .editor-toolbar button {
color: #f0f0f0 !important;
color: #e6e6e6 !important;
}
.EasyMDEContainer .editor-toolbar button:hover {
background: #3d3d3d;
background: #2a2a2a;
}
.CodeMirror-cursor {
border-left-color: #f0f0f0 !important;
border-left-color: #e6e6e6 !important;
}
.editor-statusbar {

View File

@ -4,90 +4,52 @@
{% block main %}
<section style="padding: 20px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h2 style="margin: 0;">Your Profile Pages</h2>
<a href="/settings/profile_pages/create.html" class="button" style="text-decoration: none;">
<div class="settings-topbar">
<h2>Your Profile Pages</h2>
<a href="/settings/profile_pages/create.html" class="btn btn-primary">
<i class="fa-solid fa-plus"></i> New Page
</a>
</div>
{% if pages %}
<div id="pages-list">
<div class="settings-list">
{% for page in pages %}
<div class="page-item" data-uid="{{ page.uid }}" style="border: 1px solid #444; border-radius: 8px; padding: 15px; margin-bottom: 15px; background: #2d2d2d;">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div style="flex: 1;">
<h3 style="margin: 0 0 5px 0;">
<div class="settings-list-item">
<div class="settings-list-info">
<div class="settings-list-title">
<i class="fa-solid fa-file-lines"></i>
{{ page.title }}
{% if not page.is_published %}
<span style="opacity: 0.6; font-size: 0.85em; font-weight: normal;">(Draft)</span>
<span class="settings-list-badge">(Draft)</span>
{% endif %}
</h3>
<p style="margin: 5px 0; color: #888; font-size: 0.9em;">
Slug: <code style="background: #1d1d1d; padding: 2px 6px; border-radius: 3px;">{{ page.slug }}</code>
</p>
<p style="margin: 5px 0; color: #888; font-size: 0.9em;">
Order: {{ page.order_index }}
</p>
</div>
<div style="display: flex; gap: 10px;">
<a href="/user/{{ user.uid.value }}/{{ page.slug }}.html" class="button" style="text-decoration: none;">
<div class="settings-list-meta">
<span>Slug: <code>{{ page.slug }}</code></span>
</div>
</div>
<div class="settings-list-actions">
<a href="/user/{{ user.uid.value }}/{{ page.slug }}.html" class="btn btn-sm">
<i class="fa-solid fa-eye"></i> View
</a>
<a href="/settings/profile_pages/{{ page.uid }}/edit.html" class="button" style="text-decoration: none;">
<a href="/settings/profile_pages/{{ page.uid }}/edit.html" class="btn btn-sm btn-secondary">
<i class="fa-solid fa-edit"></i> Edit
</a>
<form method="post" action="/settings/profile_pages/{{ page.uid }}/delete.html" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this page?');">
<button type="submit" class="button" style="background: #c42;">
<button type="submit" class="btn btn-sm btn-danger">
<i class="fa-solid fa-trash"></i> Delete
</button>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
<div style="margin-top: 30px; padding: 15px; background: #2d2d2d; border-radius: 8px; border: 1px solid #444;">
<h3>Page Order</h3>
<p style="color: #888;">Drag and drop pages to reorder them (coming soon), or use the order index field when editing.</p>
</div>
{% else %}
<div style="text-align: center; padding: 40px; background: #2d2d2d; border-radius: 8px; border: 1px solid #444;">
<p style="color: #888; font-size: 1.1em; margin-bottom: 20px;">You haven't created any profile pages yet.</p>
<a href="/settings/profile_pages/create.html" class="button" style="text-decoration: none;">
<div class="settings-empty">
<p>You haven't created any profile pages yet.</p>
<a href="/settings/profile_pages/create.html" class="btn btn-primary">
<i class="fa-solid fa-plus"></i> Create Your First Page
</a>
</div>
{% endif %}
</section>
<style>
.button {
display: inline-block;
padding: 8px 16px;
background: #0d6efd;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
}
.button:hover {
background: #0b5ed7;
}
.button i {
margin-right: 5px;
}
.page-item {
transition: background 0.2s;
}
.page-item:hover {
background: #353535 !important;
}
</style>
{% endblock %}

View File

@ -1,5 +1,5 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
<style>
form {
padding: 2rem;
border-radius: 10px;
@ -8,21 +8,81 @@
padding-bottom: 15px
}
}
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
button {
background: #0d6efd; color: #fff;
border: none; border-radius: 5px; padding: 0.6rem 1rem;
label {
font-family: 'Courier New', monospace;
font-weight: bold;
display: flex;
align-items: center;
gap: 0.5rem;
color: #aaa;
text-transform: uppercase;
font-size: 13px;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
input[type="text"], input[type="password"], textarea {
width: 100%;
padding: 10px 12px;
font-family: 'Courier New', monospace;
font-size: 14px;
background: #0f0f0f;
border: 1px solid #333;
border-radius: 4px;
color: #e6e6e6;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input[type="text"]:focus, input[type="password"]:focus, textarea:focus {
outline: none;
border-color: #f05a28;
box-shadow: 0 0 0 2px rgba(240, 90, 40, 0.2);
}
input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #f05a28;
cursor: pointer;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
button, a.cancel {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 500;
text-decoration: none;
border: 1px solid #333;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
background: #1a1a1a;
color: #e6e6e6;
}
button:hover, a.cancel:hover {
background: #2a2a2a;
border-color: #444;
color: #fff;
}
button[type="submit"] {
background: #f05a28;
border-color: #f05a28;
color: #fff;
}
button[type="submit"]:hover {
background: #e04924;
border-color: #e04924;
}
.cancel {
background: #6c757d;
background: #2a2a2a;
border-color: #444;
}
.cancel:hover {
background: #3a3a3a;
border-color: #555;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
form { padding: 1rem; }
}
</style>
</style>

View File

@ -1,111 +1,59 @@
{% extends 'settings/index.html' %}
{% block header_text %}<h1><i class="fa-solid fa-database"></i> Repositories</h1>{% endblock %}
{% block header_text %}<h2 style="color:#fff">Repositories</h2>{% endblock %}
{% block main %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Repositories - List</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
.actions {
display: flex;
gap: 0.5rem;
justify-content: center;
flex-wrap: wrap;
}
.repo-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
.repo-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
border-radius: 8px;
flex-wrap: wrap;
}
.repo-info {
display: flex;
align-items: center;
gap: 1rem;
flex: 1;
min-width: 220px;
}
.repo-name {
font-size: 1.1rem;
font-weight: 600;
}
@media (max-width: 600px) {
.repo-row { flex-direction: column; align-items: stretch; }
.actions { justify-content: flex-start; }
}
.topbar {
display: flex;
/* justify-content: flex-end;*/
margin-bottom: 1rem;
}
button, a.button {
background: #198754; color: #fff; border: none; border-radius: 5px;
padding: 0.4rem 0.8rem; text-decoration: none; cursor: pointer;
transition: background 0.2s;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
.button.delete { background: #dc3545; }
.button.edit { background: #0d6efd; }
.button.clone { background: #6c757d; }
.button.browse { background: #ffc107; color: #212529; }
.button.create { background: #20c997; margin-left: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="topbar">
<a class="button create" href="/settings/repositories/create.html">
<section style="padding: 20px;">
<div class="settings-topbar">
<h2>Your Repositories</h2>
<a href="/settings/repositories/create.html" class="btn btn-primary">
<i class="fa-solid fa-plus"></i> New Repository
</a>
</div>
<section class="repo-list">
<!-- Example repository entries; replace with your templating/iteration -->
{% for repo in repositories %}
<div class="repo-row">
<div class="repo-info">
<div>
<span class="repo-name"><i class="fa-solid fa-book"></i> {{ repo.name }}</span>
{% if repo.description %}
<div style="color: #888; font-size: 0.9rem; margin-top: 0.25rem;">{{ repo.description }}</div>
{% endif %}
</div>
<span title="Public">
{% if repositories %}
<div class="settings-list">
{% for repo in repositories %}
<div class="settings-list-item">
<div class="settings-list-info">
<div class="settings-list-title">
<i class="fa-solid fa-book"></i>
{{ repo.name }}
</div>
{% if repo.description %}
<div class="settings-list-meta">{{ repo.description }}</div>
{% endif %}
<div class="settings-list-meta">
<span class="settings-list-badge">
<i class="fa-solid {% if repo.is_private %}fa-lock{% else %}fa-lock-open{% endif %}"></i>
{% if repo.is_private %}Private{% else %}Public{% endif %}
</span>
</span>
</div>
<div class="actions">
<a class="button browse" href="/repository/{{ user.username.value }}/{{ repo.name }}" target="_blank">
</div>
<div class="settings-list-actions">
<a class="btn btn-sm" href="/repository/{{ user.username.value }}/{{ repo.name }}" target="_blank">
<i class="fa-solid fa-folder-open"></i> Browse
</a>
<button class="button clone" onclick="navigator.clipboard.writeText(window.location.protocol + '//' + window.location.host + '/git/{{ user.username.value }}/{{ repo.name }}.git'); alert('Clone URL copied to clipboard!')">
<button class="btn btn-sm" onclick="navigator.clipboard.writeText(window.location.protocol + '//' + window.location.host + '/git/{{ user.username.value }}/{{ repo.name }}.git'); alert('Clone URL copied to clipboard!')">
<i class="fa-solid fa-code-branch"></i> Clone URL
</button>
<a class="button edit" href="/settings/repositories/repository/{{ repo.name }}/update.html">
<a class="btn btn-sm btn-secondary" href="/settings/repositories/repository/{{ repo.name }}/update.html">
<i class="fa-solid fa-pen"></i> Edit
</a>
<a class="button delete" href="/settings/repositories/repository/{{ repo.name }}/delete.html">
<a class="btn btn-sm btn-danger" href="/settings/repositories/repository/{{ repo.name }}/delete.html">
<i class="fa-solid fa-trash"></i> Delete
</a>
</div>
</div>
{% endfor %}
<!-- ... -->
</section>
</div>
</body>
</html>
{% else %}
<div class="settings-empty">
<p>You haven't created any repositories yet.</p>
<a href="/settings/repositories/create.html" class="btn btn-primary">
<i class="fa-solid fa-plus"></i> Create Your First Repository
</a>
</div>
{% endif %}
</section>
{% endblock %}

View File

@ -1,10 +1,8 @@
<aside class="sidebar" id="channelSidebar">
<h2>You</h2>
<aside class="sidebar settings-sidebar" id="channelSidebar">
<h2>Settings</h2>
<ul>
<li><a class="no-select" href="/settings/profile.html">Profile</a></li>
<li><a class="no-select" href="/settings/profile_pages/index.html">Profile Pages</a></li>
<li><a class="no-select" href="/settings/repositories/index.html">Repositories</a></li>
</ul>
</aside>
</aside>