Téléverser les fichiers vers "web/templates"

This commit is contained in:
RainbowYoshi 2026-01-31 21:53:58 +00:00
parent e8d43f425b
commit b318f26906
3 changed files with 488 additions and 0 deletions

275
web/templates/settings.html Normal file
View File

@ -0,0 +1,275 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings | PortaGit</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container" style="margin-top: 2rem;">
<aside class="sidebar">
<div style="margin-bottom: 2rem;">
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1.5rem;">
<span
style="width: 24px; height: 24px; background: var(--border-color); border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<img src="{{.Config.Avatar}}"
style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%;">
</span>
<span style="font-weight: 600; font-size: 0.9rem;">{{.Config.Username}}</span>
</div>
<nav class="settings-nav">
<a href="/settings?tab=profile" class="settings-nav-item {{if eq .CurrentTab "profile"}}active{{end}}">Profile</a>
<a href="/settings?tab=appearance" class="settings-nav-item {{if eq .CurrentTab "appearance"}}active{{end}}">Appearance</a>
</nav>
<div style="margin-top: 2rem; padding-left: 1rem;">
<a href="/"
style="color: var(--text-secondary); font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem;">
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
</svg>
Return to profile
</a>
</div>
</div>
</aside>
<main class="main-content">
{{if eq .CurrentTab "profile"}}
<h2
style="font-size: 1.5rem; border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; margin-bottom: 2rem;">
Profile</h2>
<form action="/settings" method="POST" enctype="multipart/form-data">
<input type="hidden" name="action" value="update_profile">
<div class="form-group">
<label>Name</label>
<input type="text" name="username" class="form-control" value="{{.Config.Username}}" required>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.4rem;">Your name may appear
around GitHub where you contribute or are mentioned. You can remove it at any time.</p>
</div>
<div class="form-group">
<label>Public email</label>
<input type="email" name="email" class="form-control" value="{{.Config.Email}}"
placeholder="email@example.com">
</div>
<div class="form-group">
<label>Bio</label>
<textarea name="bio" class="form-control" rows="4">{{.Config.Bio}}</textarea>
</div>
<div class="form-group">
<label>URL</label>
<input type="text" name="website" class="form-control" value="{{.Config.Socials.Website}}">
</div>
<div class="form-group">
<label style="display: block; margin-bottom: 0.5rem;">Profile picture</label>
<div style="display: flex; gap: 1rem; align-items: flex-start;">
<div style="width: 200px;">
<img src="{{if .Config.Avatar}}{{.Config.Avatar}}{{else}}/static/logo.png{{end}}"
id="settingsAvatar"
style="width: 100%; border-radius: 6px; border: 1px solid var(--border-color);">
</div>
<div style="flex: 1;">
<label for="avatarInput" class="btn-edit"
style="width: auto; padding: 0.5rem 1rem; display: inline-block; margin-bottom: 0.5rem;">Edit</label>
<input type="file" id="avatarInput" name="avatar" accept="image/*" style="display: none;"
onchange="previewImg(this)">
</div>
</div>
</div>
<h3
style="font-size: 1rem; margin: 2.5rem 0 1rem; border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem;">
Social accounts</h3>
<div class="form-group">
<label>GitHub Username</label>
<div style="position: relative;">
<span style="position: absolute; left: 10px; top: 10px; opacity: 0.5;">
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path
d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02.79-.22 1.65-.33 2.5-.33.85 0 1.71.11 2.5.33 1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2z" />
</svg>
</span>
<input type="text" name="github" class="form-control" value="{{.Config.Socials.Github}}"
style="padding-left: 35px;">
</div>
</div>
<div class="form-group">
<label>LinkedIn Profile</label>
<div style="position: relative;">
<span style="position: absolute; left: 10px; top: 10px; opacity: 0.5;">
<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
<path
d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14m-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93h2.79M6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37h2.77z" />
</svg>
</span>
<input type="text" name="linkedin" class="form-control" value="{{.Config.Socials.Linkedin}}"
style="padding-left: 35px;">
</div>
</div>
<div style="margin-top: 2rem;">
<button type="submit" class="btn-primary" style="width: auto; padding: 0.5rem 1.5rem;">Update
profile</button>
</div>
</form>
{{else if eq .CurrentTab "appearance"}}
<h2
style="font-size: 1.5rem; border-bottom: 1px solid var(--border-color); padding-bottom: 0.5rem; margin-bottom: 2rem;">
Appearance</h2>
<form action="/settings?tab=appearance" method="POST">
<input type="hidden" name="action" value="update_appearance">
<h3 style="font-size: 1rem; margin-bottom: 1rem;">Customize Layout Colors</h3>
<p style="color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem;">Pick any color you like
for each element of the interface.</p>
<div class="color-grid"
style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1.5rem; margin-bottom: 2rem;">
<div class="form-group">
<label>Background Color</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="bg_color" value="{{.Config.CustomColors.BgColor}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.BgColor}}" class="form-control" disabled
style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Card Background</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="card_bg" value="{{.Config.CustomColors.CardBg}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.CardBg}}" class="form-control" disabled
style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Borders</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="border_color" value="{{.Config.CustomColors.BorderColor}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.BorderColor}}" class="form-control"
disabled style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Accent Color</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="accent_color" value="{{.Config.CustomColors.AccentColor}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.AccentColor}}" class="form-control"
disabled style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Button Text Color</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="button_text" value="{{.Config.CustomColors.ButtonText}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.ButtonText}}" class="form-control" disabled
style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Primary Text</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="text_primary" value="{{.Config.CustomColors.TextPrimary}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.TextPrimary}}" class="form-control"
disabled style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Secondary Text</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="text_secondary" value="{{.Config.CustomColors.TextSecondary}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.TextSecondary}}" class="form-control"
disabled style="flex: 1;">
</div>
</div>
<div class="form-group">
<label>Muted Text</label>
<div style="display: flex; gap: 0.5rem;">
<input type="color" name="text_muted" value="{{.Config.CustomColors.TextMuted}}"
class="form-control" style="height: 40px; width: 60px; padding: 2px;">
<input type="text" value="{{.Config.CustomColors.TextMuted}}" class="form-control" disabled
style="flex: 1;">
</div>
</div>
</div>
<div style="margin-top: 2rem; display: flex; align-items: center; gap: 1rem;">
<button type="submit" class="btn-primary" style="width: auto; padding: 0.5rem 1.5rem;">Save
Aesthetics</button>
<button type="button" onclick="resetDefaults()"
style="background: transparent; border: 1px solid var(--border-color); color: var(--text-secondary); padding: 0.5rem 1.5rem; border-radius: 6px; cursor: pointer;">Reset
to Defaults</button>
</div>
</form>
<script>
function resetDefaults() {
if (!confirm('Reset all colors to default dark theme?')) return;
document.querySelector('input[name="bg_color"]').value = '#09090b';
document.querySelector('input[name="card_bg"]').value = '#18181b';
document.querySelector('input[name="border_color"]').value = '#27272a';
document.querySelector('input[name="accent_color"]').value = '#6366f1';
document.querySelector('input[name="button_text"]').value = '#ffffff';
document.querySelector('input[name="text_primary"]').value = '#f4f4f5';
document.querySelector('input[name="text_secondary"]').value = '#a1a1aa';
document.querySelector('input[name="text_muted"]').value = '#71717a';
document.querySelector('form[action*="appearance"]').submit();
}
</script>
{{end}}
</main>
</div>
<footer class="footer">
Powered by
<span
style="display: inline-block; width: 20px; height: 20px; background-color: var(--text-muted); -webkit-mask: url(/static/logo.png) no-repeat center; mask: url(/static/logo.png) no-repeat center; -webkit-mask-size: contain; mask-size: contain; vertical-align: middle; margin: 0 -4px;"></span>
<a href="https://github.com/ArthurAugis/PortaGit" target="_blank"
style="color: inherit; text-decoration: none; font-weight: 600;">PortaGit</a>
</footer>
<script>
function previewImg(input) {
if (input.files[0]) {
const reader = new FileReader();
reader.onload = e => {
document.getElementById('settingsAvatar').src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
}
}
</script>
</body>
</html>

157
web/templates/setup.html Normal file
View File

@ -0,0 +1,157 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Setup Profile | PortaGit</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="center-wrapper">
<div class="form-card setup-container">
<div class="setup-column">
<div style="text-align: center; margin-bottom: 2rem;">
<img src="/static/logo.png" alt="Logo" style="width: 48px; height: 48px; margin-bottom: 1rem;">
<h2 style="font-size: 1.2rem; margin-bottom: 0.5rem;">Sync with GitHub</h2>
<p style="color: var(--text-secondary); font-size: 0.9rem;">Instantly import your avatar, bio, and all your public repositories.</p>
</div>
<form onsubmit="startGitHubSync(event)" style="display: flex; flex-direction: column; flex: 1;">
<div class="form-group" style="flex: 1;">
<label>GitHub Username</label>
<input type="text" name="github_username" class="form-control" placeholder="e.g. monalisa" required>
</div>
<button type="submit" class="btn-primary" style="background: #238636; border-color: #238636;">Sync & Start</button>
</form>
</div>
<div class="setup-divider">
<span>OR</span>
</div>
<div class="setup-column">
<div style="text-align: center; margin-bottom: 2rem;">
<div style="width: 48px; height: 48px; margin: 0 auto 1rem; border: 1px dashed var(--border-color); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-muted);">?</div>
<h2 style="font-size: 1.2rem; margin-bottom: 0.5rem;">Create Manually</h2>
<p style="color: var(--text-secondary); font-size: 0.9rem;">Start fresh with a local profile and empty workspace.</p>
</div>
<form action="/setup" method="POST" enctype="multipart/form-data" style="display: flex; flex-direction: column; flex: 1;">
<div class="form-group" style="text-align: center; margin-bottom: 1rem;">
<label for="avatarInput" style="cursor: pointer; display: inline-block;">
<div id="previewContainer" class="avatar-preview-box">
<span style="font-size: 1.5rem; color: var(--text-muted);">+</span>
</div>
</label>
<input type="file" id="avatarInput" name="avatar" accept="image/*" style="display: none;" onchange="previewImg(this)">
<div style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.5rem;">Upload Avatar</div>
</div>
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control" placeholder="Display Name" required>
</div>
<div class="form-group">
<label>Bio</label>
<textarea name="bio" class="form-control" rows="2" placeholder="Short description..."></textarea>
</div>
<button type="submit" class="btn-primary">Create Profile</button>
</form>
</div>
</div>
</div>
<div id="sync-overlay" class="loading-overlay">
<div class="loading-content">
<img src="/static/logo.png" alt="Loading" style="width: 64px; height: 64px; margin-bottom: 2rem;" class="animate-pulse">
<h2 id="sync-status" style="margin-bottom: 1rem; color: var(--text-primary);">Connecting to GitHub...</h2>
<div class="progress-track">
<div id="sync-bar" class="progress-bar"></div>
</div>
<div class="progress-meta">
<span id="sync-step">Step 0/0</span>
<span id="sync-percent">0%</span>
</div>
</div>
</div>
<script>
function previewImg(input) {
if (input.files[0]) {
const reader = new FileReader();
reader.onload = e => {
const c = document.getElementById('previewContainer');
c.innerHTML = `<img src="${e.target.result}" style="width:100%; height:100%; object-fit:cover;">`;
c.style.borderStyle = 'solid';
};
reader.readAsDataURL(input.files[0]);
}
}
async function startGitHubSync(e) {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
document.getElementById('sync-overlay').style.display = 'flex';
try {
const response = await fetch('/setup?stream=true', {
method: 'POST',
body: formData
});
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (!line.trim()) continue;
if (line.startsWith("data: ")) {
try {
const data = JSON.parse(line.substring(6));
if (data.redirect) {
window.location.href = data.redirect;
return;
}
document.getElementById('sync-status').innerText = data.message;
const pct = Math.round((data.current / data.total) * 100);
document.getElementById('sync-bar').style.width = pct + '%';
document.getElementById('sync-step').innerText = `Step ${data.current}/${data.total}`;
document.getElementById('sync-percent').innerText = pct + '%';
} catch (err) {
console.error("Parse error", err);
}
}
}
}
} catch (err) {
alert("Sync failed: " + err);
document.getElementById('sync-overlay').style.display = 'none';
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,56 @@
{{define "sidebar"}}
<aside class="sidebar">
<div class="avatar-wrapper">
{{if .Config.Avatar}}
<img src="{{.Config.Avatar}}" alt="{{.Config.Username}}">
{{else}}
<div class="avatar-placeholder">
{{slice .Config.Username 0 1}}
</div>
{{end}}
</div>
<div class="profile-names">
<h1 class="full-name">{{.Config.Username}}</h1>
<span class="username">@{{if .Config.GithubUser}}{{.Config.GithubUser}}{{else}}{{.Config.Username}}{{end}}</span>
</div>
<div class="bio">
{{.Config.Bio}}
</div>
<form action="/settings" method="GET">
<button class="btn-edit">Edit Profile</button>
</form>
<div class="profile-details">
{{if .Config.Socials.Website}}
<div class="detail-item">
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
<a href="{{.Config.Socials.Website}}" target="_blank">Website</a>
</div>
{{end}}
{{if .Config.Socials.Github}}
<div class="detail-item">
<svg viewBox="0 0 24 24"><path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02.79-.22 1.65-.33 2.5-.33.85 0 1.71.11 2.5.33 1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2z"/></svg>
<a href="{{.Config.Socials.Github}}" target="_blank">Github</a>
</div>
{{end}}
{{if .Config.Email}}
<div class="detail-item">
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
<a href="mailto:{{.Config.Email}}">{{.Config.Email}}</a>
</div>
{{end}}
{{if .Config.Socials.Linkedin}}
<div class="detail-item">
<svg viewBox="0 0 24 24"><path d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14m-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93h2.79M6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37h2.77z"/></svg>
<a href="{{.Config.Socials.Linkedin}}" target="_blank">LinkedIn</a>
</div>
{{end}}
</div>
</aside>
{{end}}