This commit is contained in:
2026-08-19 21:14:42 +02:00
parent 619a6f9557
commit 63ea313742
3 changed files with 345 additions and 162 deletions

View File

@@ -77,176 +77,118 @@
<!-- ═══════════════════════════════ PROFILE ═══════════════════════════════ -->
<h2 id="profile"><i class="fa-solid fa-user-pen"></i> {{ t('settings.profile') }}</h2>
<!-- Identity & Display Settings -->
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-signature"></i> {{ t('settings.display_name') }}</legend>
<div class="setting-item">
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
<input type="text" id="display_name_input" class="input"
placeholder="{{ t('settings.display_name_placeholder') }}" value="{!! session.display_name || '' !!}"
maxlength="32" style="max-width: 240px; height: 32px; font-size: 0.9em;">
<button type="button" id="btn-update-display-name" class="button"
style="padding: 5px 14px; font-size: 0.85em;">{{ t('settings.save') }}</button>
</div>
<div id="display-name-status" class="avatar-status"></div>
</div>
</fieldset>
<p class="text-muted" style="margin-bottom: 16px; font-size: 0.9em; display: flex; align-items: center; gap: 8px;">
<i class="fa-solid fa-wand-magic-sparkles" style="color: var(--accent);"></i> Click directly on your profile card below to edit your avatar, banner, name, color, or bio.
</p>
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-palette"></i> {{ t('settings.username_color') }}</legend>
<div class="setting-item">
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
<input type="color" id="username_color_picker" value="{{ session.username_color || '#ffffff' }}"
style="width: 50px; height: 32px; padding: 0; border: 1px solid var(--nav-border-color); cursor: pointer; background: none; border-radius: 4px;">
<input type="text" id="username_color_hex" value="{{ session.username_color || '#ffffff' }}" maxlength="7"
placeholder="#ffffff" class="input"
style="width: 95px; font-family: monospace; font-size: 0.9em; padding: 4px 8px; height: 32px;">
<button type="button" id="btn-save-username-color" class="button"
style="padding: 5px 14px; font-size: 0.85em;">{{ t('settings.save_color') }}</button>
<button type="button" id="btn-reset-username-color" class="button button-secondary"
style="padding: 5px 14px; font-size: 0.85em;">{{ t('settings.reset') }}</button>
</div>
<small class="text-muted" style="display: block; margin-top: 6px;">{{ t('settings.username_color_hint') }}</small>
</div>
</fieldset>
@if(enable_profile_description)
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-align-left"></i> {{ t('settings.custom_description') }}</legend>
<div class="setting-item">
<textarea id="profile_description" class="input" placeholder="{{ t('settings.description_placeholder') }}"
maxlength="2000" style="min-height: 80px;">{!! session.description || '' !!}</textarea>
<div class="profile-settings-actions" style="margin-top: 10px;">
<button type="button" id="btn-save-description" class="button">{{ t('settings.save_description') }}</button>
<button type="button" id="btn-clear-description" class="button button-danger">{{ t('settings.clear') }}</button>
</div>
<div id="description-status" class="avatar-status"></div>
</div>
</fieldset>
@endif
<!-- Avatar & Media Uploads -->
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-user-gear"></i> {{ t('settings.avatar') }}</legend>
<div class="avatar-settings-wrapper">
<div class="avatar-preview-container">
<div class="avatar-preview-label">{{ t('settings.current_avatar') }}</div>
@if(avatar_file)
<a href="/user/{!! session.user !!}" target="_blank">
<img id="avatar-preview" class="avatar-preview-img" src="/a/{{ avatar_file }}">
</a>
@elseif(session.avatar && session.avatar > 0)
<a href="/user/{!! session.user !!}" target="_blank">
<img id="avatar-preview" class="avatar-preview-img" src="/t/{{ session.avatar }}.webp">
</a>
@else
<div id="avatar-preview" class="avatar-preview-img avatar-placeholder">?</div>
@endif
</div>
<div class="avatar-upload-section">
<p class="avatar-hint">{{ t('settings.avatar_hint') }}</p>
<div class="avatar-upload-wrapper">
<input type="file" id="avatar-file-input" accept="image/gif,image/jpeg,image/png,image/webp" hidden>
<button type="button" id="avatar-choose-btn" class="button">{{ t('settings.choose_file') }}</button>
<span id="avatar-filename" class="avatar-filename">{{ t('settings.no_file_selected') }}</span>
</div>
<div class="avatar-progress-wrapper" id="avatar-progress-wrapper" style="display: none;">
<div class="avatar-progress-bar">
<div class="avatar-progress-fill" id="avatar-progress-fill"></div>
</div>
<span class="avatar-progress-text" id="avatar-progress-text">0%</span>
</div>
<div class="avatar-upload-actions">
<button type="button" id="avatar-upload-btn" class="button" disabled>{{ t('settings.upload_btn') }}</button>
<div class="direct-edit-profile-card-wrapper" style="position: relative; max-width: 750px; margin-bottom: 30px;">
<!-- Interactive User Card -->
<div class="user-infobox-block editable-profile-card" id="live-profile-preview-box"
style="--author-accent: {{ session.username_color || 'var(--accent)' }}; --author-border: {{ session.username_color || 'var(--accent)' }}; @if(banner_file && user_banner_enabled) --author-banner: url('/a/{{ banner_file }}?t={{ Date.now() }}'); --author-banner-position: {{ banner_position === 'center' ? 'center top' : (banner_position || 'center top') }}; --author-banner-size: {{ banner_size || 'cover' }}; @endif">
<!-- Avatar with Hover Overlay -->
<div class="user-infobox-avatar editable-avatar-container" id="avatar-choose-btn" title="Click to change avatar">
<a href="#" onclick="event.preventDefault(); document.getElementById('avatar-file-input').click();" style="display:block; position:relative; width:100%; height:100%;">
@if(avatar_file)
<button type="button" id="avatar-remove-btn" class="button button-danger">{{ t('settings.remove_custom') }}</button>
<img id="live-preview-avatar" class="avatar-preview-img" src="/a/{{ avatar_file }}" />
@elseif(session.avatar && session.avatar > 0)
<img id="live-preview-avatar" class="avatar-preview-img" src="/t/{{ session.avatar }}.webp" />
@else
<img id="live-preview-avatar" class="avatar-preview-img" src="/a/default.png" style="border-color: #444;" />
@endif
</div>
<div id="avatar-upload-status" class="avatar-status"></div>
</div>
</div>
</fieldset>
@if(user_banner_enabled)
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 20px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-image"></i> Profile Banner</legend>
<div class="avatar-settings-wrapper">
<div class="avatar-preview-container" style="width: 360px; max-width: 100%;">
<div class="avatar-preview-label">{{ t('settings.current_banner') }}</div>
@if(banner_file)
<a href="/user/{!! session.user !!}" target="_blank" style="display:block; width: 100%;">
<div id="banner-preview" class="banner-preview-img" style="width:100%;aspect-ratio:400/140;border-radius:4px;border:1px solid var(--nav-border-color);background-image:url('/a/{{ banner_file }}');background-size:{{ banner_size || 'cover' }};background-position:{{ banner_position === 'center' ? 'center top' : (banner_position || 'center top') }};background-repeat:no-repeat;"></div>
<div class="avatar-hover-overlay">
<i class="fa-solid fa-camera"></i>
</div>
</a>
@else
<div id="banner-preview" class="banner-preview-img banner-placeholder" style="width:100%;aspect-ratio:400/140;display:flex;align-items:center;justify-content:center;border:1px dashed var(--nav-border-color);border-radius:4px;color:var(--text-muted);font-size:0.85em;">{{ t('settings.no_banner_set') }}</div>
@if(avatar_file)
<button type="button" class="avatar-quick-remove" id="avatar-remove-btn" title="Remove custom avatar">
<i class="fa-solid fa-xmark"></i>
</button>
@endif
</div>
<div class="avatar-upload-section">
<p class="avatar-hint">{{ t('settings.banner_hint') }}</p>
<div class="avatar-upload-wrapper">
<input type="file" id="banner-file-input" accept="image/gif,image/jpeg,image/png,image/webp" hidden>
<button type="button" id="banner-choose-btn" class="button">{{ t('settings.choose_file') }}</button>
<span id="banner-filename" class="avatar-filename">{{ t('settings.no_file_selected') }}</span>
</div>
<div class="user-infobox-info">
<div class="user-infobox-header">
<div class="user-infobox-username-container" style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<!-- Editable Display Name -->
<div class="inline-edit-username-wrapper">
<span id="live-preview-username" class="user-infobox-username" contenteditable="true" spellcheck="false" title="Click to edit display name">{!! session.display_name || session.user !!}</span>
<i class="fa-solid fa-pen" style="font-size: 0.65em; opacity: 0.5; margin-left: 2px;" title="Editable"></i>
</div>
<!-- Color Picker Badge -->
<div class="inline-color-picker-wrapper" style="display: inline-flex; align-items: center;">
<label for="username_color_picker" class="color-picker-badge" id="color-picker-badge" style="background: {{ session.username_color || 'var(--accent)' }};" title="Click to change username color">
<i class="fa-solid fa-palette"></i>
</label>
<input type="color" id="username_color_picker" value="{{ session.username_color || '#ffffff' }}" style="position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;">
<input type="text" id="username_color_hex" value="{{ session.username_color || '#ffffff' }}" style="display: none;">
</div>
<!-- Hidden Backing Save Buttons for JS event trigger -->
<button type="button" id="btn-update-display-name" class="inline-save-btn" style="display: none;"><i class="fa-solid fa-check"></i> Save Name</button>
<button type="button" id="btn-save-username-color" style="display: none;">Save Color</button>
<button type="button" id="btn-reset-username-color" style="display: none;">Reset</button>
<div class="avatar-progress-wrapper" id="banner-progress-wrapper" style="display: none;">
<div class="avatar-progress-bar">
<div class="avatar-progress-fill" id="banner-progress-fill"></div>
</div>
<span class="avatar-progress-text" id="banner-progress-text">0%</span>
<span class="user-infobox-timestamp"><a href="#" class="timestamp-link"><time>just now</time></a></span>
</div>
<div class="avatar-upload-actions">
<button type="button" id="banner-upload-btn" class="button" disabled>Upload Banner</button>
@if(banner_file)
<button type="button" id="banner-edit-btn" class="button">Edit Position</button>
<button type="button" id="banner-remove-btn" class="button button-danger">Remove Banner</button>
@endif
<div class="user-infobox-body">
<!-- Editable Description -->
<div class="inline-edit-description-wrapper">
<div class="user-infobox-description" id="live-preview-description" contenteditable="true" spellcheck="false" title="Click to edit description" data-placeholder="Click to write a bio...">
{!! session.description || '' !!}
</div>
<div class="description-edit-controls" id="desc-edit-controls" style="display: flex; margin-top: 10px; gap: 8px; align-items: center; flex-wrap: wrap;">
<button type="button" id="btn-save-description" class="button button-sm" style="padding: 4px 10px; font-size: 0.8em;"><i class="fa-solid fa-floppy-disk"></i> {{ t('settings.save_description') }}</button>
<button type="button" id="btn-clear-description" class="button button-danger button-sm" style="padding: 4px 10px; font-size: 0.8em;"><i class="fa-solid fa-trash"></i> {{ t('settings.clear') }}</button>
@if(user_banner_enabled)
<button type="button" id="banner-choose-btn" class="button button-sm" style="padding: 4px 10px; font-size: 0.8em;"><i class="fa-solid fa-camera"></i> Change Banner</button>
@if(banner_file)
<button type="button" id="banner-edit-btn" class="button button-sm" style="padding: 4px 10px; font-size: 0.8em;"><i class="fa-solid fa-arrows-up-down-left-right"></i> Reposition</button>
<button type="button" id="banner-remove-btn" class="button button-danger button-sm" style="padding: 4px 10px; font-size: 0.8em;"><i class="fa-solid fa-trash"></i> Remove Banner</button>
@endif
@endif
</div>
</div>
<div class="user-infobox-actions">
<i class="iconset fa-solid fa-heart" title="Favorite"></i>
</div>
</div>
<div id="banner-upload-status" class="avatar-status"></div>
</div>
</div>
</fieldset>
@endif
<!-- Live Profile Preview -->
<fieldset style="border: 1px solid var(--nav-border-color); padding: 12px 16px; border-radius: 6px; margin-bottom: 25px; background: rgba(0,0,0,0.06);">
<legend style="width: auto; padding: 0 8px; font-size: 1.05em; font-weight: bold;"><i class="fa-solid fa-eye"></i> Live Profile Preview</legend>
<div class="setting-item" style="max-width: 750px;">
<div class="user-infobox-block" id="live-profile-preview-box" style="--author-accent: {{ session.username_color || 'var(--accent)' }}; --author-border: {{ session.username_color || 'var(--accent)' }}; @if(banner_file && user_banner_enabled) --author-banner: url('/a/{{ banner_file }}?t={{ Date.now() }}'); --author-banner-position: {{ banner_position === 'center' ? 'center top' : (banner_position || 'center top') }}; --author-banner-size: {{ banner_size || 'cover' }}; @endif">
<div class="user-infobox-avatar">
<a href="#">
@if(avatar_file)
<img id="live-preview-avatar" src="/a/{{ avatar_file }}" />
@else
<img id="live-preview-avatar" src="/a/default.png" style="border-color: #444;" />
@endif
</a>
</div>
<div class="user-infobox-info">
<div class="user-infobox-header">
<div class="user-infobox-username-container">
<a id="live-preview-username" href="#" class="user-infobox-username">{!! session.display_name || session.user !!}</a>
</div>
<span class="user-infobox-timestamp"><a href="#" class="timestamp-link"><time>just now</time></a></span>
</div>
<div class="user-infobox-body">
<div class="user-infobox-description">
{!! session.description || 'Welcome to my profile! This is a live preview.' !!}
</div>
<div class="user-infobox-actions">
<i class="iconset fa-solid fa-heart" title="Favorite"></i>
</div>
</div>
</div>
</div>
<!-- Hidden Backing Elements for JavaScript compatibility -->
<input type="file" id="avatar-file-input" accept="image/gif,image/jpeg,image/png,image/webp" style="display: none;">
<span id="avatar-filename" style="display: none;"></span>
<button type="button" id="avatar-upload-btn" style="display: none;"></button>
<div id="avatar-progress-wrapper" style="display: none;">
<div id="avatar-progress-fill"></div>
<span id="avatar-progress-text"></span>
</div>
</fieldset>
<input type="file" id="banner-file-input" accept="image/gif,image/jpeg,image/png,image/webp" style="display: none;">
<span id="banner-filename" style="display: none;"></span>
<button type="button" id="banner-upload-btn" style="display: none;"></button>
<div id="banner-progress-wrapper" style="display: none;">
<div id="banner-progress-fill"></div>
<span id="banner-progress-text"></span>
</div>
<input type="text" id="display_name_input" value="{!! session.display_name || '' !!}" style="display: none;">
@if(enable_profile_description)
<textarea id="profile_description" style="display: none;">{!! session.description || '' !!}</textarea>
@endif
</div>
<!-- ═══════════════════════════════ PREFERENCES ═══════════════════════════════ -->
<h2 id="preferences"><i class="fa-solid fa-sliders"></i> {{ t('settings.preferences') }}</h2>